From 5a8510c5c2c0a0e4710f30b536f1e2c1f5e3f565 Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Wed, 21 Jul 2021 11:42:38 +0300 Subject: [PATCH 001/798] spring.jpa.properties.hibernate.order_by.default_null_ordering: "none" (remove NULLS LAST from queries) --- application/src/main/resources/thingsboard.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index 0cede00913..93023a3d19 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -463,7 +463,7 @@ spring.servlet.multipart.max-file-size: "50MB" spring.servlet.multipart.max-request-size: "50MB" spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation: "true" -spring.jpa.properties.hibernate.order_by.default_null_ordering: "last" +spring.jpa.properties.hibernate.order_by.default_null_ordering: "none" # SQL DAO Configuration spring: From a01ac25a5ac1fc56298c620976fd5e5087057671 Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Fri, 30 Jul 2021 18:00:05 +0300 Subject: [PATCH 002/798] added parameter SPRING_JPA_PROPERTIES_HIBERNATE_ORDER_BY_DEFAULT_NULL_ORDERING to maintain possibility to override this settings. Those may be useful for existing Thingsboard instance that strictly tuned on previous setting for DESC NULLS LAST --- application/src/main/resources/thingsboard.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index 93023a3d19..33889f92f3 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -463,7 +463,7 @@ spring.servlet.multipart.max-file-size: "50MB" spring.servlet.multipart.max-request-size: "50MB" spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation: "true" -spring.jpa.properties.hibernate.order_by.default_null_ordering: "none" +spring.jpa.properties.hibernate.order_by.default_null_ordering: "${SPRING_JPA_PROPERTIES_HIBERNATE_ORDER_BY_DEFAULT_NULL_ORDERING:none}" # SQL DAO Configuration spring: From 55394f6367a877c118156d7d53864fb1e32d4552 Mon Sep 17 00:00:00 2001 From: vparomskiy Date: Fri, 17 Sep 2021 13:11:51 +0300 Subject: [PATCH 003/798] add REST API for fetching TB application version --- .../controller/SystemInfoController.java | 67 +++++++++++++++++++ pom.xml | 1 + 2 files changed, 68 insertions(+) create mode 100644 application/src/main/java/org/thingsboard/server/controller/SystemInfoController.java diff --git a/application/src/main/java/org/thingsboard/server/controller/SystemInfoController.java b/application/src/main/java/org/thingsboard/server/controller/SystemInfoController.java new file mode 100644 index 0000000000..1fd3819880 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/controller/SystemInfoController.java @@ -0,0 +1,67 @@ +/** + * Copyright © 2016-2021 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.controller; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.info.BuildProperties; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RestController; +import org.thingsboard.server.queue.util.TbCoreComponent; + +import javax.annotation.PostConstruct; + +@RestController +@TbCoreComponent +@RequestMapping("/api") +@Slf4j +public class SystemInfoController { + + @Autowired + private BuildProperties buildProperties; + + @PostConstruct + public void init() { + JsonNode info = buildInfoObject(); + log.info("System build info: {}", info); + } + + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") + @RequestMapping(value = "/system/info", method = RequestMethod.GET) + @ResponseBody + public JsonNode getSystemVersionInfo() { + return buildInfoObject(); + } + + private JsonNode buildInfoObject() { + ObjectMapper objectMapper = new ObjectMapper(); + ObjectNode infoObject = objectMapper.createObjectNode(); + if (buildProperties != null) { + infoObject.put("version", buildProperties.getVersion()); + infoObject.put("artifact", buildProperties.getArtifact()); + infoObject.put("name", buildProperties.getName()); + } else { + infoObject.put("version", "unknown"); + } + return infoObject; + } +} diff --git a/pom.xml b/pom.xml index fa33548a56..b638ea55ff 100755 --- a/pom.xml +++ b/pom.xml @@ -497,6 +497,7 @@ repackage + build-info From 19d08428ffd8ca07c98881295b8f0fd4d88301bc Mon Sep 17 00:00:00 2001 From: vparomskiy Date: Fri, 17 Sep 2021 13:12:50 +0300 Subject: [PATCH 004/798] add support for multi-root relation queries --- .../data/query/RelationsQueryFilter.java | 3 + .../server/dao/entity/BaseEntityService.java | 12 ++ .../query/DefaultEntityQueryRepository.java | 30 ++- .../dao/sql/query/EntityKeyMapping.java | 4 +- .../dao/service/BaseEntityServiceTest.java | 180 ++++++++++++++++++ 5 files changed, 221 insertions(+), 8 deletions(-) diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/query/RelationsQueryFilter.java b/common/data/src/main/java/org/thingsboard/server/common/data/query/RelationsQueryFilter.java index 9c113eb793..4409ebdab6 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/query/RelationsQueryFilter.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/query/RelationsQueryFilter.java @@ -21,6 +21,7 @@ import org.thingsboard.server.common.data.relation.EntitySearchDirection; import org.thingsboard.server.common.data.relation.RelationEntityTypeFilter; import java.util.List; +import java.util.Set; @Data public class RelationsQueryFilter implements EntityFilter { @@ -31,6 +32,8 @@ public class RelationsQueryFilter implements EntityFilter { } private EntityId rootEntity; + private boolean isMultiRoot; + private Set multiRootEntities; private EntitySearchDirection direction; private List filters; private int maxLevel; diff --git a/dao/src/main/java/org/thingsboard/server/dao/entity/BaseEntityService.java b/dao/src/main/java/org/thingsboard/server/dao/entity/BaseEntityService.java index 7686d9ec21..6c3fe1fd24 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/entity/BaseEntityService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/entity/BaseEntityService.java @@ -42,6 +42,8 @@ import org.thingsboard.server.common.data.query.EntityCountQuery; import org.thingsboard.server.common.data.query.EntityData; import org.thingsboard.server.common.data.query.EntityDataPageLink; import org.thingsboard.server.common.data.query.EntityDataQuery; +import org.thingsboard.server.common.data.query.EntityFilterType; +import org.thingsboard.server.common.data.query.RelationsQueryFilter; import org.thingsboard.server.dao.alarm.AlarmService; import org.thingsboard.server.dao.asset.AssetService; import org.thingsboard.server.dao.customer.CustomerService; @@ -228,6 +230,8 @@ public class BaseEntityService extends AbstractEntityService implements EntitySe throw new IncorrectParameterException("Query entity filter must be specified."); } else if (query.getEntityFilter().getType() == null) { throw new IncorrectParameterException("Query entity filter type must be specified."); + } else if (query.getEntityFilter().getType().equals(EntityFilterType.RELATIONS_QUERY)) { + validateRelationQuery((RelationsQueryFilter) query.getEntityFilter()); } } @@ -246,4 +250,12 @@ public class BaseEntityService extends AbstractEntityService implements EntitySe } } + private static void validateRelationQuery(RelationsQueryFilter queryFilter) { + if(queryFilter.isMultiRoot() && queryFilter.getMultiRootEntities().isEmpty()) { + throw new IncorrectParameterException("Multi-root relation query filter should contain root entities"); + } + if(!queryFilter.isMultiRoot() && queryFilter.getRootEntity() == null) { + throw new IncorrectParameterException("Relation query filter root entity should not be blank"); + } + } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/query/DefaultEntityQueryRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/query/DefaultEntityQueryRepository.java index 7b3ea36cfa..6a4212df6f 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/query/DefaultEntityQueryRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/query/DefaultEntityQueryRepository.java @@ -220,6 +220,8 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { " THEN (select additional_info from edge where id = entity_id)" + " END as additional_info"; + private static final String SELECT_RELATED_PARENT_ID = "entity.parentId AS parentId"; + private static final String SELECT_API_USAGE_STATE = "(select aus.id, aus.created_time, aus.tenant_id, aus.entity_id, " + "coalesce((select title from tenant where id = aus.entity_id), (select title from customer where id = aus.entity_id)) as name " + "from api_usage_state as aus)"; @@ -242,16 +244,16 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { private static final String HIERARCHICAL_QUERY_TEMPLATE = " FROM (WITH RECURSIVE related_entities(from_id, from_type, to_id, to_type, relation_type, lvl) AS (" + " SELECT from_id, from_type, to_id, to_type, relation_type, 1 as lvl" + " FROM relation" + - " WHERE $in_id = :relation_root_id and $in_type = :relation_root_type and relation_type_group = 'COMMON'" + + " WHERE $in_id $rootIdCondition and $in_type = :relation_root_type and relation_type_group = 'COMMON'" + " UNION ALL" + " SELECT r.from_id, r.from_type, r.to_id, r.to_type, r.relation_type, lvl + 1" + " FROM relation r" + " INNER JOIN related_entities re ON" + " r.$in_id = re.$out_id and r.$in_type = re.$out_type and" + " relation_type_group = 'COMMON' %s)" + - " SELECT re.$out_id entity_id, re.$out_type entity_type, max(re.lvl) lvl" + + " SELECT re.$out_id entity_id, re.$out_type entity_type, re.$in_id parentId, max(re.lvl) lvl" + " from related_entities re" + - " %s GROUP BY entity_id, entity_type) entity"; + " %s GROUP BY entity_id, entity_type, parentId) entity"; private static final String HIERARCHICAL_TO_QUERY_TEMPLATE = HIERARCHICAL_QUERY_TEMPLATE.replace("$in", "to").replace("$out", "from"); private static final String HIERARCHICAL_FROM_QUERY_TEMPLATE = HIERARCHICAL_QUERY_TEMPLATE.replace("$in", "from").replace("$out", "to"); @@ -562,6 +564,7 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { + (entityType.equals(EntityType.ENTITY_VIEW) ? "" : ", label ") + "FROM " + entityType.name() + " WHERE id in ( SELECT entity_id"; String from = getQueryTemplate(entityFilter.getDirection()); + from = from.replace("$rootIdCondition", "= :relation_root_id"); String whereFilter = " WHERE"; if (!StringUtils.isEmpty(entityFilter.getRelationType())) { ctx.addStringParameter("where_relation_type", entityFilter.getRelationType()); @@ -604,11 +607,20 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { + SELECT_TYPE + ", " + SELECT_NAME + ", " + SELECT_LABEL + ", " + SELECT_FIRST_NAME + ", " + SELECT_LAST_NAME + ", " + SELECT_EMAIL + ", " + SELECT_REGION + ", " + SELECT_TITLE + ", " + SELECT_COUNTRY + ", " + SELECT_STATE + ", " + SELECT_CITY + ", " + - SELECT_ADDRESS + ", " + SELECT_ADDRESS_2 + ", " + SELECT_ZIP + ", " + SELECT_PHONE + ", " + SELECT_ADDITIONAL_INFO + + SELECT_ADDRESS + ", " + SELECT_ADDRESS_2 + ", " + SELECT_ZIP + ", " + SELECT_PHONE + ", " + + SELECT_ADDITIONAL_INFO + ", " + SELECT_RELATED_PARENT_ID + ", entity.entity_type as entity_type"; String from = getQueryTemplate(entityFilter.getDirection()); - ctx.addUuidParameter("relation_root_id", rootId.getId()); - ctx.addStringParameter("relation_root_type", rootId.getEntityType().name()); + + if(entityFilter.isMultiRoot()) { + ctx.addUuidListParameter("relation_root_ids", entityFilter.getMultiRootEntities().stream().map(EntityId::getId).collect(Collectors.toList())); + ctx.addStringParameter("relation_root_type", entityFilter.getMultiRootEntities().stream().map(EntityId::getEntityType).findFirst().get().name()); + from = from.replace("$rootIdCondition", "in (:relation_root_ids)"); + } else { + ctx.addUuidParameter("relation_root_id", rootId.getId()); + ctx.addStringParameter("relation_root_type", rootId.getEntityType().name()); + from = from.replace("$rootIdCondition", "= :relation_root_id"); + } StringBuilder whereFilter = new StringBuilder(); @@ -790,7 +802,11 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { case EDGE_SEARCH_QUERY: return EntityType.EDGE; case RELATIONS_QUERY: - return ((RelationsQueryFilter) entityFilter).getRootEntity().getEntityType(); + RelationsQueryFilter rgf = (RelationsQueryFilter) entityFilter; + if(rgf.isMultiRoot()) { + return rgf.getMultiRootEntities().iterator().next().getEntityType(); + } + return rgf.getRootEntity().getEntityType(); case API_USAGE_STATE: return EntityType.API_USAGE_STATE; default: diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/query/EntityKeyMapping.java b/dao/src/main/java/org/thingsboard/server/dao/sql/query/EntityKeyMapping.java index caed48758a..3aab5ee63e 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/query/EntityKeyMapping.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/query/EntityKeyMapping.java @@ -72,6 +72,7 @@ public class EntityKeyMapping { public static final String ZIP = "zip"; public static final String PHONE = "phone"; public static final String ADDITIONAL_INFO = "additionalInfo"; + public static final String RELATED_PARENT_ID = "parentId"; public static final List typedEntityFields = Arrays.asList(CREATED_TIME, ENTITY_TYPE, NAME, TYPE, ADDITIONAL_INFO); public static final List widgetEntityFields = Arrays.asList(CREATED_TIME, ENTITY_TYPE, NAME); @@ -82,7 +83,7 @@ public class EntityKeyMapping { public static final Set apiUsageStateEntityFields = new HashSet<>(Arrays.asList(CREATED_TIME, ENTITY_TYPE, NAME)); public static final Set commonEntityFieldsSet = new HashSet<>(commonEntityFields); - public static final Set relationQueryEntityFieldsSet = new HashSet<>(Arrays.asList(CREATED_TIME, ENTITY_TYPE, NAME, TYPE, LABEL, FIRST_NAME, LAST_NAME, EMAIL, REGION, TITLE, COUNTRY, STATE, CITY, ADDRESS, ADDRESS_2, ZIP, PHONE, ADDITIONAL_INFO)); + public static final Set relationQueryEntityFieldsSet = new HashSet<>(Arrays.asList(CREATED_TIME, ENTITY_TYPE, NAME, TYPE, LABEL, FIRST_NAME, LAST_NAME, EMAIL, REGION, TITLE, COUNTRY, STATE, CITY, ADDRESS, ADDRESS_2, ZIP, PHONE, ADDITIONAL_INFO, RELATED_PARENT_ID)); static { allowedEntityFieldMap.put(EntityType.DEVICE, new HashSet<>(labeledEntityFields)); @@ -120,6 +121,7 @@ public class EntityKeyMapping { entityFieldColumnMap.put(ZIP, ModelConstants.ZIP_PROPERTY); entityFieldColumnMap.put(PHONE, ModelConstants.PHONE_PROPERTY); entityFieldColumnMap.put(ADDITIONAL_INFO, ModelConstants.ADDITIONAL_INFO_PROPERTY); + entityFieldColumnMap.put(RELATED_PARENT_ID, RELATED_PARENT_ID); Map contactBasedAliases = new HashMap<>(); contactBasedAliases.put(NAME, TITLE); diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/BaseEntityServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/BaseEntityServiceTest.java index 831d92965b..ab8d8233e1 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/BaseEntityServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/BaseEntityServiceTest.java @@ -15,10 +15,12 @@ */ package org.thingsboard.server.dao.service; +import com.google.common.collect.Lists; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import org.apache.commons.lang3.RandomStringUtils; import org.apache.commons.lang3.RandomUtils; +import org.apache.commons.lang3.StringUtils; import org.junit.After; import org.junit.Assert; import org.junit.Before; @@ -35,6 +37,7 @@ 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.IdBased; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.kv.AttributeKvEntry; import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; @@ -75,8 +78,11 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Random; +import java.util.UUID; import java.util.concurrent.ExecutionException; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -373,6 +379,108 @@ public abstract class BaseEntityServiceTest extends AbstractServiceTest { deviceService.deleteDevicesByTenantId(tenantId); } + @Test + public void testCountHierarchicalEntitiesByMultiRootQuery() throws InterruptedException { + List buildings = new ArrayList<>(); + List apartments = new ArrayList<>(); + Map> entityNameByTypeMap = new HashMap<>(); + Map childParentRelationMap = new HashMap<>(); + createMultiRootHierarchy(buildings, apartments, entityNameByTypeMap, childParentRelationMap); + + RelationsQueryFilter filter = new RelationsQueryFilter(); + filter.setMultiRoot(true); + filter.setMultiRootEntities(buildings.stream().map(IdBased::getId).collect(Collectors.toSet())); + filter.setDirection(EntitySearchDirection.FROM); + + EntityCountQuery countQuery = new EntityCountQuery(filter); + + long count = entityService.countEntitiesByQuery(tenantId, new CustomerId(CustomerId.NULL_UUID), countQuery); + Assert.assertEquals(63, count); + + filter.setFilters(Collections.singletonList(new RelationEntityTypeFilter("AptToHeat", Collections.singletonList(EntityType.DEVICE)))); + count = entityService.countEntitiesByQuery(tenantId, new CustomerId(CustomerId.NULL_UUID), countQuery); + Assert.assertEquals(27, count); + + filter.setMultiRootEntities(apartments.stream().map(IdBased::getId).collect(Collectors.toSet())); + filter.setDirection(EntitySearchDirection.TO); + filter.setFilters(Lists.newArrayList( + new RelationEntityTypeFilter("buildingToApt", Collections.singletonList(EntityType.ASSET)), + new RelationEntityTypeFilter("AptToEnergy", Collections.singletonList(EntityType.DEVICE)))); + + count = entityService.countEntitiesByQuery(tenantId, new CustomerId(CustomerId.NULL_UUID), countQuery); + Assert.assertEquals(9, count); + + deviceService.deleteDevicesByTenantId(tenantId); + assetService.deleteAssetsByTenantId(tenantId); + + } + + @Test + public void testMultiRootHierarchicalFindEntityDataWithAttributesByQuery() throws ExecutionException, InterruptedException { + List buildings = new ArrayList<>(); + List apartments = new ArrayList<>(); + Map> entityNameByTypeMap = new HashMap<>(); + Map childParentRelationMap = new HashMap<>(); + createMultiRootHierarchy(buildings, apartments, entityNameByTypeMap, childParentRelationMap); + + RelationsQueryFilter filter = new RelationsQueryFilter(); + filter.setMultiRoot(true); + filter.setMultiRootEntities(buildings.stream().map(IdBased::getId).collect(Collectors.toSet())); + filter.setDirection(EntitySearchDirection.FROM); + + EntityDataSortOrder sortOrder = new EntityDataSortOrder( + new EntityKey(EntityKeyType.ENTITY_FIELD, "createdTime"), EntityDataSortOrder.Direction.ASC + ); + EntityDataPageLink pageLink = new EntityDataPageLink(10, 0, null, sortOrder); + List entityFields = Lists.newArrayList( + new EntityKey(EntityKeyType.ENTITY_FIELD, "name"), + new EntityKey(EntityKeyType.ENTITY_FIELD, "parentId"), + new EntityKey(EntityKeyType.ENTITY_FIELD, "type") + ); + List latestValues = Collections.singletonList(new EntityKey(EntityKeyType.ATTRIBUTE, "status")); + + KeyFilter onlineStatusFilter = new KeyFilter(); + onlineStatusFilter.setKey(new EntityKey(EntityKeyType.ENTITY_FIELD, "name")); + StringFilterPredicate predicate = new StringFilterPredicate(); + predicate.setOperation(StringOperation.ENDS_WITH); + predicate.setValue(FilterPredicateValue.fromString("_1")); + onlineStatusFilter.setPredicate(predicate); + List keyFilters = Collections.singletonList(onlineStatusFilter); + + EntityDataQuery query = new EntityDataQuery(filter, pageLink, entityFields, latestValues, keyFilters); + PageData data = entityService.findEntityDataByQuery(tenantId, new CustomerId(CustomerId.NULL_UUID), query); + List loadedEntities = new ArrayList<>(data.getData()); + while (data.hasNext()) { + query = query.next(); + data = entityService.findEntityDataByQuery(tenantId, new CustomerId(CustomerId.NULL_UUID), query); + loadedEntities.addAll(data.getData()); + } + + long expectedEntitiesCnt = entityNameByTypeMap.entrySet() + .stream() + .filter(e -> !e.getKey().equals("building")) + .flatMap(e -> e.getValue().entrySet().stream()) + .map(Map.Entry::getValue) + .filter(e -> StringUtils.endsWith(e, "_1")) + .count(); + Assert.assertEquals(expectedEntitiesCnt, loadedEntities.size()); + + Map actualRelations = new HashMap<>(); + loadedEntities.forEach(ed -> { + UUID parentId = UUID.fromString(ed.getLatest().get(EntityKeyType.ENTITY_FIELD).get("parentId").getValue()); + UUID entityId = ed.getEntityId().getId(); + Assert.assertEquals(childParentRelationMap.get(entityId), parentId); + actualRelations.put(entityId, parentId); + + String entityType = ed.getLatest().get(EntityKeyType.ENTITY_FIELD).get("type").getValue(); + String actualEntityName = ed.getLatest().get(EntityKeyType.ENTITY_FIELD).get("name").getValue(); + String expectedEntityName = entityNameByTypeMap.get(entityType).get(entityId); + Assert.assertEquals(expectedEntityName, actualEntityName); + }); + + deviceService.deleteDevicesByTenantId(tenantId); + assetService.deleteAssetsByTenantId(tenantId); + } @Test public void testHierarchicalFindDevicesWithAttributesByQuery() throws ExecutionException, InterruptedException { @@ -1534,4 +1642,76 @@ public abstract class BaseEntityServiceTest extends AbstractServiceTest { BasicTsKvEntry timeseries = new BasicTsKvEntry(42L, telemetryValue); return timeseriesService.save(SYSTEM_TENANT_ID, entityId, timeseries); } + + private void createMultiRootHierarchy(List buildings, List apartments, + Map> entityNameByTypeMap, + Map childParentRelationMap) throws InterruptedException { + for (int k = 0; k < 3; k++) { + Asset building = new Asset(); + building.setTenantId(tenantId); + building.setName("Building _" + k); + building.setType("building"); + building.setLabel("building label" + k); + building = assetService.saveAsset(building); + buildings.add(building); + entityNameByTypeMap.computeIfAbsent(building.getType(), n -> new HashMap<>()).put(building.getId().getId(), building.getName()); + + for (int i = 0; i < 3; i++) { + Asset asset = new Asset(); + asset.setTenantId(tenantId); + asset.setName("Apt " + k + "_" + i); + asset.setType("apartment"); + asset.setLabel("apartment " + i); + asset = assetService.saveAsset(asset); + //TO make sure devices have different created time + Thread.sleep(1); + entityNameByTypeMap.computeIfAbsent(asset.getType(), n -> new HashMap<>()).put(asset.getId().getId(), asset.getName()); + apartments.add(asset); + EntityRelation er = new EntityRelation(); + er.setFrom(building.getId()); + er.setTo(asset.getId()); + er.setType("buildingToApt"); + er.setTypeGroup(RelationTypeGroup.COMMON); + relationService.saveRelation(tenantId, er); + childParentRelationMap.put(asset.getUuidId(), building.getUuidId()); + for (int j = 0; j < 3; j++) { + Device device = new Device(); + device.setTenantId(tenantId); + device.setName("Heat" + k + "_" + i + "_" + j); + device.setType("heatmeter"); + device.setLabel("heatmeter" + (int) (Math.random() * 1000)); + device = deviceService.saveDevice(device); + //TO make sure devices have different created time + Thread.sleep(1); + entityNameByTypeMap.computeIfAbsent(device.getType(), n -> new HashMap<>()).put(device.getId().getId(), device.getName()); + er = new EntityRelation(); + er.setFrom(asset.getId()); + er.setTo(device.getId()); + er.setType("AptToHeat"); + er.setTypeGroup(RelationTypeGroup.COMMON); + relationService.saveRelation(tenantId, er); + childParentRelationMap.put(device.getUuidId(), asset.getUuidId()); + } + + for (int j = 0; j < 3; j++) { + Device device = new Device(); + device.setTenantId(tenantId); + device.setName("Energy" + k + "_" + i + "_" + j); + device.setType("energymeter"); + device.setLabel("energymeter" + (int) (Math.random() * 1000)); + device = deviceService.saveDevice(device); + //TO make sure devices have different created time + Thread.sleep(1); + entityNameByTypeMap.computeIfAbsent(device.getType(), n -> new HashMap<>()).put(device.getId().getId(), device.getName()); + er = new EntityRelation(); + er.setFrom(asset.getId()); + er.setTo(device.getId()); + er.setType("AptToEnergy"); + er.setTypeGroup(RelationTypeGroup.COMMON); + relationService.saveRelation(tenantId, er); + childParentRelationMap.put(device.getUuidId(), asset.getUuidId()); + } + } + } + } } From 5a7673a118787834f1ec06d5b742af3cc777383b Mon Sep 17 00:00:00 2001 From: hanyuepeng Date: Wed, 10 Nov 2021 17:16:17 +0800 Subject: [PATCH 005/798] fix(chinese translate): fix some display problem In Api Usage module,when use chinese ,there are some display problem because of the lose of the translate items --- ui-ngx/src/assets/locale/locale.constant-zh_CN.json | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/ui-ngx/src/assets/locale/locale.constant-zh_CN.json b/ui-ngx/src/assets/locale/locale.constant-zh_CN.json index 270fd6ad4e..39f4063b49 100644 --- a/ui-ngx/src/assets/locale/locale.constant-zh_CN.json +++ b/ui-ngx/src/assets/locale/locale.constant-zh_CN.json @@ -388,7 +388,14 @@ "transport-messages": "传输消息", "transport-monthly-activity": "Transport monthly activity", "view-details": "查看详细信息", - "view-statistics": "查看统计信息" + "view-statistics": "查看统计信息", + "alarm":"警告", + "alarms-created": "创建警告数", + "alarms-created-hourly-activity":"Alarms created hourly activity", + "alarms-created-daily-activity": "Alarms created daily activity", + "alarms-created-monthly-activity": "Alarms created monthly activity", + "notifications": "通知", + "notifications-hourly-activity": "Notifications hourly activity" }, "asset": { "add": "添加资产", From 524ff2f980cf4a5baa963dc29aaf2455e1e7c4d7 Mon Sep 17 00:00:00 2001 From: Vladyslav Date: Wed, 10 Nov 2021 14:36:28 +0200 Subject: [PATCH 006/798] Update locale.constant-zh_CN.json --- ui-ngx/src/assets/locale/locale.constant-zh_CN.json | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/ui-ngx/src/assets/locale/locale.constant-zh_CN.json b/ui-ngx/src/assets/locale/locale.constant-zh_CN.json index 39f4063b49..f8e7e810fe 100644 --- a/ui-ngx/src/assets/locale/locale.constant-zh_CN.json +++ b/ui-ngx/src/assets/locale/locale.constant-zh_CN.json @@ -391,11 +391,7 @@ "view-statistics": "查看统计信息", "alarm":"警告", "alarms-created": "创建警告数", - "alarms-created-hourly-activity":"Alarms created hourly activity", - "alarms-created-daily-activity": "Alarms created daily activity", - "alarms-created-monthly-activity": "Alarms created monthly activity", - "notifications": "通知", - "notifications-hourly-activity": "Notifications hourly activity" + "notifications": "通知" }, "asset": { "add": "添加资产", From 79f3d3826a1c916288c8a89b15c9be64d068d9ef Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Fri, 12 Nov 2021 15:52:58 +0200 Subject: [PATCH 007/798] UI: Added clearing selectedWidgetsBundleAlias when calling resetSortAndFilter function in attribute table --- .../home/components/attribute/attribute-table.component.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/ui-ngx/src/app/modules/home/components/attribute/attribute-table.component.ts b/ui-ngx/src/app/modules/home/components/attribute/attribute-table.component.ts index bcb9edd07e..9e9bbceb6f 100644 --- a/ui-ngx/src/app/modules/home/components/attribute/attribute-table.component.ts +++ b/ui-ngx/src/app/modules/home/components/attribute/attribute-table.component.ts @@ -263,6 +263,7 @@ export class AttributeTableComponent extends PageComponent implements AfterViewI this.attributeScopeSelectionReadonly = true; } this.mode = 'default'; + this.selectedWidgetsBundleAlias = null; this.attributeScope = this.defaultAttributeScope; this.pageLink.textSearch = null; if (this.viewsInited) { From 5d4579ce288bcd6750b9a8a82e80f94d757c26ed Mon Sep 17 00:00:00 2001 From: ArtemDzhereleiko Date: Fri, 26 Nov 2021 17:00:51 +0200 Subject: [PATCH 008/798] UI: New control widget Persistent table --- .../widget_bundles/control_widgets.json | 18 + .../server/controller/RpcV2Controller.java | 11 +- .../server/dao/rpc/RpcService.java | 2 + .../server/dao/rpc/BaseRpcService.java | 10 +- .../thingsboard/server/dao/rpc/RpcDao.java | 4 +- .../server/dao/sql/rpc/JpaRpcDao.java | 7 +- .../server/dao/sql/rpc/RpcRepository.java | 2 + ui-ngx/src/app/core/api/widget-api.models.ts | 14 +- .../src/app/core/api/widget-subscription.ts | 23 +- ui-ngx/src/app/core/http/device.service.ts | 13 +- .../rpc/persistent-add-dialog.component.html | 92 ++++ .../rpc/persistent-add-dialog.component.scss | 30 ++ .../rpc/persistent-add-dialog.component.ts | 77 +++ .../persistent-details-dialog.component.html | 118 +++++ .../persistent-details-dialog.component.scss | 33 ++ .../persistent-details-dialog.component.ts | 151 ++++++ .../persistent-filter-panel.component.html | 44 ++ .../persistent-filter-panel.component.scss | 36 ++ .../rpc/persistent-filter-panel.component.ts | 71 +++ .../lib/rpc/persistent-table.component.html | 125 +++++ .../lib/rpc/persistent-table.component.scss | 52 ++ .../lib/rpc/persistent-table.component.ts | 476 ++++++++++++++++++ .../widget/lib/rpc/rpc-widgets.module.ts | 15 +- .../home/models/widget-component.models.ts | 10 +- .../json-object-view.component.html | 22 + .../json-object-view.component.scss | 27 + .../components/json-object-view.component.ts | 166 ++++++ ui-ngx/src/app/shared/models/rpc.models.ts | 51 +- ui-ngx/src/app/shared/shared.module.ts | 3 + .../assets/locale/locale.constant-en_US.json | 44 +- .../assets/locale/locale.constant-ru_RU.json | 41 ++ .../assets/locale/locale.constant-uk_UA.json | 41 ++ 32 files changed, 1805 insertions(+), 24 deletions(-) create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-add-dialog.component.html create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-add-dialog.component.scss create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-add-dialog.component.ts create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-details-dialog.component.html create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-details-dialog.component.scss create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-details-dialog.component.ts create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-filter-panel.component.html create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-filter-panel.component.scss create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-filter-panel.component.ts create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-table.component.html create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-table.component.scss create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-table.component.ts create mode 100644 ui-ngx/src/app/shared/components/json-object-view.component.html create mode 100644 ui-ngx/src/app/shared/components/json-object-view.component.scss create mode 100644 ui-ngx/src/app/shared/components/json-object-view.component.ts diff --git a/application/src/main/data/json/system/widget_bundles/control_widgets.json b/application/src/main/data/json/system/widget_bundles/control_widgets.json index f1e3eaa1fe..73a1ef633d 100644 --- a/application/src/main/data/json/system/widget_bundles/control_widgets.json +++ b/application/src/main/data/json/system/widget_bundles/control_widgets.json @@ -149,6 +149,24 @@ "dataKeySettingsSchema": "{}\n", "defaultConfig": "{\"showTitle\":false,\"backgroundColor\":\"#e6e7e8\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"styleButton\":{\"isRaised\":true,\"isPrimary\":false},\"entityParameters\":\"{}\",\"entityAttributeType\":\"SERVER_SCOPE\",\"buttonText\":\"Update device attribute\"},\"title\":\"Update device attribute\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{},\"targetDeviceAliases\":[]}" } + }, + { + "alias": "persistent_table", + "name": "Persistent table", + "image": "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgdmlld0JveD0iMCAwIDIwMCAxNjAiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxwYXRoIGQ9Ik0yMDAgMEgwVjE2MEgyMDBWMFoiIGZpbGw9IndoaXRlIi8+CjxwYXRoIGQ9Ik0yMDAgMTIwSDBWMTIxSDIwMFYxMjBaIiBmaWxsPSIjRTBFMEUwIi8+CjxwYXRoIGQ9Ik0yMDAgODBIMFY4MUgyMDBWODBaIiBmaWxsPSIjRTBFMEUwIi8+CjxwYXRoIGQ9Ik0yMDAgMzlIMFY0MEgyMDBWMzlaIiBmaWxsPSIjRTBFMEUwIi8+CjxwYXRoIGQ9Ik0xNS42Njg1IDE5Ljk4NjhIMTQuMTUzOFYyM0gxMi43OTQ5VjE1LjE3OTdIMTUuNTQ0OUMxNi40NDczIDE1LjE3OTcgMTcuMTQzNyAxNS4zODIgMTcuNjM0MyAxNS43ODY2QzE4LjEyNDggMTYuMTkxMiAxOC4zNzAxIDE2Ljc3NjcgMTguMzcwMSAxNy41NDNDMTguMzcwMSAxOC4wNjU4IDE4LjI0MyAxOC41MDQ0IDE3Ljk4ODggMTguODU4OUMxNy43MzgxIDE5LjIwOTggMTcuMzg3MiAxOS40ODAxIDE2LjkzNiAxOS42Njk5TDE4LjY5MjQgMjIuOTMwMlYyM0gxNy4yMzY4TDE1LjY2ODUgMTkuOTg2OFpNMTQuMTUzOCAxOC44OTY1SDE1LjU1MDNDMTYuMDA4NiAxOC44OTY1IDE2LjM2NjcgMTguNzgxOSAxNi42MjQ1IDE4LjU1MjdDMTYuODgyMyAxOC4zMiAxNy4wMTEyIDE4LjAwMzEgMTcuMDExMiAxNy42MDIxQzE3LjAxMTIgMTcuMTgzMSAxNi44OTEzIDE2Ljg1OSAxNi42NTE0IDE2LjYyOTlDMTYuNDE1IDE2LjQwMDcgMTYuMDYwNSAxNi4yODI2IDE1LjU4NzkgMTYuMjc1NEgxNC4xNTM4VjE4Ljg5NjVaIiBmaWxsPSJibGFjayIgZmlsbC1vcGFjaXR5PSIwLjg3Ii8+CjxwYXRoIGQ9Ik0yMS4wMTgxIDIwLjA5NDJWMjNIMTkuNjU5MlYxNS4xNzk3SDIyLjY1MDlDMjMuNTI0NiAxNS4xNzk3IDI0LjIxNzQgMTUuNDA3MSAyNC43Mjk1IDE1Ljg2MThDMjUuMjQ1MSAxNi4zMTY2IDI1LjUwMjkgMTYuOTE4MSAyNS41MDI5IDE3LjY2NjVDMjUuNTAyOSAxOC40MzI4IDI1LjI1MDUgMTkuMDI5IDI0Ljc0NTYgMTkuNDU1MUMyNC4yNDQzIDE5Ljg4MTIgMjMuNTQwNyAyMC4wOTQyIDIyLjYzNDggMjAuMDk0MkgyMS4wMTgxWk0yMS4wMTgxIDE5LjAwMzlIMjIuNjUwOUMyMy4xMzQzIDE5LjAwMzkgMjMuNTAzMSAxOC44OTExIDIzLjc1NzMgMTguNjY1NUMyNC4wMTE2IDE4LjQzNjQgMjQuMTM4NyAxOC4xMDY5IDI0LjEzODcgMTcuNjc3MkMyNC4xMzg3IDE3LjI1NDcgMjQuMDA5OCAxNi45MTgxIDIzLjc1MiAxNi42Njc1QzIzLjQ5NDEgMTYuNDEzMiAyMy4xMzk2IDE2LjI4MjYgMjIuNjg4NSAxNi4yNzU0SDIxLjAxODFWMTkuMDAzOVoiIGZpbGw9ImJsYWNrIiBmaWxsLW9wYWNpdHk9IjAuODciLz4KPHBhdGggZD0iTTMyLjY2MjYgMjAuNDU0MUMzMi41ODM4IDIxLjI4ODQgMzIuMjc1OSAyMS45NDAxIDMxLjczODggMjIuNDA5MkMzMS4yMDE3IDIyLjg3NDcgMzAuNDg3MyAyMy4xMDc0IDI5LjU5NTcgMjMuMTA3NEMyOC45NzI3IDIzLjEwNzQgMjguNDIzIDIyLjk2MDYgMjcuOTQ2OCAyMi42NjdDMjcuNDc0MSAyMi4zNjk4IDI3LjEwODkgMjEuOTQ5MSAyNi44NTExIDIxLjQwNDhDMjYuNTkzMyAyMC44NjA1IDI2LjQ1OSAyMC4yMjg1IDI2LjQ0ODIgMTkuNTA4OFYxOC43NzgzQzI2LjQ0ODIgMTguMDQwNyAyNi41Nzg5IDE3LjM5MDggMjYuODQwMyAxNi44Mjg2QzI3LjEwMTcgMTYuMjY2NCAyNy40NzU5IDE1LjgzMzIgMjcuOTYyOSAxNS41Mjg4QzI4LjQ1MzUgMTUuMjI0NCAyOS4wMTkyIDE1LjA3MjMgMjkuNjYwMiAxNS4wNzIzQzMwLjUyMzEgMTUuMDcyMyAzMS4yMTc4IDE1LjMwNjggMzEuNzQ0MSAxNS43NzU5QzMyLjI3MDUgMTYuMjQ1IDMyLjU3NjcgMTYuOTA3NCAzMi42NjI2IDE3Ljc2MzJIMzEuMzA5MUMzMS4yNDQ2IDE3LjIwMSAzMS4wNzk5IDE2Ljc5NjQgMzAuODE0OSAxNi41NDkzQzMwLjU1MzUgMTYuMjk4NyAzMC4xNjg2IDE2LjE3MzMgMjkuNjYwMiAxNi4xNzMzQzI5LjA2OTMgMTYuMTczMyAyOC42MTQ2IDE2LjM5IDI4LjI5NTkgMTYuODIzMkMyNy45ODA4IDE3LjI1MjkgMjcuODE5NyAxNy44ODQ5IDI3LjgxMjUgMTguNzE5MlYxOS40MTIxQzI3LjgxMjUgMjAuMjU3MiAyNy45NjI5IDIwLjkwMTcgMjguMjYzNyAyMS4zNDU3QzI4LjU2OCAyMS43ODk3IDI5LjAxMiAyMi4wMTE3IDI5LjU5NTcgMjIuMDExN0MzMC4xMjkyIDIyLjAxMTcgMzAuNTMwMyAyMS44OTE4IDMwLjc5ODggMjEuNjUxOUMzMS4wNjc0IDIxLjQxMTkgMzEuMjM3NSAyMS4wMTI3IDMxLjMwOTEgMjAuNDU0MUgzMi42NjI2WiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC44NyIvPgo8cGF0aCBkPSJNMzguMDU1MiAyM0gzNi43MDE3VjE1LjE3OTdIMzguMDU1MlYyM1oiIGZpbGw9ImJsYWNrIiBmaWxsLW9wYWNpdHk9IjAuODciLz4KPHBhdGggZD0iTTM5LjcyNTYgMjNWMTUuMTc5N0g0Mi4wMzUyQzQyLjcyNjIgMTUuMTc5NyA0My4zMzg1IDE1LjMzMzcgNDMuODcyMSAxNS42NDE2QzQ0LjQwOTIgMTUuOTQ5NSA0NC44MjQ1IDE2LjM4NjQgNDUuMTE4MiAxNi45NTIxQzQ1LjQxMTggMTcuNTE3OSA0NS41NTg2IDE4LjE2NiA0NS41NTg2IDE4Ljg5NjVWMTkuMjg4NkM0NS41NTg2IDIwLjAyOTggNDUuNDEgMjAuNjgxNSA0NS4xMTI4IDIxLjI0MzdDNDQuODE5MiAyMS44MDU4IDQ0LjM5ODQgMjIuMjM5MSA0My44NTA2IDIyLjU0MzVDNDMuMzA2MyAyMi44NDc4IDQyLjY4MTUgMjMgNDEuOTc2MSAyM0gzOS43MjU2Wk00MS4wODQ1IDE2LjI3NTRWMjEuOTE1SDQxLjk3MDdDNDIuNjgzMyAyMS45MTUgNDMuMjI5MyAyMS42OTMgNDMuNjA4OSAyMS4yNDlDNDMuOTkyIDIwLjgwMTQgNDQuMTg3MiAyMC4xNjA1IDQ0LjE5NDMgMTkuMzI2MlYxOC44OTExQzQ0LjE5NDMgMTguMDQyNSA0NC4wMDk5IDE3LjM5NDQgNDMuNjQxMSAxNi45NDY4QzQzLjI3MjMgMTYuNDk5MiA0Mi43MzcgMTYuMjc1NCA0Mi4wMzUyIDE2LjI3NTRINDEuMDg0NVoiIGZpbGw9ImJsYWNrIiBmaWxsLW9wYWNpdHk9IjAuODciLz4KPHBhdGggZD0iTTYxLjU1NjYgMTUuMTc5N0w2My44MTI1IDIxLjE3MzhMNjYuMDYzIDE1LjE3OTdINjcuODE5M1YyM0g2Ni40NjU4VjIwLjQyMTlMNjYuNjAwMSAxNi45NzM2TDY0LjI5MDUgMjNINjMuMzE4NEw2MS4wMTQyIDE2Ljk3OUw2MS4xNDg0IDIwLjQyMTlWMjNINTkuNzk0OVYxNS4xNzk3SDYxLjU1NjZaIiBmaWxsPSJibGFjayIgZmlsbC1vcGFjaXR5PSIwLjg3Ii8+CjxwYXRoIGQ9Ik03MS44NjM4IDIzLjEwNzRDNzEuMDM2NiAyMy4xMDc0IDcwLjM2NTIgMjIuODQ3OCA2OS44NDk2IDIyLjMyODZDNjkuMzM3NiAyMS44MDU4IDY5LjA4MTUgMjEuMTExMiA2OS4wODE1IDIwLjI0NDZWMjAuMDgzNUM2OS4wODE1IDE5LjUwMzQgNjkuMTkyNSAxOC45ODYgNjkuNDE0NiAxOC41MzEyQzY5LjY0MDEgMTguMDcyOSA2OS45NTUyIDE3LjcxNjYgNzAuMzU5OSAxNy40NjI0QzcwLjc2NDUgMTcuMjA4MiA3MS4yMTU3IDE3LjA4MTEgNzEuNzEzNCAxNy4wODExQzcyLjUwNDcgMTcuMDgxMSA3My4xMTUyIDE3LjMzMzUgNzMuNTQ0OSAxNy44Mzg0QzczLjk3ODIgMTguMzQzMyA3NC4xOTQ4IDE5LjA1NzYgNzQuMTk0OCAxOS45ODE0VjIwLjUwNzhINzAuMzk3NUM3MC40MzY4IDIwLjk4NzYgNzAuNTk2MiAyMS4zNjcyIDcwLjg3NTUgMjEuNjQ2NUM3MS4xNTg0IDIxLjkyNTggNzEuNTEyOSAyMi4wNjU0IDcxLjkzOSAyMi4wNjU0QzcyLjUzNjkgMjIuMDY1NCA3My4wMjM5IDIxLjgyMzcgNzMuMzk5OSAyMS4zNDAzTDc0LjEwMzUgMjIuMDExN0M3My44NzA4IDIyLjM1OSA3My41NTkyIDIyLjYyOTQgNzMuMTY4OSAyMi44MjI4QzcyLjc4MjIgMjMuMDEyNSA3Mi4zNDcyIDIzLjEwNzQgNzEuODYzOCAyMy4xMDc0Wk03MS43MDggMTguMTI4NEM3MS4zNDk5IDE4LjEyODQgNzEuMDU5OSAxOC4yNTM3IDcwLjgzNzkgMTguNTA0NEM3MC42MTk1IDE4Ljc1NSA3MC40Nzk4IDE5LjEwNDIgNzAuNDE4OSAxOS41NTE4SDcyLjkwNThWMTkuNDU1MUM3Mi44NzcxIDE5LjAxODIgNzIuNzYwNyAxOC42ODg4IDcyLjU1NjYgMTguNDY2OEM3Mi4zNTI1IDE4LjI0MTIgNzIuMDY5NyAxOC4xMjg0IDcxLjcwOCAxOC4xMjg0WiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC44NyIvPgo8cGF0aCBkPSJNNzguNDcwMiAyMS40MjA5Qzc4LjQ3MDIgMjEuMTg4MiA3OC4zNzM1IDIxLjAxMDkgNzguMTgwMiAyMC44ODkyQzc3Ljk5MDQgMjAuNzY3NCA3Ny42NzM1IDIwLjY2IDc3LjIyOTUgMjAuNTY2OUM3Ni43ODU1IDIwLjQ3MzggNzYuNDE0OSAyMC4zNTU2IDc2LjExNzcgMjAuMjEyNEM3NS40NjYgMTkuODk3MyA3NS4xNDAxIDE5LjQ0MDggNzUuMTQwMSAxOC44NDI4Qzc1LjE0MDEgMTguMzQxNSA3NS4zNTE0IDE3LjkyMjUgNzUuNzczOSAxNy41ODU5Qzc2LjE5NjUgMTcuMjQ5MyA3Ni43MzM2IDE3LjA4MTEgNzcuMzg1MyAxNy4wODExQzc4LjA3OTkgMTcuMDgxMSA3OC42NDAzIDE3LjI1MjkgNzkuMDY2NCAxNy41OTY3Qzc5LjQ5NjEgMTcuOTQwNCA3OS43MTA5IDE4LjM4NjIgNzkuNzEwOSAxOC45MzQxSDc4LjQwNThDNzguNDA1OCAxOC42ODM0IDc4LjMxMjcgMTguNDc1NyA3OC4xMjY1IDE4LjMxMUM3Ny45NDAzIDE4LjE0MjcgNzcuNjkzMiAxOC4wNTg2IDc3LjM4NTMgMTguMDU4NkM3Ny4wOTg4IDE4LjA1ODYgNzYuODY0MyAxOC4xMjQ4IDc2LjY4MTYgMTguMjU3M0M3Ni41MDI2IDE4LjM4OTggNzYuNDEzMSAxOC41NjcxIDc2LjQxMzEgMTguNzg5MUM3Ni40MTMxIDE4Ljk4OTYgNzYuNDk3MiAxOS4xNDUzIDc2LjY2NTUgMTkuMjU2M0M3Ni44MzM4IDE5LjM2NzQgNzcuMTc0IDE5LjQ4MDEgNzcuNjg2IDE5LjU5NDdDNzguMTk4MSAxOS43MDU3IDc4LjU5OTEgMTkuODQgNzguODg5MiAxOS45OTc2Qzc5LjE4MjggMjAuMTUxNSA3OS4zOTk0IDIwLjMzNzcgNzkuNTM5MSAyMC41NTYyQzc5LjY4MjMgMjAuNzc0NiA3OS43NTM5IDIxLjAzOTYgNzkuNzUzOSAyMS4zNTExQzc5Ljc1MzkgMjEuODczOSA3OS41MzczIDIyLjI5ODIgNzkuMTA0IDIyLjYyNEM3OC42NzA3IDIyLjk0NjMgNzguMTAzMiAyMy4xMDc0IDc3LjQwMTQgMjMuMTA3NEM3Ni45MjUxIDIzLjEwNzQgNzYuNTAwOCAyMy4wMjE1IDc2LjEyODQgMjIuODQ5NkM3NS43NTYgMjIuNjc3NyA3NS40NjYgMjIuNDQxNCA3NS4yNTgzIDIyLjE0MDZDNzUuMDUwNiAyMS44Mzk4IDc0Ljk0NjggMjEuNTE1OCA3NC45NDY4IDIxLjE2ODVINzYuMjE0NEM3Ni4yMzIzIDIxLjQ3NjQgNzYuMzQ4NiAyMS43MTQ1IDc2LjU2MzUgMjEuODgyOEM3Ni43NzgzIDIyLjA0NzUgNzcuMDYzIDIyLjEyOTkgNzcuNDE3NSAyMi4xMjk5Qzc3Ljc2MTIgMjIuMTI5OSA3OC4wMjI2IDIyLjA2NTQgNzguMjAxNyAyMS45MzY1Qzc4LjM4MDcgMjEuODA0IDc4LjQ3MDIgMjEuNjMyMiA3OC40NzAyIDIxLjQyMDlaIiBmaWxsPSJibGFjayIgZmlsbC1vcGFjaXR5PSIwLjg3Ii8+CjxwYXRoIGQ9Ik04NC4xNTI4IDIxLjQyMDlDODQuMTUyOCAyMS4xODgyIDg0LjA1NjIgMjEuMDEwOSA4My44NjI4IDIwLjg4OTJDODMuNjczIDIwLjc2NzQgODMuMzU2MSAyMC42NiA4Mi45MTIxIDIwLjU2NjlDODIuNDY4MSAyMC40NzM4IDgyLjA5NzUgMjAuMzU1NiA4MS44MDAzIDIwLjIxMjRDODEuMTQ4NiAxOS44OTczIDgwLjgyMjggMTkuNDQwOCA4MC44MjI4IDE4Ljg0MjhDODAuODIyOCAxOC4zNDE1IDgxLjAzNCAxNy45MjI1IDgxLjQ1NjUgMTcuNTg1OUM4MS44NzkxIDE3LjI0OTMgODIuNDE2MiAxNy4wODExIDgzLjA2NzkgMTcuMDgxMUM4My43NjI1IDE3LjA4MTEgODQuMzIyOSAxNy4yNTI5IDg0Ljc0OSAxNy41OTY3Qzg1LjE3ODcgMTcuOTQwNCA4NS4zOTM2IDE4LjM4NjIgODUuMzkzNiAxOC45MzQxSDg0LjA4ODRDODQuMDg4NCAxOC42ODM0IDgzLjk5NTMgMTguNDc1NyA4My44MDkxIDE4LjMxMUM4My42MjI5IDE4LjE0MjcgODMuMzc1OCAxOC4wNTg2IDgzLjA2NzkgMTguMDU4NkM4Mi43ODE0IDE4LjA1ODYgODIuNTQ2OSAxOC4xMjQ4IDgyLjM2NDMgMTguMjU3M0M4Mi4xODUyIDE4LjM4OTggODIuMDk1NyAxOC41NjcxIDgyLjA5NTcgMTguNzg5MUM4Mi4wOTU3IDE4Ljk4OTYgODIuMTc5OSAxOS4xNDUzIDgyLjM0ODEgMTkuMjU2M0M4Mi41MTY0IDE5LjM2NzQgODIuODU2NiAxOS40ODAxIDgzLjM2ODcgMTkuNTk0N0M4My44ODA3IDE5LjcwNTcgODQuMjgxNyAxOS44NCA4NC41NzE4IDE5Ljk5NzZDODQuODY1NCAyMC4xNTE1IDg1LjA4MiAyMC4zMzc3IDg1LjIyMTcgMjAuNTU2MkM4NS4zNjQ5IDIwLjc3NDYgODUuNDM2NSAyMS4wMzk2IDg1LjQzNjUgMjEuMzUxMUM4NS40MzY1IDIxLjg3MzkgODUuMjE5OSAyMi4yOTgyIDg0Ljc4NjYgMjIuNjI0Qzg0LjM1MzQgMjIuOTQ2MyA4My43ODU4IDIzLjEwNzQgODMuMDg0IDIzLjEwNzRDODIuNjA3NyAyMy4xMDc0IDgyLjE4MzQgMjMuMDIxNSA4MS44MTEgMjIuODQ5NkM4MS40Mzg2IDIyLjY3NzcgODEuMTQ4NiAyMi40NDE0IDgwLjk0MDkgMjIuMTQwNkM4MC43MzMyIDIxLjgzOTggODAuNjI5NCAyMS41MTU4IDgwLjYyOTQgMjEuMTY4NUg4MS44OTdDODEuOTE0OSAyMS40NzY0IDgyLjAzMTIgMjEuNzE0NSA4Mi4yNDYxIDIxLjg4MjhDODIuNDYwOSAyMi4wNDc1IDgyLjc0NTYgMjIuMTI5OSA4My4xMDAxIDIyLjEyOTlDODMuNDQzOCAyMi4xMjk5IDgzLjcwNTIgMjIuMDY1NCA4My44ODQzIDIxLjkzNjVDODQuMDYzMyAyMS44MDQgODQuMTUyOCAyMS42MzIyIDg0LjE1MjggMjEuNDIwOVoiIGZpbGw9ImJsYWNrIiBmaWxsLW9wYWNpdHk9IjAuODciLz4KPHBhdGggZD0iTTkwLjA1MDMgMjNDODkuOTkzIDIyLjg4OSA4OS45NDI5IDIyLjcwODIgODkuODk5OSAyMi40NTc1Qzg5LjQ4NDUgMjIuODkwOCA4OC45NzYxIDIzLjEwNzQgODguMzc0NSAyMy4xMDc0Qzg3Ljc5MDkgMjMuMTA3NCA4Ny4zMTQ2IDIyLjk0MDkgODYuOTQ1OCAyMi42MDc5Qzg2LjU3NyAyMi4yNzQ5IDg2LjM5MjYgMjEuODYzMSA4Ni4zOTI2IDIxLjM3MjZDODYuMzkyNiAyMC43NTMxIDg2LjYyMTcgMjAuMjc4NiA4Ny4wODAxIDE5Ljk0OTJDODcuNTQyIDE5LjYxNjIgODguMjAwOCAxOS40NDk3IDg5LjA1NjYgMTkuNDQ5N0g4OS44NTY5VjE5LjA2ODRDODkuODU2OSAxOC43Njc2IDg5Ljc3MjggMTguNTI3NyA4OS42MDQ1IDE4LjM0ODZDODkuNDM2MiAxOC4xNjYgODkuMTgwMiAxOC4wNzQ3IDg4LjgzNjQgMTguMDc0N0M4OC41MzkyIDE4LjA3NDcgODguMjk1NyAxOC4xNDk5IDg4LjEwNiAxOC4zMDAzQzg3LjkxNjIgMTguNDQ3MSA4Ny44MjEzIDE4LjYzNTEgODcuODIxMyAxOC44NjQzSDg2LjUxNjFDODYuNTE2MSAxOC41NDU2IDg2LjYyMTcgMTguMjQ4NCA4Ni44MzMgMTcuOTcyN0M4Ny4wNDQzIDE3LjY5MzQgODcuMzMwNyAxNy40NzQ5IDg3LjY5MjQgMTcuMzE3NEM4OC4wNTc2IDE3LjE1OTggODguNDY0IDE3LjA4MTEgODguOTExNiAxNy4wODExQzg5LjU5MiAxNy4wODExIDkwLjEzNDQgMTcuMjUyOSA5MC41MzkxIDE3LjU5NjdDOTAuOTQzNyAxNy45MzY4IDkxLjE1MTQgMTguNDE2NyA5MS4xNjIxIDE5LjAzNjFWMjEuNjU3MkM5MS4xNjIxIDIyLjE4IDkxLjIzNTUgMjIuNTk3MiA5MS4zODIzIDIyLjkwODdWMjNIOTAuMDUwM1pNODguNjE2MiAyMi4wNjAxQzg4Ljg3NCAyMi4wNjAxIDg5LjExNTcgMjEuOTk3NCA4OS4zNDEzIDIxLjg3MjFDODkuNTcwNSAyMS43NDY3IDg5Ljc0MjQgMjEuNTc4NSA4OS44NTY5IDIxLjM2NzJWMjAuMjcxNUg4OS4xNTMzQzg4LjY2OTkgMjAuMjcxNSA4OC4zMDY1IDIwLjM1NTYgODguMDYzIDIwLjUyMzlDODcuODE5NSAyMC42OTIyIDg3LjY5NzggMjAuOTMwMyA4Ny42OTc4IDIxLjIzODNDODcuNjk3OCAyMS40ODg5IDg3Ljc4MDEgMjEuNjg5NSA4Ny45NDQ4IDIxLjgzOThDODguMTEzMSAyMS45ODY3IDg4LjMzNjkgMjIuMDYwMSA4OC42MTYyIDIyLjA2MDFaIiBmaWxsPSJibGFjayIgZmlsbC1vcGFjaXR5PSIwLjg3Ii8+CjxwYXRoIGQ9Ik05Mi4zMDA4IDIwLjA1MTNDOTIuMzAwOCAxOS4xNDg5IDkyLjUxMiAxOC40MjkyIDkyLjkzNDYgMTcuODkyMUM5My4zNjA3IDE3LjM1MTQgOTMuOTI0NiAxNy4wODExIDk0LjYyNjUgMTcuMDgxMUM5NS4yODg5IDE3LjA4MTEgOTUuODA5OSAxNy4zMTIgOTYuMTg5NSAxNy43NzM5TDk2LjI0ODUgMTcuMTg4NUg5Ny40MjQ4VjIyLjgyMjhDOTcuNDI0OCAyMy41ODU0IDk3LjE4NjcgMjQuMTg3IDk2LjcxMDQgMjQuNjI3NEM5Ni4yMzc4IDI1LjA2NzkgOTUuNTk4NiAyNS4yODgxIDk0Ljc5MyAyNS4yODgxQzk0LjM2NjkgMjUuMjg4MSA5My45NDk3IDI1LjE5ODYgOTMuNTQxNSAyNS4wMTk1QzkzLjEzNjkgMjQuODQ0MSA5Mi44Mjg5IDI0LjYxMzEgOTIuNjE3NyAyNC4zMjY3TDkzLjIzNTQgMjMuNTQyNUM5My42MzY0IDI0LjAxODcgOTQuMTMwNSAyNC4yNTY4IDk0LjcxNzggMjQuMjU2OEM5NS4xNTEgMjQuMjU2OCA5NS40OTMgMjQuMTM4NyA5NS43NDM3IDIzLjkwMjNDOTUuOTk0MyAyMy42Njk2IDk2LjExOTYgMjMuMzI1OCA5Ni4xMTk2IDIyLjg3MTFWMjIuNDc5Qzk1Ljc0MzcgMjIuODk3OSA5NS4yNDI0IDIzLjEwNzQgOTQuNjE1NyAyMy4xMDc0QzkzLjkzNTQgMjMuMTA3NCA5My4zNzg2IDIyLjgzNzEgOTIuOTQ1MyAyMi4yOTY0QzkyLjUxNTYgMjEuNzU1NyA5Mi4zMDA4IDIxLjAwNzMgOTIuMzAwOCAyMC4wNTEzWk05My42MDA2IDIwLjE2NDFDOTMuNjAwNiAyMC43NDc3IDkzLjcxODggMjEuMjA3OCA5My45NTUxIDIxLjU0NDRDOTQuMTk1IDIxLjg3NzQgOTQuNTI2MiAyMi4wNDM5IDk0Ljk0ODcgMjIuMDQzOUM5NS40NzUxIDIyLjA0MzkgOTUuODY1NCAyMS44MTg0IDk2LjExOTYgMjEuMzY3MlYxOC44MTA1Qzk1Ljg3MjYgMTguMzcwMSA5NS40ODU4IDE4LjE0OTkgOTQuOTU5NSAxOC4xNDk5Qzk0LjUyOTggMTguMTQ5OSA5NC4xOTUgMTguMzIgOTMuOTU1MSAxOC42NjAyQzkzLjcxODggMTkuMDAwMyA5My42MDA2IDE5LjUwMTYgOTMuNjAwNiAyMC4xNjQxWiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC44NyIvPgo8cGF0aCBkPSJNMTAxLjMzIDIzLjEwNzRDMTAwLjUwMiAyMy4xMDc0IDk5LjgzMTEgMjIuODQ3OCA5OS4zMTU0IDIyLjMyODZDOTguODAzNCAyMS44MDU4IDk4LjU0NzQgMjEuMTExMiA5OC41NDc0IDIwLjI0NDZWMjAuMDgzNUM5OC41NDc0IDE5LjUwMzQgOTguNjU4NCAxOC45ODYgOTguODgwNCAxOC41MzEyQzk5LjEwNiAxOC4wNzI5IDk5LjQyMTEgMTcuNzE2NiA5OS44MjU3IDE3LjQ2MjRDMTAwLjIzIDE3LjIwODIgMTAwLjY4MSAxNy4wODExIDEwMS4xNzkgMTcuMDgxMUMxMDEuOTcxIDE3LjA4MTEgMTAyLjU4MSAxNy4zMzM1IDEwMy4wMTEgMTcuODM4NEMxMDMuNDQ0IDE4LjM0MzMgMTAzLjY2MSAxOS4wNTc2IDEwMy42NjEgMTkuOTgxNFYyMC41MDc4SDk5Ljg2MzNDOTkuOTAyNyAyMC45ODc2IDEwMC4wNjIgMjEuMzY3MiAxMDAuMzQxIDIxLjY0NjVDMTAwLjYyNCAyMS45MjU4IDEwMC45NzkgMjIuMDY1NCAxMDEuNDA1IDIyLjA2NTRDMTAyLjAwMyAyMi4wNjU0IDEwMi40OSAyMS44MjM3IDEwMi44NjYgMjEuMzQwM0wxMDMuNTY5IDIyLjAxMTdDMTAzLjMzNyAyMi4zNTkgMTAzLjAyNSAyMi42Mjk0IDEwMi42MzUgMjIuODIyOEMxMDIuMjQ4IDIzLjAxMjUgMTAxLjgxMyAyMy4xMDc0IDEwMS4zMyAyMy4xMDc0Wk0xMDEuMTc0IDE4LjEyODRDMTAwLjgxNiAxOC4xMjg0IDEwMC41MjYgMTguMjUzNyAxMDAuMzA0IDE4LjUwNDRDMTAwLjA4NSAxOC43NTUgOTkuOTQ1NiAxOS4xMDQyIDk5Ljg4NDggMTkuNTUxOEgxMDIuMzcyVjE5LjQ1NTFDMTAyLjM0MyAxOS4wMTgyIDEwMi4yMjcgMTguNjg4OCAxMDIuMDIyIDE4LjQ2NjhDMTAxLjgxOCAxOC4yNDEyIDEwMS41MzUgMTguMTI4NCAxMDEuMTc0IDE4LjEyODRaIiBmaWxsPSJibGFjayIgZmlsbC1vcGFjaXR5PSIwLjg3Ii8+CjxwYXRoIGQ9Ik0xMDkuMDUzIDE1Ljc3NTlWMTcuMTg4NUgxMTAuMDc5VjE4LjE1NTNIMTA5LjA1M1YyMS4zOTk0QzEwOS4wNTMgMjEuNjIxNCAxMDkuMDk2IDIxLjc4MjYgMTA5LjE4MiAyMS44ODI4QzEwOS4yNzIgMjEuOTc5NSAxMDkuNDI5IDIyLjAyNzggMTA5LjY1NSAyMi4wMjc4QzEwOS44MDUgMjIuMDI3OCAxMDkuOTU3IDIyLjAwOTkgMTEwLjExMSAyMS45NzQxVjIyLjk4MzlDMTA5LjgxNCAyMy4wNjYyIDEwOS41MjggMjMuMTA3NCAxMDkuMjUyIDIzLjEwNzRDMTA4LjI0OSAyMy4xMDc0IDEwNy43NDggMjIuNTU0MiAxMDcuNzQ4IDIxLjQ0NzhWMTguMTU1M0gxMDYuNzkyVjE3LjE4ODVIMTA3Ljc0OFYxNS43NzU5SDEwOS4wNTNaIiBmaWxsPSJibGFjayIgZmlsbC1vcGFjaXR5PSIwLjg3Ii8+CjxwYXRoIGQ9Ik0xMTMuMTE0IDIxLjEzMDlMMTE0LjI5NSAxNy4xODg1SDExNS42ODdMMTEzLjM3NyAyMy44ODA5QzExMy4wMjIgMjQuODU4NCAxMTIuNDIxIDI1LjM0NzIgMTExLjU3MiAyNS4zNDcyQzExMS4zODIgMjUuMzQ3MiAxMTEuMTczIDI1LjMxNDkgMTEwLjk0NCAyNS4yNTA1VjI0LjI0MDdMMTExLjE5MSAyNC4yNTY4QzExMS41MiAyNC4yNTY4IDExMS43NjcgMjQuMTk2IDExMS45MzIgMjQuMDc0MkMxMTIuMSAyMy45NTYxIDExMi4yMzMgMjMuNzU1NSAxMTIuMzMgMjMuNDcyN0wxMTIuNTE4IDIyLjk3MzFMMTEwLjQ3NyAxNy4xODg1SDExMS44ODRMMTEzLjExNCAyMS4xMzA5WiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC44NyIvPgo8cGF0aCBkPSJNMTIxLjUzIDIwLjE1MzNDMTIxLjUzIDIxLjA1MjEgMTIxLjMyNiAyMS43NyAxMjAuOTE4IDIyLjMwNzFDMTIwLjUxIDIyLjg0MDcgMTE5Ljk2MiAyMy4xMDc0IDExOS4yNzQgMjMuMTA3NEMxMTguNjM3IDIzLjEwNzQgMTE4LjEyNyAyMi44OTc5IDExNy43NDQgMjIuNDc5VjI1LjIzNDRIMTE2LjQzOFYxNy4xODg1SDExNy42NDJMMTE3LjY5NSAxNy43NzkzQzExOC4wNzggMTcuMzEzOCAxMTguNTk5IDE3LjA4MTEgMTE5LjI1OCAxNy4wODExQzExOS45NjcgMTcuMDgxMSAxMjAuNTIyIDE3LjM0NiAxMjAuOTIzIDE3Ljg3NkMxMjEuMzI4IDE4LjQwMjMgMTIxLjUzIDE5LjEzNDYgMTIxLjUzIDIwLjA3MjhWMjAuMTUzM1pNMTIwLjIzIDIwLjA0MDVDMTIwLjIzIDE5LjQ2MDQgMTIwLjExNCAxOS4wMDAzIDExOS44ODEgMTguNjYwMkMxMTkuNjUyIDE4LjMyIDExOS4zMjMgMTguMTQ5OSAxMTguODkzIDE4LjE0OTlDMTE4LjM2IDE4LjE0OTkgMTE3Ljk3NiAxOC4zNzAxIDExNy43NDQgMTguODEwNVYyMS4zODg3QzExNy45OCAyMS44Mzk4IDExOC4zNjcgMjIuMDY1NCAxMTguOTA0IDIyLjA2NTRDMTE5LjMxOSAyMi4wNjU0IDExOS42NDMgMjEuODk4OSAxMTkuODc2IDIxLjU2NTlDMTIwLjExMiAyMS4yMjkzIDEyMC4yMyAyMC43MjA5IDEyMC4yMyAyMC4wNDA1WiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC44NyIvPgo8cGF0aCBkPSJNMTI1LjE5OSAyMy4xMDc0QzEyNC4zNzIgMjMuMTA3NCAxMjMuNyAyMi44NDc4IDEyMy4xODUgMjIuMzI4NkMxMjIuNjczIDIxLjgwNTggMTIyLjQxNyAyMS4xMTEyIDEyMi40MTcgMjAuMjQ0NlYyMC4wODM1QzEyMi40MTcgMTkuNTAzNCAxMjIuNTI4IDE4Ljk4NiAxMjIuNzUgMTguNTMxMkMxMjIuOTc1IDE4LjA3MjkgMTIzLjI5IDE3LjcxNjYgMTIzLjY5NSAxNy40NjI0QzEyNC4wOTkgMTcuMjA4MiAxMjQuNTUxIDE3LjA4MTEgMTI1LjA0OCAxNy4wODExQzEyNS44NCAxNy4wODExIDEyNi40NSAxNy4zMzM1IDEyNi44OCAxNy44Mzg0QzEyNy4zMTMgMTguMzQzMyAxMjcuNTMgMTkuMDU3NiAxMjcuNTMgMTkuOTgxNFYyMC41MDc4SDEyMy43MzJDMTIzLjc3MiAyMC45ODc2IDEyMy45MzEgMjEuMzY3MiAxMjQuMjEgMjEuNjQ2NUMxMjQuNDkzIDIxLjkyNTggMTI0Ljg0OCAyMi4wNjU0IDEyNS4yNzQgMjIuMDY1NEMxMjUuODcyIDIyLjA2NTQgMTI2LjM1OSAyMS44MjM3IDEyNi43MzUgMjEuMzQwM0wxMjcuNDM4IDIyLjAxMTdDMTI3LjIwNiAyMi4zNTkgMTI2Ljg5NCAyMi42Mjk0IDEyNi41MDQgMjIuODIyOEMxMjYuMTE3IDIzLjAxMjUgMTI1LjY4MiAyMy4xMDc0IDEyNS4xOTkgMjMuMTA3NFpNMTI1LjA0MyAxOC4xMjg0QzEyNC42ODUgMTguMTI4NCAxMjQuMzk1IDE4LjI1MzcgMTI0LjE3MyAxOC41MDQ0QzEyMy45NTQgMTguNzU1IDEyMy44MTUgMTkuMTA0MiAxMjMuNzU0IDE5LjU1MThIMTI2LjI0MVYxOS40NTUxQzEyNi4yMTIgMTkuMDE4MiAxMjYuMDk2IDE4LjY4ODggMTI1Ljg5MiAxOC40NjY4QzEyNS42ODggMTguMjQxMiAxMjUuNDA1IDE4LjEyODQgMTI1LjA0MyAxOC4xMjg0WiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC44NyIvPgo8cGF0aCBkPSJNMTQ0Ljg4MiAyMC45ODU4QzE0NC44ODIgMjAuNjQyMSAxNDQuNzYxIDIwLjM3NzEgMTQ0LjUxNyAyMC4xOTA5QzE0NC4yNzcgMjAuMDA0NyAxNDMuODQyIDE5LjgxNjcgMTQzLjIxMiAxOS42MjdDMTQyLjU4MiAxOS40MzcyIDE0Mi4wOCAxOS4yMjU5IDE0MS43MDggMTguOTkzMkMxNDAuOTk1IDE4LjU0NTYgMTQwLjYzOSAxNy45NjE5IDE0MC42MzkgMTcuMjQyMkMxNDAuNjM5IDE2LjYxMiAxNDAuODk1IDE2LjA5MjggMTQxLjQwNyAxNS42ODQ2QzE0MS45MjMgMTUuMjc2NCAxNDIuNTkxIDE1LjA3MjMgMTQzLjQxMSAxNS4wNzIzQzE0My45NTUgMTUuMDcyMyAxNDQuNDQgMTUuMTcyNSAxNDQuODY2IDE1LjM3M0MxNDUuMjkyIDE1LjU3MzYgMTQ1LjYyNyAxNS44NiAxNDUuODcxIDE2LjIzMjRDMTQ2LjExNCAxNi42MDEyIDE0Ni4yMzYgMTcuMDExMiAxNDYuMjM2IDE3LjQ2MjRIMTQ0Ljg4MkMxNDQuODgyIDE3LjA1NDIgMTQ0Ljc1MyAxNi43MzU1IDE0NC40OTYgMTYuNTA2M0MxNDQuMjQxIDE2LjI3MzYgMTQzLjg3NiAxNi4xNTcyIDE0My40IDE2LjE1NzJDMTQyLjk1NiAxNi4xNTcyIDE0Mi42MSAxNi4yNTIxIDE0Mi4zNjMgMTYuNDQxOUMxNDIuMTIgMTYuNjMxNyAxNDEuOTk4IDE2Ljg5NjYgMTQxLjk5OCAxNy4yMzY4QzE0MS45OTggMTcuNTIzMyAxNDIuMTMxIDE3Ljc2MzIgMTQyLjM5NiAxNy45NTY1QzE0Mi42NiAxOC4xNDYzIDE0My4wOTcgMTguMzMyNSAxNDMuNzA2IDE4LjUxNTFDMTQ0LjMxNSAxOC42OTQyIDE0NC44MDQgMTguOTAwMSAxNDUuMTcyIDE5LjEzMjhDMTQ1LjU0MSAxOS4zNjIgMTQ1LjgxMiAxOS42MjcgMTQ1Ljk4MyAxOS45Mjc3QzE0Ni4xNTUgMjAuMjI0OSAxNDYuMjQxIDIwLjU3NDEgMTQ2LjI0MSAyMC45NzUxQzE0Ni4yNDEgMjEuNjI2OCAxNDUuOTkxIDIyLjE0NiAxNDUuNDg5IDIyLjUzMjdDMTQ0Ljk5MiAyMi45MTU5IDE0NC4zMTUgMjMuMTA3NCAxNDMuNDU5IDIzLjEwNzRDMTQyLjg5MyAyMy4xMDc0IDE0Mi4zNzIgMjMuMDAzNiAxNDEuODk2IDIyLjc5NTlDMTQxLjQyMyAyMi41ODQ2IDE0MS4wNTUgMjIuMjk0NiAxNDAuNzkgMjEuOTI1OEMxNDAuNTI4IDIxLjU1NyAxNDAuMzk3IDIxLjEyNzMgMTQwLjM5NyAyMC42MzY3SDE0MS43NTZDMTQxLjc1NiAyMS4wODA3IDE0MS45MDMgMjEuNDI0NSAxNDIuMTk3IDIxLjY2OEMxNDIuNDkgMjEuOTExNSAxNDIuOTExIDIyLjAzMzIgMTQzLjQ1OSAyMi4wMzMyQzE0My45MzIgMjIuMDMzMiAxNDQuMjg2IDIxLjkzODMgMTQ0LjUyMiAyMS43NDg1QzE0NC43NjIgMjEuNTU1MiAxNDQuODgyIDIxLjMwMDkgMTQ0Ljg4MiAyMC45ODU4WiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC44NyIvPgo8cGF0aCBkPSJNMTQ4Ljk0MyAxNS43NzU5VjE3LjE4ODVIMTQ5Ljk2OVYxOC4xNTUzSDE0OC45NDNWMjEuMzk5NEMxNDguOTQzIDIxLjYyMTQgMTQ4Ljk4NiAyMS43ODI2IDE0OS4wNzIgMjEuODgyOEMxNDkuMTYxIDIxLjk3OTUgMTQ5LjMxOSAyMi4wMjc4IDE0OS41NDQgMjIuMDI3OEMxNDkuNjk1IDIyLjAyNzggMTQ5Ljg0NyAyMi4wMDk5IDE1MC4wMDEgMjEuOTc0MVYyMi45ODM5QzE0OS43MDQgMjMuMDY2MiAxNDkuNDE3IDIzLjEwNzQgMTQ5LjE0MiAyMy4xMDc0QzE0OC4xMzkgMjMuMTA3NCAxNDcuNjM4IDIyLjU1NDIgMTQ3LjYzOCAyMS40NDc4VjE4LjE1NTNIMTQ2LjY4MlYxNy4xODg1SDE0Ny42MzhWMTUuNzc1OUgxNDguOTQzWiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC44NyIvPgo8cGF0aCBkPSJNMTU0LjQ0MyAyM0MxNTQuMzg2IDIyLjg4OSAxNTQuMzM1IDIyLjcwODIgMTU0LjI5MiAyMi40NTc1QzE1My44NzcgMjIuODkwOCAxNTMuMzY5IDIzLjEwNzQgMTUyLjc2NyAyMy4xMDc0QzE1Mi4xODMgMjMuMTA3NCAxNTEuNzA3IDIyLjk0MDkgMTUxLjMzOCAyMi42MDc5QzE1MC45NyAyMi4yNzQ5IDE1MC43ODUgMjEuODYzMSAxNTAuNzg1IDIxLjM3MjZDMTUwLjc4NSAyMC43NTMxIDE1MS4wMTQgMjAuMjc4NiAxNTEuNDczIDE5Ljk0OTJDMTUxLjkzNSAxOS42MTYyIDE1Mi41OTMgMTkuNDQ5NyAxNTMuNDQ5IDE5LjQ0OTdIMTU0LjI1VjE5LjA2ODRDMTU0LjI1IDE4Ljc2NzYgMTU0LjE2NSAxOC41Mjc3IDE1My45OTcgMTguMzQ4NkMxNTMuODI5IDE4LjE2NiAxNTMuNTczIDE4LjA3NDcgMTUzLjIyOSAxOC4wNzQ3QzE1Mi45MzIgMTguMDc0NyAxNTIuNjg4IDE4LjE0OTkgMTUyLjQ5OSAxOC4zMDAzQzE1Mi4zMDkgMTguNDQ3MSAxNTIuMjE0IDE4LjYzNTEgMTUyLjIxNCAxOC44NjQzSDE1MC45MDlDMTUwLjkwOSAxOC41NDU2IDE1MS4wMTQgMTguMjQ4NCAxNTEuMjI2IDE3Ljk3MjdDMTUxLjQzNyAxNy42OTM0IDE1MS43MjMgMTcuNDc0OSAxNTIuMDg1IDE3LjMxNzRDMTUyLjQ1IDE3LjE1OTggMTUyLjg1NyAxNy4wODExIDE1My4zMDQgMTcuMDgxMUMxNTMuOTg1IDE3LjA4MTEgMTU0LjUyNyAxNy4yNTI5IDE1NC45MzIgMTcuNTk2N0MxNTUuMzM2IDE3LjkzNjggMTU1LjU0NCAxOC40MTY3IDE1NS41NTUgMTkuMDM2MVYyMS42NTcyQzE1NS41NTUgMjIuMTggMTU1LjYyOCAyMi41OTcyIDE1NS43NzUgMjIuOTA4N1YyM0gxNTQuNDQzWk0xNTMuMDA5IDIyLjA2MDFDMTUzLjI2NyAyMi4wNjAxIDE1My41MDggMjEuOTk3NCAxNTMuNzM0IDIxLjg3MjFDMTUzLjk2MyAyMS43NDY3IDE1NC4xMzUgMjEuNTc4NSAxNTQuMjUgMjEuMzY3MlYyMC4yNzE1SDE1My41NDZDMTUzLjA2MiAyMC4yNzE1IDE1Mi42OTkgMjAuMzU1NiAxNTIuNDU2IDIwLjUyMzlDMTUyLjIxMiAyMC42OTIyIDE1Mi4wOSAyMC45MzAzIDE1Mi4wOSAyMS4yMzgzQzE1Mi4wOSAyMS40ODg5IDE1Mi4xNzMgMjEuNjg5NSAxNTIuMzM3IDIxLjgzOThDMTUyLjUwNiAyMS45ODY3IDE1Mi43MjkgMjIuMDYwMSAxNTMuMDA5IDIyLjA2MDFaIiBmaWxsPSJibGFjayIgZmlsbC1vcGFjaXR5PSIwLjg3Ii8+CjxwYXRoIGQ9Ik0xNTguNTU3IDE1Ljc3NTlWMTcuMTg4NUgxNTkuNTgzVjE4LjE1NTNIMTU4LjU1N1YyMS4zOTk0QzE1OC41NTcgMjEuNjIxNCAxNTguNiAyMS43ODI2IDE1OC42ODYgMjEuODgyOEMxNTguNzc2IDIxLjk3OTUgMTU4LjkzMyAyMi4wMjc4IDE1OS4xNTkgMjIuMDI3OEMxNTkuMzA5IDIyLjAyNzggMTU5LjQ2MSAyMi4wMDk5IDE1OS42MTUgMjEuOTc0MVYyMi45ODM5QzE1OS4zMTggMjMuMDY2MiAxNTkuMDMyIDIzLjEwNzQgMTU4Ljc1NiAyMy4xMDc0QzE1Ny43NTMgMjMuMTA3NCAxNTcuMjUyIDIyLjU1NDIgMTU3LjI1MiAyMS40NDc4VjE4LjE1NTNIMTU2LjI5NlYxNy4xODg1SDE1Ny4yNTJWMTUuNzc1OUgxNTguNTU3WiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC44NyIvPgo8cGF0aCBkPSJNMTY0LjEwNSAyMi40MzA3QzE2My43MjIgMjIuODgxOCAxNjMuMTc4IDIzLjEwNzQgMTYyLjQ3MyAyMy4xMDc0QzE2MS44NDIgMjMuMTA3NCAxNjEuMzY0IDIyLjkyMyAxNjEuMDM5IDIyLjU1NDJDMTYwLjcxNiAyMi4xODU0IDE2MC41NTUgMjEuNjUxOSAxNjAuNTU1IDIwLjk1MzZWMTcuMTg4NUgxNjEuODZWMjAuOTM3NUMxNjEuODYgMjEuNjc1MSAxNjIuMTY3IDIyLjA0MzkgMTYyLjc3OSAyMi4wNDM5QzE2My40MTMgMjIuMDQzOSAxNjMuODQgMjEuODE2NiAxNjQuMDYyIDIxLjM2MThWMTcuMTg4NUgxNjUuMzY4VjIzSDE2NC4xMzhMMTY0LjEwNSAyMi40MzA3WiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC44NyIvPgo8cGF0aCBkPSJNMTY5Ljk1NSAyMS40MjA5QzE2OS45NTUgMjEuMTg4MiAxNjkuODU4IDIxLjAxMDkgMTY5LjY2NSAyMC44ODkyQzE2OS40NzUgMjAuNzY3NCAxNjkuMTU4IDIwLjY2IDE2OC43MTQgMjAuNTY2OUMxNjguMjcgMjAuNDczOCAxNjcuODk5IDIwLjM1NTYgMTY3LjYwMiAyMC4yMTI0QzE2Ni45NSAxOS44OTczIDE2Ni42MjUgMTkuNDQwOCAxNjYuNjI1IDE4Ljg0MjhDMTY2LjYyNSAxOC4zNDE1IDE2Ni44MzYgMTcuOTIyNSAxNjcuMjU4IDE3LjU4NTlDMTY3LjY4MSAxNy4yNDkzIDE2OC4yMTggMTcuMDgxMSAxNjguODcgMTcuMDgxMUMxNjkuNTY0IDE3LjA4MTEgMTcwLjEyNSAxNy4yNTI5IDE3MC41NTEgMTcuNTk2N0MxNzAuOTggMTcuOTQwNCAxNzEuMTk1IDE4LjM4NjIgMTcxLjE5NSAxOC45MzQxSDE2OS44OUMxNjkuODkgMTguNjgzNCAxNjkuNzk3IDE4LjQ3NTcgMTY5LjYxMSAxOC4zMTFDMTY5LjQyNSAxOC4xNDI3IDE2OS4xNzggMTguMDU4NiAxNjguODcgMTguMDU4NkMxNjguNTgzIDE4LjA1ODYgMTY4LjM0OSAxOC4xMjQ4IDE2OC4xNjYgMTguMjU3M0MxNjcuOTg3IDE4LjM4OTggMTY3Ljg5NyAxOC41NjcxIDE2Ny44OTcgMTguNzg5MUMxNjcuODk3IDE4Ljk4OTYgMTY3Ljk4MiAxOS4xNDUzIDE2OC4xNSAxOS4yNTYzQzE2OC4zMTggMTkuMzY3NCAxNjguNjU4IDE5LjQ4MDEgMTY5LjE3IDE5LjU5NDdDMTY5LjY4MiAxOS43MDU3IDE3MC4wODMgMTkuODQgMTcwLjM3NCAxOS45OTc2QzE3MC42NjcgMjAuMTUxNSAxNzAuODg0IDIwLjMzNzcgMTcxLjAyMyAyMC41NTYyQzE3MS4xNjcgMjAuNzc0NiAxNzEuMjM4IDIxLjAzOTYgMTcxLjIzOCAyMS4zNTExQzE3MS4yMzggMjEuODczOSAxNzEuMDIyIDIyLjI5ODIgMTcwLjU4OCAyMi42MjRDMTcwLjE1NSAyMi45NDYzIDE2OS41ODggMjMuMTA3NCAxNjguODg2IDIzLjEwNzRDMTY4LjQxIDIzLjEwNzQgMTY3Ljk4NSAyMy4wMjE1IDE2Ny42MTMgMjIuODQ5NkMxNjcuMjQgMjIuNjc3NyAxNjYuOTUgMjIuNDQxNCAxNjYuNzQzIDIyLjE0MDZDMTY2LjUzNSAyMS44Mzk4IDE2Ni40MzEgMjEuNTE1OCAxNjYuNDMxIDIxLjE2ODVIMTY3LjY5OUMxNjcuNzE3IDIxLjQ3NjQgMTY3LjgzMyAyMS43MTQ1IDE2OC4wNDggMjEuODgyOEMxNjguMjYzIDIyLjA0NzUgMTY4LjU0NyAyMi4xMjk5IDE2OC45MDIgMjIuMTI5OUMxNjkuMjQ2IDIyLjEyOTkgMTY5LjUwNyAyMi4wNjU0IDE2OS42ODYgMjEuOTM2NUMxNjkuODY1IDIxLjgwNCAxNjkuOTU1IDIxLjYzMjIgMTY5Ljk1NSAyMS40MjA5WiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC44NyIvPgo8cGF0aCBkPSJNMTIuNzczNCA2My40NzlDMTIuNzczNCA2My4zMDcxIDEyLjgyMzYgNjMuMTYzOSAxMi45MjM4IDYzLjA0OTNDMTMuMDI3NyA2Mi45MzQ3IDEzLjE4MTYgNjIuODc3NCAxMy4zODU3IDYyLjg3NzRDMTMuNTg5OCA2Mi44Nzc0IDEzLjc0MzggNjIuOTM0NyAxMy44NDc3IDYzLjA0OTNDMTMuOTU1MSA2My4xNjM5IDE0LjAwODggNjMuMzA3MSAxNC4wMDg4IDYzLjQ3OUMxNC4wMDg4IDYzLjY0MzcgMTMuOTU1MSA2My43ODE2IDEzLjg0NzcgNjMuODkyNkMxMy43NDM4IDY0LjAwMzYgMTMuNTg5OCA2NC4wNTkxIDEzLjM4NTcgNjQuMDU5MUMxMy4xODE2IDY0LjA1OTEgMTMuMDI3NyA2NC4wMDM2IDEyLjkyMzggNjMuODkyNkMxMi44MjM2IDYzLjc4MTYgMTIuNzczNCA2My42NDM3IDEyLjc3MzQgNjMuNDc5WiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC44NyIvPgo8cGF0aCBkPSJNMTUuNjczOCA2My40NzlDMTUuNjczOCA2My4zMDcxIDE1LjcyNCA2My4xNjM5IDE1LjgyNDIgNjMuMDQ5M0MxNS45MjgxIDYyLjkzNDcgMTYuMDgyIDYyLjg3NzQgMTYuMjg2MSA2Mi44Nzc0QzE2LjQ5MDIgNjIuODc3NCAxNi42NDQyIDYyLjkzNDcgMTYuNzQ4IDYzLjA0OTNDMTYuODU1NSA2My4xNjM5IDE2LjkwOTIgNjMuMzA3MSAxNi45MDkyIDYzLjQ3OUMxNi45MDkyIDYzLjY0MzcgMTYuODU1NSA2My43ODE2IDE2Ljc0OCA2My44OTI2QzE2LjY0NDIgNjQuMDAzNiAxNi40OTAyIDY0LjA1OTEgMTYuMjg2MSA2NC4wNTkxQzE2LjA4MiA2NC4wNTkxIDE1LjkyODEgNjQuMDAzNiAxNS44MjQyIDYzLjg5MjZDMTUuNzI0IDYzLjc4MTYgMTUuNjczOCA2My42NDM3IDE1LjY3MzggNjMuNDc5WiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC44NyIvPgo8cGF0aCBkPSJNMjAuODgzOCA2My4yOTY0QzIxLjIzODMgNjMuMjk2NCAyMS41NDggNjMuMTg5IDIxLjgxMyA2Mi45NzQxQzIyLjA3OCA2Mi43NTkzIDIyLjIyNDggNjIuNDkwNyAyMi4yNTM0IDYyLjE2ODVIMjMuMTkzNEMyMy4xNzU1IDYyLjUwMTUgMjMuMDYwOSA2Mi44MTg0IDIyLjg0OTYgNjMuMTE5MUMyMi42MzgzIDYzLjQxOTkgMjIuMzU1NSA2My42NTk4IDIyLjAwMSA2My44Mzg5QzIxLjY1MDEgNjQuMDE3OSAyMS4yNzc3IDY0LjEwNzQgMjAuODgzOCA2NC4xMDc0QzIwLjA5MjQgNjQuMTA3NCAxOS40NjIyIDYzLjg0NDIgMTguOTkzMiA2My4zMTc5QzE4LjUyNzcgNjIuNzg3OSAxOC4yOTQ5IDYyLjA2NDYgMTguMjk0OSA2MS4xNDc5VjYwLjk4MTRDMTguMjk0OSA2MC40MTU3IDE4LjM5ODggNTkuOTEyNiAxOC42MDY0IDU5LjQ3MjJDMTguODE0MSA1OS4wMzE3IDE5LjExMTMgNTguNjg5OCAxOS40OTggNTguNDQ2M0MxOS44ODgzIDU4LjIwMjggMjAuMzQ4NSA1OC4wODExIDIwLjg3ODQgNTguMDgxMUMyMS41MzAxIDU4LjA4MTEgMjIuMDcwOCA1OC4yNzYyIDIyLjUwMDUgNTguNjY2NUMyMi45MzM4IDU5LjA1NjggMjMuMTY0NyA1OS41NjM1IDIzLjE5MzQgNjAuMTg2NUgyMi4yNTM0QzIyLjIyNDggNTkuODEwNSAyMi4wODE1IDU5LjUwMjYgMjEuODIzNyA1OS4yNjI3QzIxLjU2OTUgNTkuMDE5MiAyMS4yNTQ0IDU4Ljg5NzUgMjAuODc4NCA1OC44OTc1QzIwLjM3MzUgNTguODk3NSAxOS45ODE0IDU5LjA4MDEgMTkuNzAyMSA1OS40NDUzQzE5LjQyNjQgNTkuODA3IDE5LjI4ODYgNjAuMzMxNSAxOS4yODg2IDYxLjAxOVY2MS4yMDdDMTkuMjg4NiA2MS44NzY2IDE5LjQyNjQgNjIuMzkyMyAxOS43MDIxIDYyLjc1MzlDMTkuOTc3OSA2My4xMTU2IDIwLjM3MTcgNjMuMjk2NCAyMC44ODM4IDYzLjI5NjRaIiBmaWxsPSJibGFjayIgZmlsbC1vcGFjaXR5PSIwLjg3Ii8+CjxwYXRoIGQ9Ik0yNC4wNDc0IDYxLjA0MDVDMjQuMDQ3NCA2MC40NzEyIDI0LjE1ODQgNTkuOTU5MSAyNC4zODA0IDU5LjUwNDRDMjQuNjA2IDU5LjA0OTYgMjQuOTE3NSA1OC42OTg3IDI1LjMxNDkgNTguNDUxN0MyNS43MTYgNTguMjA0NiAyNi4xNzI1IDU4LjA4MTEgMjYuNjg0NiA1OC4wODExQzI3LjQ3NTkgNTguMDgxMSAyOC4xMTUxIDU4LjM1NSAyOC42MDIxIDU4LjkwMjhDMjkuMDkyNiA1OS40NTA3IDI5LjMzNzkgNjAuMTc5NCAyOS4zMzc5IDYxLjA4ODlWNjEuMTU4N0MyOS4zMzc5IDYxLjcyNDQgMjkuMjI4NyA2Mi4yMzI5IDI5LjAxMDMgNjIuNjg0MUMyOC43OTU0IDYzLjEzMTcgMjguNDg1NyA2My40ODA4IDI4LjA4MTEgNjMuNzMxNEMyNy42OCA2My45ODIxIDI3LjIxODEgNjQuMTA3NCAyNi42OTUzIDY0LjEwNzRDMjUuOTA3NiA2NC4xMDc0IDI1LjI2ODQgNjMuODMzNSAyNC43Nzc4IDYzLjI4NTZDMjQuMjkwOSA2Mi43Mzc4IDI0LjA0NzQgNjIuMDEyNyAyNC4wNDc0IDYxLjExMDRWNjEuMDQwNVpNMjUuMDQ2NCA2MS4xNTg3QzI1LjA0NjQgNjEuODAzMiAyNS4xOTUgNjIuMzIwNiAyNS40OTIyIDYyLjcxMDlDMjUuNzkzIDYzLjEwMTIgMjYuMTk0IDYzLjI5NjQgMjYuNjk1MyA2My4yOTY0QzI3LjIwMDIgNjMuMjk2NCAyNy42MDEyIDYzLjA5OTQgMjcuODk4NCA2Mi43MDU2QzI4LjE5NTYgNjIuMzA4MSAyOC4zNDQyIDYxLjc1MzEgMjguMzQ0MiA2MS4wNDA1QzI4LjM0NDIgNjAuNDAzMiAyOC4xOTIxIDU5Ljg4NzUgMjcuODg3NyA1OS40OTM3QzI3LjU4NjkgNTkuMDk2MiAyNy4xODU5IDU4Ljg5NzUgMjYuNjg0NiA1OC44OTc1QzI2LjE5NCA1OC44OTc1IDI1Ljc5ODMgNTkuMDkyNiAyNS40OTc2IDU5LjQ4MjlDMjUuMTk2OCA1OS44NzMyIDI1LjA0NjQgNjAuNDMxOCAyNS4wNDY0IDYxLjE1ODdaIiBmaWxsPSJibGFjayIgZmlsbC1vcGFjaXR5PSIwLjg3Ii8+CjxwYXRoIGQ9Ik0zNC42NzY4IDYxLjM3MzVIMzUuNzYxN1Y2Mi4xODQ2SDM0LjY3NjhWNjRIMzMuNjc3N1Y2Mi4xODQ2SDMwLjExNjdWNjEuNTk5MUwzMy42MTg3IDU2LjE3OTdIMzQuNjc2OFY2MS4zNzM1Wk0zMS4yNDQ2IDYxLjM3MzVIMzMuNjc3N1Y1Ny41Mzg2TDMzLjU1OTYgNTcuNzUzNEwzMS4yNDQ2IDYxLjM3MzVaIiBmaWxsPSJibGFjayIgZmlsbC1vcGFjaXR5PSIwLjg3Ii8+CjxwYXRoIGQ9Ik0zNy43ODY2IDU4LjE4ODVWNjQuNjcxNEMzNy43ODY2IDY1Ljc4ODYgMzcuMjc5OSA2Ni4zNDcyIDM2LjI2NjYgNjYuMzQ3MkMzNi4wNDgyIDY2LjM0NzIgMzUuODQ1OSA2Ni4zMTQ5IDM1LjY1OTcgNjYuMjUwNVY2NS40NTU2QzM1Ljc3NDMgNjUuNDg0MiAzNS45MjQ2IDY1LjQ5ODUgMzYuMTEwOCA2NS40OTg1QzM2LjMzMjggNjUuNDk4NSAzNi41MDExIDY1LjQzNzcgMzYuNjE1NyA2NS4zMTU5QzM2LjczMzkgNjUuMTk3OCAzNi43OTMgNjQuOTkwMSAzNi43OTMgNjQuNjkyOVY1OC4xODg1SDM3Ljc4NjZaTTM2LjY5MDkgNTYuNjQ3QzM2LjY5MDkgNTYuNDg5NCAzNi43MzkzIDU2LjM1NTEgMzYuODM1OSA1Ni4yNDQxQzM2LjkzNjIgNTYuMTI5NiAzNy4wODEyIDU2LjA3MjMgMzcuMjcxIDU2LjA3MjNDMzcuNDY0NCA1Ni4wNzIzIDM3LjYxMTIgNTYuMTI3OCAzNy43MTE0IDU2LjIzODhDMzcuODExNyA1Ni4zNDk4IDM3Ljg2MTggNTYuNDg1OCAzNy44NjE4IDU2LjY0N0MzNy44NjE4IDU2LjgwODEgMzcuODExNyA1Ni45NDI0IDM3LjcxMTQgNTcuMDQ5OEMzNy42MTEyIDU3LjE1NzIgMzcuNDY0NCA1Ny4yMTA5IDM3LjI3MSA1Ny4yMTA5QzM3LjA3NzYgNTcuMjEwOSAzNi45MzI2IDU3LjE1NzIgMzYuODM1OSA1Ny4wNDk4QzM2LjczOTMgNTYuOTQyNCAzNi42OTA5IDU2LjgwODEgMzYuNjkwOSA1Ni42NDdaIiBmaWxsPSJibGFjayIgZmlsbC1vcGFjaXR5PSIwLjg3Ii8+CjxwYXRoIGQ9Ik00Mi45ODA1IDYzLjQyNTNDNDIuNTkzOCA2My44OCA0Mi4wMjYyIDY0LjEwNzQgNDEuMjc3OCA2NC4xMDc0QzQwLjY1ODQgNjQuMTA3NCA0MC4xODU3IDYzLjkyODQgMzkuODU5OSA2My41NzAzQzM5LjUzNzYgNjMuMjA4NyAzOS4zNzQ3IDYyLjY3NTEgMzkuMzcxMSA2MS45Njk3VjU4LjE4ODVINDAuMzY0N1Y2MS45NDI5QzQwLjM2NDcgNjIuODIzNyA0MC43MjI4IDYzLjI2NDIgNDEuNDM5IDYzLjI2NDJDNDIuMTk4MSA2My4yNjQyIDQyLjcwMyA2Mi45ODEzIDQyLjk1MzYgNjIuNDE1NVY1OC4xODg1SDQzLjk0NzNWNjRINDMuMDAyTDQyLjk4MDUgNjMuNDI1M1oiIGZpbGw9ImJsYWNrIiBmaWxsLW9wYWNpdHk9IjAuODciLz4KPHBhdGggZD0iTTY1LjMxMSA1Ny4wMjgzSDYyLjc5NzRWNjRINjEuNzcxNVY1Ny4wMjgzSDU5LjI2MzJWNTYuMTc5N0g2NS4zMTFWNTcuMDI4M1oiIGZpbGw9ImJsYWNrIiBmaWxsLW9wYWNpdHk9IjAuODciLz4KPHBhdGggZD0iTTcxLjE0OTQgNjIuNjMwNEw3Mi4yNjY2IDU4LjE4ODVINzMuMjYwM0w3MS41Njg0IDY0SDcwLjc2MjdMNjkuMzUwMSA1OS41OTU3TDY3Ljk3NTEgNjRINjcuMTY5NEw2NS40ODI5IDU4LjE4ODVINjYuNDcxMkw2Ny42MTUyIDYyLjUzOTFMNjguOTY4OCA1OC4xODg1SDY5Ljc2OUw3MS4xNDk0IDYyLjYzMDRaIiBmaWxsPSJibGFjayIgZmlsbC1vcGFjaXR5PSIwLjg3Ii8+CjxwYXRoIGQ9Ik03NC4wMTIyIDYxLjA0MDVDNzQuMDEyMiA2MC40NzEyIDc0LjEyMzIgNTkuOTU5MSA3NC4zNDUyIDU5LjUwNDRDNzQuNTcwOCA1OS4wNDk2IDc0Ljg4MjMgNTguNjk4NyA3NS4yNzk4IDU4LjQ1MTdDNzUuNjgwOCA1OC4yMDQ2IDc2LjEzNzQgNTguMDgxMSA3Ni42NDk0IDU4LjA4MTFDNzcuNDQwOCA1OC4wODExIDc4LjA3OTkgNTguMzU1IDc4LjU2NjkgNTguOTAyOEM3OS4wNTc1IDU5LjQ1MDcgNzkuMzAyNyA2MC4xNzk0IDc5LjMwMjcgNjEuMDg4OVY2MS4xNTg3Qzc5LjMwMjcgNjEuNzI0NCA3OS4xOTM1IDYyLjIzMjkgNzguOTc1MSA2Mi42ODQxQzc4Ljc2MDMgNjMuMTMxNyA3OC40NTA1IDYzLjQ4MDggNzguMDQ1OSA2My43MzE0Qzc3LjY0NDkgNjMuOTgyMSA3Ny4xODI5IDY0LjEwNzQgNzYuNjYwMiA2NC4xMDc0Qzc1Ljg3MjQgNjQuMTA3NCA3NS4yMzMyIDYzLjgzMzUgNzQuNzQyNyA2My4yODU2Qzc0LjI1NTcgNjIuNzM3OCA3NC4wMTIyIDYyLjAxMjcgNzQuMDEyMiA2MS4xMTA0VjYxLjA0MDVaTTc1LjAxMTIgNjEuMTU4N0M3NS4wMTEyIDYxLjgwMzIgNzUuMTU5OCA2Mi4zMjA2IDc1LjQ1NyA2Mi43MTA5Qzc1Ljc1NzggNjMuMTAxMiA3Ni4xNTg5IDYzLjI5NjQgNzYuNjYwMiA2My4yOTY0Qzc3LjE2NSA2My4yOTY0IDc3LjU2NjEgNjMuMDk5NCA3Ny44NjMzIDYyLjcwNTZDNzguMTYwNSA2Mi4zMDgxIDc4LjMwOTEgNjEuNzUzMSA3OC4zMDkxIDYxLjA0MDVDNzguMzA5MSA2MC40MDMyIDc4LjE1NjkgNTkuODg3NSA3Ny44NTI1IDU5LjQ5MzdDNzcuNTUxOCA1OS4wOTYyIDc3LjE1MDcgNTguODk3NSA3Ni42NDk0IDU4Ljg5NzVDNzYuMTU4OSA1OC44OTc1IDc1Ljc2MzIgNTkuMDkyNiA3NS40NjI0IDU5LjQ4MjlDNzUuMTYxNiA1OS44NzMyIDc1LjAxMTIgNjAuNDMxOCA3NS4wMTEyIDYxLjE1ODdaIiBmaWxsPSJibGFjayIgZmlsbC1vcGFjaXR5PSIwLjg3Ii8+CjxwYXRoIGQ9Ik04Mi42MTY3IDYxLjA4MzVINzkuOTk1NlY2MC4yNzI1SDgyLjYxNjdWNjEuMDgzNVoiIGZpbGw9ImJsYWNrIiBmaWxsLW9wYWNpdHk9IjAuODciLz4KPHBhdGggZD0iTTg4LjczNDQgNjIuNjMwNEw4OS44NTE2IDU4LjE4ODVIOTAuODQ1Mkw4OS4xNTMzIDY0SDg4LjM0NzdMODYuOTM1MSA1OS41OTU3TDg1LjU2MDEgNjRIODQuNzU0NEw4My4wNjc5IDU4LjE4ODVIODQuMDU2Mkw4NS4yMDAyIDYyLjUzOTFMODYuNTUzNyA1OC4xODg1SDg3LjM1NEw4OC43MzQ0IDYyLjYzMDRaIiBmaWxsPSJibGFjayIgZmlsbC1vcGFjaXR5PSIwLjg3Ii8+CjxwYXRoIGQ9Ik05NS40NDgyIDY0Qzk1LjM5MSA2My44ODU0IDk1LjM0NDQgNjMuNjgxMyA5NS4zMDg2IDYzLjM4NzdDOTQuODQ2NyA2My44Njc1IDk0LjI5NTIgNjQuMTA3NCA5My42NTQzIDY0LjEwNzRDOTMuMDgxNCA2NC4xMDc0IDkyLjYxMDUgNjMuOTQ2MyA5Mi4yNDE3IDYzLjYyNEM5MS44NzY1IDYzLjI5ODIgOTEuNjkzOCA2Mi44ODY0IDkxLjY5MzggNjIuMzg4N0M5MS42OTM4IDYxLjc4MzUgOTEuOTIzIDYxLjMxNDUgOTIuMzgxMyA2MC45ODE0QzkyLjg0MzMgNjAuNjQ0OSA5My40OTE0IDYwLjQ3NjYgOTQuMzI1NyA2MC40NzY2SDk1LjI5MjVWNjAuMDJDOTUuMjkyNSA1OS42NzI3IDk1LjE4ODYgNTkuMzk3IDk0Ljk4MSA1OS4xOTI5Qzk0Ljc3MzMgNTguOTg1MiA5NC40NjcxIDU4Ljg4MTMgOTQuMDYyNSA1OC44ODEzQzkzLjcwOCA1OC44ODEzIDkzLjQxMDggNTguOTcwOSA5My4xNzA5IDU5LjE0OTlDOTIuOTMxIDU5LjMyODkgOTIuODExIDU5LjU0NTYgOTIuODExIDU5Ljc5OThIOTEuODEyQzkxLjgxMiA1OS41MDk4IDkxLjkxNDEgNTkuMjMwNSA5Mi4xMTgyIDU4Ljk2MTlDOTIuMzI1OCA1OC42ODk4IDkyLjYwNTEgNTguNDc0OSA5Mi45NTYxIDU4LjMxNzRDOTMuMzEwNSA1OC4xNTk4IDkzLjY5OTEgNTguMDgxMSA5NC4xMjE2IDU4LjA4MTFDOTQuNzkxMiA1OC4wODExIDk1LjMxNTggNTguMjQ5MyA5NS42OTUzIDU4LjU4NTlDOTYuMDc0OSA1OC45MTg5IDk2LjI3MTggNTkuMzc5MSA5Ni4yODYxIDU5Ljk2NjNWNjIuNjQxMUM5Ni4yODYxIDYzLjE3NDYgOTYuMzU0MiA2My41OTkgOTYuNDkwMiA2My45MTQxVjY0SDk1LjQ0ODJaTTkzLjc5OTMgNjMuMjQyN0M5NC4xMTA4IDYzLjI0MjcgOTQuNDA2MiA2My4xNjIxIDk0LjY4NTUgNjMuMDAxQzk0Ljk2NDggNjIuODM5OCA5NS4xNjcyIDYyLjYzMDQgOTUuMjkyNSA2Mi4zNzI2VjYxLjE4MDJIOTQuNTEzN0M5My4yOTYyIDYxLjE4MDIgOTIuNjg3NSA2MS41MzY1IDkyLjY4NzUgNjIuMjQ5QzkyLjY4NzUgNjIuNTYwNSA5Mi43OTEzIDYyLjgwNCA5Mi45OTkgNjIuOTc5NUM5My4yMDY3IDYzLjE1NDkgOTMuNDczNSA2My4yNDI3IDkzLjc5OTMgNjMuMjQyN1oiIGZpbGw9ImJsYWNrIiBmaWxsLW9wYWNpdHk9IjAuODciLz4KPHBhdGggZD0iTTk5LjY1OTIgNjIuNTQ0NEwxMDEuMDEzIDU4LjE4ODVIMTAyLjA3Nkw5OS43Mzk3IDY0Ljg5N0M5OS4zNzgxIDY1Ljg2MzggOTguODAzNCA2Ni4zNDcyIDk4LjAxNTYgNjYuMzQ3Mkw5Ny44Mjc2IDY2LjMzMTFMOTcuNDU3IDY2LjI2MTJWNjUuNDU1Nkw5Ny43MjU2IDY1LjQ3NzFDOTguMDYyMiA2NS40NzcxIDk4LjMyMzYgNjUuNDA5IDk4LjUwOTggNjUuMjcyOUM5OC42OTk1IDY1LjEzNjkgOTguODU1MyA2NC44ODggOTguOTc3MSA2NC41MjY0TDk5LjE5NzMgNjMuOTM1NUw5Ny4xMjQgNTguMTg4NUg5OC4yMDlMOTkuNjU5MiA2Mi41NDQ0WiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC44NyIvPgo8cGF0aCBkPSJNNjUuOTIzMyAxMDAuMzQyQzY1LjkyMzMgMTAxLjEwOSA2NS43OTQ0IDEwMS43NzggNjUuNTM2NiAxMDIuMzUxQzY1LjI3ODggMTAyLjkyIDY0LjkxMzYgMTAzLjM1NSA2NC40NDA5IDEwMy42NTZDNjMuOTY4MyAxMDMuOTU3IDYzLjQxNjggMTA0LjEwNyA2Mi43ODY2IDEwNC4xMDdDNjIuMTcwNyAxMDQuMTA3IDYxLjYyNDcgMTAzLjk1NyA2MS4xNDg0IDEwMy42NTZDNjAuNjcyMiAxMDMuMzUyIDYwLjMwMTYgMTAyLjkyIDYwLjAzNjYgMTAyLjM2MkM1OS43NzUyIDEwMS44IDU5LjY0MSAxMDEuMTUgNTkuNjMzOCAxMDAuNDEyVjk5Ljg0ODFDNTkuNjMzOCA5OS4wOTYyIDU5Ljc2NDUgOTguNDMyIDYwLjAyNTkgOTcuODU1NUM2MC4yODczIDk3LjI3OSA2MC42NTYxIDk2LjgzODUgNjEuMTMyMyA5Ni41MzQyQzYxLjYxMjEgOTYuMjI2MiA2Mi4xNiA5Ni4wNzIzIDYyLjc3NTkgOTYuMDcyM0M2My40MDI1IDk2LjA3MjMgNjMuOTUzOSA5Ni4yMjQ0IDY0LjQzMDIgOTYuNTI4OEM2NC45MSA5Ni44Mjk2IDY1LjI3ODggOTcuMjY4MiA2NS41MzY2IDk3Ljg0NDdDNjUuNzk0NCA5OC40MTc2IDY1LjkyMzMgOTkuMDg1NCA2NS45MjMzIDk5Ljg0ODFWMTAwLjM0MlpNNjQuODk3NSA5OS44Mzc0QzY0Ljg5NzUgOTguOTEgNjQuNzExMyA5OC4xOTkyIDY0LjMzODkgOTcuNzA1MUM2My45NjY1IDk3LjIwNzQgNjMuNDQ1NSA5Ni45NTg1IDYyLjc3NTkgOTYuOTU4NUM2Mi4xMjQyIDk2Ljk1ODUgNjEuNjEwNCA5Ny4yMDc0IDYxLjIzNDQgOTcuNzA1MUM2MC44NjIgOTguMTk5MiA2MC42NzA0IDk4Ljg4NjcgNjAuNjU5NyA5OS43Njc2VjEwMC4zNDJDNjAuNjU5NyAxMDEuMjQxIDYwLjg0NzcgMTAxLjk0OCA2MS4yMjM2IDEwMi40NjRDNjEuNjAzMiAxMDIuOTc2IDYyLjEyNDIgMTAzLjIzMiA2Mi43ODY2IDEwMy4yMzJDNjMuNDUyNiAxMDMuMjMyIDYzLjk2ODMgMTAyLjk5IDY0LjMzMzUgMTAyLjUwN0M2NC42OTg3IDEwMi4wMiA2NC44ODY3IDEwMS4zMjMgNjQuODk3NSAxMDAuNDE3Vjk5LjgzNzRaIiBmaWxsPSJibGFjayIgZmlsbC1vcGFjaXR5PSIwLjg3Ii8+CjxwYXRoIGQ9Ik02OC4yNTQ0IDk4LjE4ODVMNjguMjg2NiA5OC45MTg5QzY4LjczMDYgOTguMzYwNCA2OS4zMTA3IDk4LjA4MTEgNzAuMDI2OSA5OC4wODExQzcxLjI1NSA5OC4wODExIDcxLjg3NDUgOTguNzczOSA3MS44ODUzIDEwMC4xNlYxMDRINzAuODkxNlYxMDAuMTU0QzcwLjg4OCA5OS43MzU0IDcwLjc5MTMgOTkuNDI1NiA3MC42MDE2IDk5LjIyNTFDNzAuNDE1NCA5OS4wMjQ2IDcwLjEyMzUgOTguOTI0MyA2OS43MjYxIDk4LjkyNDNDNjkuNDAzOCA5OC45MjQzIDY5LjEyMDkgOTkuMDEwMyA2OC44Nzc0IDk5LjE4MjFDNjguNjM0IDk5LjM1NCA2OC40NDQyIDk5LjU3OTYgNjguMzA4MSA5OS44NTg5VjEwNEg2Ny4zMTQ1Vjk4LjE4ODVINjguMjU0NFoiIGZpbGw9ImJsYWNrIiBmaWxsLW9wYWNpdHk9IjAuODciLz4KPHBhdGggZD0iTTc1Ljc5NTQgMTA0LjEwN0M3NS4wMDc2IDEwNC4xMDcgNzQuMzY2NyAxMDMuODUgNzMuODcyNiAxMDMuMzM0QzczLjM3ODQgMTAyLjgxNSA3My4xMzEzIDEwMi4xMjIgNzMuMTMxMyAxMDEuMjU1VjEwMS4wNzNDNzMuMTMxMyAxMDAuNDk2IDczLjI0MDYgOTkuOTgyNCA3My40NTkgOTkuNTMxMkM3My42ODEgOTkuMDc2NSA3My45ODg5IDk4LjcyMiA3NC4zODI4IDk4LjQ2NzhDNzQuNzgwMyA5OC4yMSA3NS4yMSA5OC4wODExIDc1LjY3MTkgOTguMDgxMUM3Ni40Mjc0IDk4LjA4MTEgNzcuMDE0NiA5OC4zMjk5IDc3LjQzMzYgOTguODI3NkM3Ny44NTI1IDk5LjMyNTQgNzguMDYyIDEwMC4wMzggNzguMDYyIDEwMC45NjVWMTAxLjM3OUg3NC4xMjVDNzQuMTM5MyAxMDEuOTUyIDc0LjMwNTggMTAyLjQxNiA3NC42MjQ1IDEwMi43N0M3NC45NDY4IDEwMy4xMjEgNzUuMzU1IDEwMy4yOTYgNzUuODQ5MSAxMDMuMjk2Qzc2LjIgMTAzLjI5NiA3Ni40OTcyIDEwMy4yMjUgNzYuNzQwNyAxMDMuMDgyQzc2Ljk4NDIgMTAyLjkzOCA3Ny4xOTczIDEwMi43NDkgNzcuMzc5OSAxMDIuNTEyTDc3Ljk4NjggMTAyLjk4NUM3Ny40OTk4IDEwMy43MzMgNzYuNzY5NCAxMDQuMTA3IDc1Ljc5NTQgMTA0LjEwN1pNNzUuNjcxOSA5OC44OTc1Qzc1LjI3MDggOTguODk3NSA3NC45MzQyIDk5LjA0NDMgNzQuNjYyMSA5OS4zMzc5Qzc0LjM5IDk5LjYyNzkgNzQuMjIxNyAxMDAuMDM2IDc0LjE1NzIgMTAwLjU2Mkg3Ny4wNjg0VjEwMC40ODdDNzcuMDM5NyA5OS45ODI0IDc2LjkwMzYgOTkuNTkyMSA3Ni42NjAyIDk5LjMxNjRDNzYuNDE2NyA5OS4wMzcxIDc2LjA4NzIgOTguODk3NSA3NS42NzE5IDk4Ljg5NzVaIiBmaWxsPSJibGFjayIgZmlsbC1vcGFjaXR5PSIwLjg3Ii8+CjxwYXRoIGQ9Ik04MS4yODQ3IDEwMS4wODNINzguNjYzNlYxMDAuMjcySDgxLjI4NDdWMTAxLjA4M1oiIGZpbGw9ImJsYWNrIiBmaWxsLW9wYWNpdHk9IjAuODciLz4KPHBhdGggZD0iTTg3LjQwMjMgMTAyLjYzTDg4LjUxOTUgOTguMTg4NUg4OS41MTMyTDg3LjgyMTMgMTA0SDg3LjAxNTZMODUuNjAzIDk5LjU5NTdMODQuMjI4IDEwNEg4My40MjI0TDgxLjczNTggOTguMTg4NUg4Mi43MjQxTDgzLjg2ODIgMTAyLjUzOUw4NS4yMjE3IDk4LjE4ODVIODYuMDIyTDg3LjQwMjMgMTAyLjYzWiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC44NyIvPgo8cGF0aCBkPSJNOTQuMTE2MiAxMDRDOTQuMDU4OSAxMDMuODg1IDk0LjAxMjQgMTAzLjY4MSA5My45NzY2IDEwMy4zODhDOTMuNTE0NiAxMDMuODY4IDkyLjk2MzIgMTA0LjEwNyA5Mi4zMjIzIDEwNC4xMDdDOTEuNzQ5MyAxMDQuMTA3IDkxLjI3ODUgMTAzLjk0NiA5MC45MDk3IDEwMy42MjRDOTAuNTQ0NCAxMDMuMjk4IDkwLjM2MTggMTAyLjg4NiA5MC4zNjE4IDEwMi4zODlDOTAuMzYxOCAxMDEuNzg0IDkwLjU5MSAxMDEuMzE0IDkxLjA0OTMgMTAwLjk4MUM5MS41MTEyIDEwMC42NDUgOTIuMTU5MyAxMDAuNDc3IDkyLjk5MzcgMTAwLjQ3N0g5My45NjA0VjEwMC4wMkM5My45NjA0IDk5LjY3MjcgOTMuODU2NiA5OS4zOTcgOTMuNjQ4OSA5OS4xOTI5QzkzLjQ0MTIgOTguOTg1MiA5My4xMzUxIDk4Ljg4MTMgOTIuNzMwNSA5OC44ODEzQzkyLjM3NiA5OC44ODEzIDkyLjA3ODggOTguOTcwOSA5MS44Mzg5IDk5LjE0OTlDOTEuNTk5IDk5LjMyODkgOTEuNDc5IDk5LjU0NTYgOTEuNDc5IDk5Ljc5OThIOTAuNDhDOTAuNDggOTkuNTA5OCA5MC41ODIgOTkuMjMwNSA5MC43ODYxIDk4Ljk2MTlDOTAuOTkzOCA5OC42ODk4IDkxLjI3MzEgOTguNDc0OSA5MS42MjQgOTguMzE3NEM5MS45Nzg1IDk4LjE1OTggOTIuMzY3IDk4LjA4MTEgOTIuNzg5NiA5OC4wODExQzkzLjQ1OTEgOTguMDgxMSA5My45ODM3IDk4LjI0OTMgOTQuMzYzMyA5OC41ODU5Qzk0Ljc0MjggOTguOTE4OSA5NC45Mzk4IDk5LjM3OTEgOTQuOTU0MSA5OS45NjYzVjEwMi42NDFDOTQuOTU0MSAxMDMuMTc1IDk1LjAyMjEgMTAzLjU5OSA5NS4xNTgyIDEwMy45MTRWMTA0SDk0LjExNjJaTTkyLjQ2NzMgMTAzLjI0M0M5Mi43Nzg4IDEwMy4yNDMgOTMuMDc0MiAxMDMuMTYyIDkzLjM1MzUgMTAzLjAwMUM5My42MzI4IDEwMi44NCA5My44MzUxIDEwMi42MyA5My45NjA0IDEwMi4zNzNWMTAxLjE4SDkzLjE4MTZDOTEuOTY0MiAxMDEuMTggOTEuMzU1NSAxMDEuNTM2IDkxLjM1NTUgMTAyLjI0OUM5MS4zNTU1IDEwMi41NjEgOTEuNDU5MyAxMDIuODA0IDkxLjY2NyAxMDIuOTc5QzkxLjg3NDcgMTAzLjE1NSA5Mi4xNDE0IDEwMy4yNDMgOTIuNDY3MyAxMDMuMjQzWiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC44NyIvPgo8cGF0aCBkPSJNOTguMzI3MSAxMDIuNTQ0TDk5LjY4MDcgOTguMTg4NUgxMDAuNzQ0TDk4LjQwNzcgMTA0Ljg5N0M5OC4wNDYxIDEwNS44NjQgOTcuNDcxNCAxMDYuMzQ3IDk2LjY4MzYgMTA2LjM0N0w5Ni40OTU2IDEwNi4zMzFMOTYuMTI1IDEwNi4yNjFWMTA1LjQ1Nkw5Ni4zOTM2IDEwNS40NzdDOTYuNzMwMSAxMDUuNDc3IDk2Ljk5MTUgMTA1LjQwOSA5Ny4xNzc3IDEwNS4yNzNDOTcuMzY3NSAxMDUuMTM3IDk3LjUyMzMgMTA0Ljg4OCA5Ny42NDUgMTA0LjUyNkw5Ny44NjUyIDEwMy45MzZMOTUuNzkyIDk4LjE4ODVIOTYuODc3TDk4LjMyNzEgMTAyLjU0NFoiIGZpbGw9ImJsYWNrIiBmaWxsLW9wYWNpdHk9IjAuODciLz4KPHBhdGggZD0iTTY1LjkyMzMgMTQwLjM0MkM2NS45MjMzIDE0MS4xMDkgNjUuNzk0NCAxNDEuNzc4IDY1LjUzNjYgMTQyLjM1MUM2NS4yNzg4IDE0Mi45MiA2NC45MTM2IDE0My4zNTUgNjQuNDQwOSAxNDMuNjU2QzYzLjk2ODMgMTQzLjk1NyA2My40MTY4IDE0NC4xMDcgNjIuNzg2NiAxNDQuMTA3QzYyLjE3MDcgMTQ0LjEwNyA2MS42MjQ3IDE0My45NTcgNjEuMTQ4NCAxNDMuNjU2QzYwLjY3MjIgMTQzLjM1MiA2MC4zMDE2IDE0Mi45MiA2MC4wMzY2IDE0Mi4zNjJDNTkuNzc1MiAxNDEuOCA1OS42NDEgMTQxLjE1IDU5LjYzMzggMTQwLjQxMlYxMzkuODQ4QzU5LjYzMzggMTM5LjA5NiA1OS43NjQ1IDEzOC40MzIgNjAuMDI1OSAxMzcuODU1QzYwLjI4NzMgMTM3LjI3OSA2MC42NTYxIDEzNi44MzkgNjEuMTMyMyAxMzYuNTM0QzYxLjYxMjEgMTM2LjIyNiA2Mi4xNiAxMzYuMDcyIDYyLjc3NTkgMTM2LjA3MkM2My40MDI1IDEzNi4wNzIgNjMuOTUzOSAxMzYuMjI0IDY0LjQzMDIgMTM2LjUyOUM2NC45MSAxMzYuODMgNjUuMjc4OCAxMzcuMjY4IDY1LjUzNjYgMTM3Ljg0NUM2NS43OTQ0IDEzOC40MTggNjUuOTIzMyAxMzkuMDg1IDY1LjkyMzMgMTM5Ljg0OFYxNDAuMzQyWk02NC44OTc1IDEzOS44MzdDNjQuODk3NSAxMzguOTEgNjQuNzExMyAxMzguMTk5IDY0LjMzODkgMTM3LjcwNUM2My45NjY1IDEzNy4yMDcgNjMuNDQ1NSAxMzYuOTU4IDYyLjc3NTkgMTM2Ljk1OEM2Mi4xMjQyIDEzNi45NTggNjEuNjEwNCAxMzcuMjA3IDYxLjIzNDQgMTM3LjcwNUM2MC44NjIgMTM4LjE5OSA2MC42NzA0IDEzOC44ODcgNjAuNjU5NyAxMzkuNzY4VjE0MC4zNDJDNjAuNjU5NyAxNDEuMjQxIDYwLjg0NzcgMTQxLjk0OCA2MS4yMjM2IDE0Mi40NjRDNjEuNjAzMiAxNDIuOTc2IDYyLjEyNDIgMTQzLjIzMiA2Mi43ODY2IDE0My4yMzJDNjMuNDUyNiAxNDMuMjMyIDYzLjk2ODMgMTQyLjk5IDY0LjMzMzUgMTQyLjUwN0M2NC42OTg3IDE0Mi4wMiA2NC44ODY3IDE0MS4zMjMgNjQuODk3NSAxNDAuNDE3VjEzOS44MzdaIiBmaWxsPSJibGFjayIgZmlsbC1vcGFjaXR5PSIwLjg3Ii8+CjxwYXRoIGQ9Ik02OC4yNTQ0IDEzOC4xODhMNjguMjg2NiAxMzguOTE5QzY4LjczMDYgMTM4LjM2IDY5LjMxMDcgMTM4LjA4MSA3MC4wMjY5IDEzOC4wODFDNzEuMjU1IDEzOC4wODEgNzEuODc0NSAxMzguNzc0IDcxLjg4NTMgMTQwLjE2VjE0NEg3MC44OTE2VjE0MC4xNTRDNzAuODg4IDEzOS43MzUgNzAuNzkxMyAxMzkuNDI2IDcwLjYwMTYgMTM5LjIyNUM3MC40MTU0IDEzOS4wMjUgNzAuMTIzNSAxMzguOTI0IDY5LjcyNjEgMTM4LjkyNEM2OS40MDM4IDEzOC45MjQgNjkuMTIwOSAxMzkuMDEgNjguODc3NCAxMzkuMTgyQzY4LjYzNCAxMzkuMzU0IDY4LjQ0NDIgMTM5LjU4IDY4LjMwODEgMTM5Ljg1OVYxNDRINjcuMzE0NVYxMzguMTg4SDY4LjI1NDRaIiBmaWxsPSJibGFjayIgZmlsbC1vcGFjaXR5PSIwLjg3Ii8+CjxwYXRoIGQ9Ik03NS43OTU0IDE0NC4xMDdDNzUuMDA3NiAxNDQuMTA3IDc0LjM2NjcgMTQzLjg1IDczLjg3MjYgMTQzLjMzNEM3My4zNzg0IDE0Mi44MTUgNzMuMTMxMyAxNDIuMTIyIDczLjEzMTMgMTQxLjI1NVYxNDEuMDczQzczLjEzMTMgMTQwLjQ5NiA3My4yNDA2IDEzOS45ODIgNzMuNDU5IDEzOS41MzFDNzMuNjgxIDEzOS4wNzYgNzMuOTg4OSAxMzguNzIyIDc0LjM4MjggMTM4LjQ2OEM3NC43ODAzIDEzOC4yMSA3NS4yMSAxMzguMDgxIDc1LjY3MTkgMTM4LjA4MUM3Ni40Mjc0IDEzOC4wODEgNzcuMDE0NiAxMzguMzMgNzcuNDMzNiAxMzguODI4Qzc3Ljg1MjUgMTM5LjMyNSA3OC4wNjIgMTQwLjAzOCA3OC4wNjIgMTQwLjk2NVYxNDEuMzc5SDc0LjEyNUM3NC4xMzkzIDE0MS45NTIgNzQuMzA1OCAxNDIuNDE2IDc0LjYyNDUgMTQyLjc3Qzc0Ljk0NjggMTQzLjEyMSA3NS4zNTUgMTQzLjI5NiA3NS44NDkxIDE0My4yOTZDNzYuMiAxNDMuMjk2IDc2LjQ5NzIgMTQzLjIyNSA3Ni43NDA3IDE0My4wODJDNzYuOTg0MiAxNDIuOTM4IDc3LjE5NzMgMTQyLjc0OSA3Ny4zNzk5IDE0Mi41MTJMNzcuOTg2OCAxNDIuOTg1Qzc3LjQ5OTggMTQzLjczMyA3Ni43Njk0IDE0NC4xMDcgNzUuNzk1NCAxNDQuMTA3Wk03NS42NzE5IDEzOC44OTdDNzUuMjcwOCAxMzguODk3IDc0LjkzNDIgMTM5LjA0NCA3NC42NjIxIDEzOS4zMzhDNzQuMzkgMTM5LjYyOCA3NC4yMjE3IDE0MC4wMzYgNzQuMTU3MiAxNDAuNTYySDc3LjA2ODRWMTQwLjQ4N0M3Ny4wMzk3IDEzOS45ODIgNzYuOTAzNiAxMzkuNTkyIDc2LjY2MDIgMTM5LjMxNkM3Ni40MTY3IDEzOS4wMzcgNzYuMDg3MiAxMzguODk3IDc1LjY3MTkgMTM4Ljg5N1oiIGZpbGw9ImJsYWNrIiBmaWxsLW9wYWNpdHk9IjAuODciLz4KPHBhdGggZD0iTTgxLjI4NDcgMTQxLjA4M0g3OC42NjM2VjE0MC4yNzJIODEuMjg0N1YxNDEuMDgzWiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC44NyIvPgo8cGF0aCBkPSJNODcuNDAyMyAxNDIuNjNMODguNTE5NSAxMzguMTg4SDg5LjUxMzJMODcuODIxMyAxNDRIODcuMDE1Nkw4NS42MDMgMTM5LjU5Nkw4NC4yMjggMTQ0SDgzLjQyMjRMODEuNzM1OCAxMzguMTg4SDgyLjcyNDFMODMuODY4MiAxNDIuNTM5TDg1LjIyMTcgMTM4LjE4OEg4Ni4wMjJMODcuNDAyMyAxNDIuNjNaIiBmaWxsPSJibGFjayIgZmlsbC1vcGFjaXR5PSIwLjg3Ii8+CjxwYXRoIGQ9Ik05NC4xMTYyIDE0NEM5NC4wNTg5IDE0My44ODUgOTQuMDEyNCAxNDMuNjgxIDkzLjk3NjYgMTQzLjM4OEM5My41MTQ2IDE0My44NjggOTIuOTYzMiAxNDQuMTA3IDkyLjMyMjMgMTQ0LjEwN0M5MS43NDkzIDE0NC4xMDcgOTEuMjc4NSAxNDMuOTQ2IDkwLjkwOTcgMTQzLjYyNEM5MC41NDQ0IDE0My4yOTggOTAuMzYxOCAxNDIuODg2IDkwLjM2MTggMTQyLjM4OUM5MC4zNjE4IDE0MS43ODQgOTAuNTkxIDE0MS4zMTQgOTEuMDQ5MyAxNDAuOTgxQzkxLjUxMTIgMTQwLjY0NSA5Mi4xNTkzIDE0MC40NzcgOTIuOTkzNyAxNDAuNDc3SDkzLjk2MDRWMTQwLjAyQzkzLjk2MDQgMTM5LjY3MyA5My44NTY2IDEzOS4zOTcgOTMuNjQ4OSAxMzkuMTkzQzkzLjQ0MTIgMTM4Ljk4NSA5My4xMzUxIDEzOC44ODEgOTIuNzMwNSAxMzguODgxQzkyLjM3NiAxMzguODgxIDkyLjA3ODggMTM4Ljk3MSA5MS44Mzg5IDEzOS4xNUM5MS41OTkgMTM5LjMyOSA5MS40NzkgMTM5LjU0NiA5MS40NzkgMTM5LjhIOTAuNDhDOTAuNDggMTM5LjUxIDkwLjU4MiAxMzkuMjMgOTAuNzg2MSAxMzguOTYyQzkwLjk5MzggMTM4LjY5IDkxLjI3MzEgMTM4LjQ3NSA5MS42MjQgMTM4LjMxN0M5MS45Nzg1IDEzOC4xNiA5Mi4zNjcgMTM4LjA4MSA5Mi43ODk2IDEzOC4wODFDOTMuNDU5MSAxMzguMDgxIDkzLjk4MzcgMTM4LjI0OSA5NC4zNjMzIDEzOC41ODZDOTQuNzQyOCAxMzguOTE5IDk0LjkzOTggMTM5LjM3OSA5NC45NTQxIDEzOS45NjZWMTQyLjY0MUM5NC45NTQxIDE0My4xNzUgOTUuMDIyMSAxNDMuNTk5IDk1LjE1ODIgMTQzLjkxNFYxNDRIOTQuMTE2MlpNOTIuNDY3MyAxNDMuMjQzQzkyLjc3ODggMTQzLjI0MyA5My4wNzQyIDE0My4xNjIgOTMuMzUzNSAxNDMuMDAxQzkzLjYzMjggMTQyLjg0IDkzLjgzNTEgMTQyLjYzIDkzLjk2MDQgMTQyLjM3M1YxNDEuMThIOTMuMTgxNkM5MS45NjQyIDE0MS4xOCA5MS4zNTU1IDE0MS41MzYgOTEuMzU1NSAxNDIuMjQ5QzkxLjM1NTUgMTQyLjU2MSA5MS40NTkzIDE0Mi44MDQgOTEuNjY3IDE0Mi45NzlDOTEuODc0NyAxNDMuMTU1IDkyLjE0MTQgMTQzLjI0MyA5Mi40NjczIDE0My4yNDNaIiBmaWxsPSJibGFjayIgZmlsbC1vcGFjaXR5PSIwLjg3Ii8+CjxwYXRoIGQ9Ik05OC4zMjcxIDE0Mi41NDRMOTkuNjgwNyAxMzguMTg4SDEwMC43NDRMOTguNDA3NyAxNDQuODk3Qzk4LjA0NjEgMTQ1Ljg2NCA5Ny40NzE0IDE0Ni4zNDcgOTYuNjgzNiAxNDYuMzQ3TDk2LjQ5NTYgMTQ2LjMzMUw5Ni4xMjUgMTQ2LjI2MVYxNDUuNDU2TDk2LjM5MzYgMTQ1LjQ3N0M5Ni43MzAxIDE0NS40NzcgOTYuOTkxNSAxNDUuNDA5IDk3LjE3NzcgMTQ1LjI3M0M5Ny4zNjc1IDE0NS4xMzcgOTcuNTIzMyAxNDQuODg4IDk3LjY0NSAxNDQuNTI2TDk3Ljg2NTIgMTQzLjkzNkw5NS43OTIgMTM4LjE4OEg5Ni44NzdMOTguMzI3MSAxNDIuNTQ0WiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC44NyIvPgo8cGF0aCBkPSJNMTIuNzczNCAxMDMuNDc5QzEyLjc3MzQgMTAzLjMwNyAxMi44MjM2IDEwMy4xNjQgMTIuOTIzOCAxMDMuMDQ5QzEzLjAyNzcgMTAyLjkzNSAxMy4xODE2IDEwMi44NzcgMTMuMzg1NyAxMDIuODc3QzEzLjU4OTggMTAyLjg3NyAxMy43NDM4IDEwMi45MzUgMTMuODQ3NyAxMDMuMDQ5QzEzLjk1NTEgMTAzLjE2NCAxNC4wMDg4IDEwMy4zMDcgMTQuMDA4OCAxMDMuNDc5QzE0LjAwODggMTAzLjY0NCAxMy45NTUxIDEwMy43ODIgMTMuODQ3NyAxMDMuODkzQzEzLjc0MzggMTA0LjAwNCAxMy41ODk4IDEwNC4wNTkgMTMuMzg1NyAxMDQuMDU5QzEzLjE4MTYgMTA0LjA1OSAxMy4wMjc3IDEwNC4wMDQgMTIuOTIzOCAxMDMuODkzQzEyLjgyMzYgMTAzLjc4MiAxMi43NzM0IDEwMy42NDQgMTIuNzczNCAxMDMuNDc5WiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC44NyIvPgo8cGF0aCBkPSJNMTUuNjczOCAxMDMuNDc5QzE1LjY3MzggMTAzLjMwNyAxNS43MjQgMTAzLjE2NCAxNS44MjQyIDEwMy4wNDlDMTUuOTI4MSAxMDIuOTM1IDE2LjA4MiAxMDIuODc3IDE2LjI4NjEgMTAyLjg3N0MxNi40OTAyIDEwMi44NzcgMTYuNjQ0MiAxMDIuOTM1IDE2Ljc0OCAxMDMuMDQ5QzE2Ljg1NTUgMTAzLjE2NCAxNi45MDkyIDEwMy4zMDcgMTYuOTA5MiAxMDMuNDc5QzE2LjkwOTIgMTAzLjY0NCAxNi44NTU1IDEwMy43ODIgMTYuNzQ4IDEwMy44OTNDMTYuNjQ0MiAxMDQuMDA0IDE2LjQ5MDIgMTA0LjA1OSAxNi4yODYxIDEwNC4wNTlDMTYuMDgyIDEwNC4wNTkgMTUuOTI4MSAxMDQuMDA0IDE1LjgyNDIgMTAzLjg5M0MxNS43MjQgMTAzLjc4MiAxNS42NzM4IDEwMy42NDQgMTUuNjczOCAxMDMuNDc5WiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC44NyIvPgo8cGF0aCBkPSJNMTguMjg5NiAxMDEuMDQxQzE4LjI4OTYgMTAwLjQ3MSAxOC40MDA2IDk5Ljk1OTEgMTguNjIyNiA5OS41MDQ0QzE4Ljg0ODEgOTkuMDQ5NiAxOS4xNTk3IDk4LjY5ODcgMTkuNTU3MSA5OC40NTE3QzE5Ljk1ODIgOTguMjA0NiAyMC40MTQ3IDk4LjA4MTEgMjAuOTI2OCA5OC4wODExQzIxLjcxODEgOTguMDgxMSAyMi4zNTczIDk4LjM1NSAyMi44NDQyIDk4LjkwMjhDMjMuMzM0OCA5OS40NTA3IDIzLjU4MDEgMTAwLjE3OSAyMy41ODAxIDEwMS4wODlWMTAxLjE1OUMyMy41ODAxIDEwMS43MjQgMjMuNDcwOSAxMDIuMjMzIDIzLjI1MjQgMTAyLjY4NEMyMy4wMzc2IDEwMy4xMzIgMjIuNzI3OSAxMDMuNDgxIDIyLjMyMzIgMTAzLjczMUMyMS45MjIyIDEwMy45ODIgMjEuNDYwMyAxMDQuMTA3IDIwLjkzNzUgMTA0LjEwN0MyMC4xNDk3IDEwNC4xMDcgMTkuNTEwNiAxMDMuODMzIDE5LjAyIDEwMy4yODZDMTguNTMzIDEwMi43MzggMTguMjg5NiAxMDIuMDEzIDE4LjI4OTYgMTAxLjExVjEwMS4wNDFaTTE5LjI4ODYgMTAxLjE1OUMxOS4yODg2IDEwMS44MDMgMTkuNDM3MiAxMDIuMzIxIDE5LjczNDQgMTAyLjcxMUMyMC4wMzUyIDEwMy4xMDEgMjAuNDM2MiAxMDMuMjk2IDIwLjkzNzUgMTAzLjI5NkMyMS40NDI0IDEwMy4yOTYgMjEuODQzNCAxMDMuMDk5IDIyLjE0MDYgMTAyLjcwNkMyMi40Mzc4IDEwMi4zMDggMjIuNTg2NCAxMDEuNzUzIDIyLjU4NjQgMTAxLjA0MUMyMi41ODY0IDEwMC40MDMgMjIuNDM0MiA5OS44ODc1IDIyLjEyOTkgOTkuNDkzN0MyMS44MjkxIDk5LjA5NjIgMjEuNDI4MSA5OC44OTc1IDIwLjkyNjggOTguODk3NUMyMC40MzYyIDk4Ljg5NzUgMjAuMDQwNSA5OS4wOTI2IDE5LjczOTcgOTkuNDgyOUMxOS40MzkgOTkuODczMiAxOS4yODg2IDEwMC40MzIgMTkuMjg4NiAxMDEuMTU5WiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC44NyIvPgo8cGF0aCBkPSJNMjYuMTY4OSA5OS42MDY0SDI2LjkxNTVDMjcuMzg0NiA5OS41OTkzIDI3Ljc1MzQgOTkuNDc1NyAyOC4wMjIgOTkuMjM1OEMyOC4yOTA1IDk4Ljk5NTkgMjguNDI0OCA5OC42NzE5IDI4LjQyNDggOTguMjYzN0MyOC40MjQ4IDk3LjM0NyAyNy45NjgzIDk2Ljg4ODcgMjcuMDU1MiA5Ni44ODg3QzI2LjYyNTUgOTYuODg4NyAyNi4yODE3IDk3LjAxMjIgMjYuMDIzOSA5Ny4yNTkzQzI1Ljc2OTcgOTcuNTAyOCAyNS42NDI2IDk3LjgyNjggMjUuNjQyNiA5OC4yMzE0SDI0LjY0ODlDMjQuNjQ4OSA5Ny42MTIgMjQuODc0NSA5Ny4wOTgxIDI1LjMyNTcgOTYuNjg5OUMyNS43ODA0IDk2LjI3ODIgMjYuMzU2OSA5Ni4wNzIzIDI3LjA1NTIgOTYuMDcyM0MyNy43OTI4IDk2LjA3MjMgMjguMzcxMSA5Ni4yNjc0IDI4Ljc5IDk2LjY1NzdDMjkuMjA5IDk3LjA0OCAyOS40MTg1IDk3LjU5MDUgMjkuNDE4NSA5OC4yODUyQzI5LjQxODUgOTguNjI1MyAyOS4zMDc1IDk4Ljk1NDggMjkuMDg1NCA5OS4yNzM0QzI4Ljg2NyA5OS41OTIxIDI4LjU2OCA5OS44MzAyIDI4LjE4ODUgOTkuOTg3OEMyOC42MTgyIDEwMC4xMjQgMjguOTQ5NCAxMDAuMzQ5IDI5LjE4MjEgMTAwLjY2NUMyOS40MTg1IDEwMC45OCAyOS41MzY2IDEwMS4zNjUgMjkuNTM2NiAxMDEuODE5QzI5LjUzNjYgMTAyLjUyMSAyOS4zMDc1IDEwMy4wNzggMjguODQ5MSAxMDMuNDlDMjguMzkwOCAxMDMuOTAyIDI3Ljc5NDYgMTA0LjEwNyAyNy4wNjA1IDEwNC4xMDdDMjYuMzI2NSAxMDQuMTA3IDI1LjcyODUgMTAzLjkwOSAyNS4yNjY2IDEwMy41MTFDMjQuODA4MyAxMDMuMTE0IDI0LjU3OTEgMTAyLjU4OSAyNC41NzkxIDEwMS45MzhIMjUuNTc4MUMyNS41NzgxIDEwMi4zNDkgMjUuNzEyNCAxMDIuNjc5IDI1Ljk4MSAxMDIuOTI2QzI2LjI0OTUgMTAzLjE3MyAyNi42MDk0IDEwMy4yOTYgMjcuMDYwNSAxMDMuMjk2QzI3LjU0MDQgMTAzLjI5NiAyNy45MDc0IDEwMy4xNzEgMjguMTYxNiAxMDIuOTJDMjguNDE1OSAxMDIuNjcgMjguNTQzIDEwMi4zMSAyOC41NDMgMTAxLjg0MUMyOC41NDMgMTAxLjM4NiAyOC40MDMzIDEwMS4wMzcgMjguMTI0IDEwMC43OTNDMjcuODQ0NyAxMDAuNTUgMjcuNDQxOSAxMDAuNDI1IDI2LjkxNTUgMTAwLjQxN0gyNi4xNjg5Vjk5LjYwNjRaIiBmaWxsPSJibGFjayIgZmlsbC1vcGFjaXR5PSIwLjg3Ii8+CjxwYXRoIGQ9Ik0zMC43NjEyIDEwMS4wNDZDMzAuNzYxMiAxMDAuMTU0IDMwLjk3MjUgOTkuNDM4MiAzMS4zOTUgOTguODk3NUMzMS44MTc1IDk4LjM1MzIgMzIuMzcwOCA5OC4wODExIDMzLjA1NDcgOTguMDgxMUMzMy43MzUgOTguMDgxMSAzNC4yNzM5IDk4LjMxMzggMzQuNjcxNCA5OC43NzkzVjk1Ljc1SDM1LjY2NVYxMDRIMzQuNzUyTDM0LjcwMzYgMTAzLjM3N0MzNC4zMDYyIDEwMy44NjQgMzMuNzUyOSAxMDQuMTA3IDMzLjA0MzkgMTA0LjEwN0MzMi4zNzA4IDEwNC4xMDcgMzEuODIxMSAxMDMuODMyIDMxLjM5NSAxMDMuMjhDMzAuOTcyNSAxMDIuNzI5IDMwLjc2MTIgMTAyLjAwOSAzMC43NjEyIDEwMS4xMjFWMTAxLjA0NlpNMzEuNzU0OSAxMDEuMTU5QzMxLjc1NDkgMTAxLjgxOCAzMS44OTEgMTAyLjMzMyAzMi4xNjMxIDEwMi43MDZDMzIuNDM1MiAxMDMuMDc4IDMyLjgxMTIgMTAzLjI2NCAzMy4yOTEgMTAzLjI2NEMzMy45MjEyIDEwMy4yNjQgMzQuMzgxMyAxMDIuOTgxIDM0LjY3MTQgMTAyLjQxNlY5OS43NDYxQzM0LjM3NDIgOTkuMTk4MiAzMy45MTc2IDk4LjkyNDMgMzMuMzAxOCA5OC45MjQzQzMyLjgxNDggOTguOTI0MyAzMi40MzUyIDk5LjExMjMgMzIuMTYzMSA5OS40ODgzQzMxLjg5MSA5OS44NjQzIDMxLjc1NDkgMTAwLjQyMSAzMS43NTQ5IDEwMS4xNTlaIiBmaWxsPSJibGFjayIgZmlsbC1vcGFjaXR5PSIwLjg3Ii8+CjxwYXRoIGQ9Ik00MC4wMjEgOTkuMDgwMUMzOS44NzA2IDk5LjA1NSAzOS43MDc3IDk5LjA0MjUgMzkuNTMyMiA5OS4wNDI1QzM4Ljg4MDUgOTkuMDQyNSAzOC40MzgzIDk5LjMyIDM4LjIwNTYgOTkuODc1VjEwNEgzNy4yMTE5Vjk4LjE4ODVIMzguMTc4N0wzOC4xOTQ4IDk4Ljg1OTlDMzguNTIwNyA5OC4zNDA3IDM4Ljk4MjYgOTguMDgxMSAzOS41ODA2IDk4LjA4MTFDMzkuNzczOSA5OC4wODExIDM5LjkyMDcgOTguMTA2MSA0MC4wMjEgOTguMTU2MlY5OS4wODAxWiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC44NyIvPgo8cGF0aCBkPSJNNDEuODc5NCA5OC4xODg1TDQxLjkxMTYgOTguOTE4OUM0Mi4zNTU2IDk4LjM2MDQgNDIuOTM1NyA5OC4wODExIDQzLjY1MTkgOTguMDgxMUM0NC44OCA5OC4wODExIDQ1LjQ5OTUgOTguNzczOSA0NS41MTAzIDEwMC4xNlYxMDRINDQuNTE2NlYxMDAuMTU0QzQ0LjUxMyA5OS43MzU0IDQ0LjQxNjMgOTkuNDI1NiA0NC4yMjY2IDk5LjIyNTFDNDQuMDQwNCA5OS4wMjQ2IDQzLjc0ODUgOTguOTI0MyA0My4zNTExIDk4LjkyNDNDNDMuMDI4OCA5OC45MjQzIDQyLjc0NTkgOTkuMDEwMyA0Mi41MDI0IDk5LjE4MjFDNDIuMjU5IDk5LjM1NCA0Mi4wNjkyIDk5LjU3OTYgNDEuOTMzMSA5OS44NTg5VjEwNEg0MC45Mzk1Vjk4LjE4ODVINDEuODc5NFoiIGZpbGw9ImJsYWNrIiBmaWxsLW9wYWNpdHk9IjAuODciLz4KPHBhdGggZD0iTTEyLjc3MzQgMTQzLjQ3OUMxMi43NzM0IDE0My4zMDcgMTIuODIzNiAxNDMuMTY0IDEyLjkyMzggMTQzLjA0OUMxMy4wMjc3IDE0Mi45MzUgMTMuMTgxNiAxNDIuODc3IDEzLjM4NTcgMTQyLjg3N0MxMy41ODk4IDE0Mi44NzcgMTMuNzQzOCAxNDIuOTM1IDEzLjg0NzcgMTQzLjA0OUMxMy45NTUxIDE0My4xNjQgMTQuMDA4OCAxNDMuMzA3IDE0LjAwODggMTQzLjQ3OUMxNC4wMDg4IDE0My42NDQgMTMuOTU1MSAxNDMuNzgyIDEzLjg0NzcgMTQzLjg5M0MxMy43NDM4IDE0NC4wMDQgMTMuNTg5OCAxNDQuMDU5IDEzLjM4NTcgMTQ0LjA1OUMxMy4xODE2IDE0NC4wNTkgMTMuMDI3NyAxNDQuMDA0IDEyLjkyMzggMTQzLjg5M0MxMi44MjM2IDE0My43ODIgMTIuNzczNCAxNDMuNjQ0IDEyLjc3MzQgMTQzLjQ3OVoiIGZpbGw9ImJsYWNrIiBmaWxsLW9wYWNpdHk9IjAuODciLz4KPHBhdGggZD0iTTE1LjY3MzggMTQzLjQ3OUMxNS42NzM4IDE0My4zMDcgMTUuNzI0IDE0My4xNjQgMTUuODI0MiAxNDMuMDQ5QzE1LjkyODEgMTQyLjkzNSAxNi4wODIgMTQyLjg3NyAxNi4yODYxIDE0Mi44NzdDMTYuNDkwMiAxNDIuODc3IDE2LjY0NDIgMTQyLjkzNSAxNi43NDggMTQzLjA0OUMxNi44NTU1IDE0My4xNjQgMTYuOTA5MiAxNDMuMzA3IDE2LjkwOTIgMTQzLjQ3OUMxNi45MDkyIDE0My42NDQgMTYuODU1NSAxNDMuNzgyIDE2Ljc0OCAxNDMuODkzQzE2LjY0NDIgMTQ0LjAwNCAxNi40OTAyIDE0NC4wNTkgMTYuMjg2MSAxNDQuMDU5QzE2LjA4MiAxNDQuMDU5IDE1LjkyODEgMTQ0LjAwNCAxNS44MjQyIDE0My44OTNDMTUuNzI0IDE0My43ODIgMTUuNjczOCAxNDMuNjQ0IDE1LjY3MzggMTQzLjQ3OVoiIGZpbGw9ImJsYWNrIiBmaWxsLW9wYWNpdHk9IjAuODciLz4KPHBhdGggZD0iTTIxLjM2MTggMTM5LjA4QzIxLjIxMTQgMTM5LjA1NSAyMS4wNDg1IDEzOS4wNDIgMjAuODczIDEzOS4wNDJDMjAuMjIxNCAxMzkuMDQyIDE5Ljc3OTEgMTM5LjMyIDE5LjU0NjQgMTM5Ljg3NVYxNDRIMTguNTUyN1YxMzguMTg4SDE5LjUxOTVMMTkuNTM1NiAxMzguODZDMTkuODYxNSAxMzguMzQxIDIwLjMyMzQgMTM4LjA4MSAyMC45MjE0IDEzOC4wODFDMjEuMTE0NyAxMzguMDgxIDIxLjI2MTYgMTM4LjEwNiAyMS4zNjE4IDEzOC4xNTZWMTM5LjA4WiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC44NyIvPgo8cGF0aCBkPSJNMjQuMjk0NCAxNDIuNjUyTDI1LjczMzkgMTM4LjE4OEgyNi43NDlMMjQuNjY1IDE0NEgyMy45MDc3TDIxLjgwMjIgMTM4LjE4OEgyMi44MTc0TDI0LjI5NDQgMTQyLjY1MloiIGZpbGw9ImJsYWNrIiBmaWxsLW9wYWNpdHk9IjAuODciLz4KPHBhdGggZD0iTTMxLjQxMTEgMTQwLjU2MkMzMS4yMDM1IDE0MC44MSAzMC45NTQ2IDE0MS4wMDggMzAuNjY0NiAxNDEuMTU5QzMwLjM3ODEgMTQxLjMwOSAzMC4wNjMgMTQxLjM4NCAyOS43MTkyIDE0MS4zODRDMjkuMjY4MSAxNDEuMzg0IDI4Ljg3NDIgMTQxLjI3MyAyOC41Mzc2IDE0MS4wNTFDMjguMjA0NiAxNDAuODI5IDI3Ljk0NjggMTQwLjUxOCAyNy43NjQyIDE0MC4xMTdDMjcuNTgxNSAxMzkuNzEyIDI3LjQ5MDIgMTM5LjI2NiAyNy40OTAyIDEzOC43NzlDMjcuNDkwMiAxMzguMjU3IDI3LjU4ODcgMTM3Ljc4NiAyNy43ODU2IDEzNy4zNjdDMjcuOTg2MiAxMzYuOTQ4IDI4LjI2OSAxMzYuNjI3IDI4LjYzNDMgMTM2LjQwNUMyOC45OTk1IDEzNi4xODMgMjkuNDI1NiAxMzYuMDcyIDI5LjkxMjYgMTM2LjA3MkMzMC42ODYgMTM2LjA3MiAzMS4yOTQ4IDEzNi4zNjIgMzEuNzM4OCAxMzYuOTQyQzMyLjE4NjQgMTM3LjUxOSAzMi40MTAyIDEzOC4zMDcgMzIuNDEwMiAxMzkuMzA2VjEzOS41OTZDMzIuNDEwMiAxNDEuMTE4IDMyLjEwOTQgMTQyLjIyOSAzMS41MDc4IDE0Mi45MzFDMzAuOTA2MiAxNDMuNjI5IDI5Ljk5ODUgMTQzLjk4NyAyOC43ODQ3IDE0NC4wMDVIMjguNTkxM1YxNDMuMTY3SDI4LjgwMDhDMjkuNjIwOCAxNDMuMTUzIDMwLjI1MSAxNDIuOTQgMzAuNjkxNCAxNDIuNTI4QzMxLjEzMTggMTQyLjExMyAzMS4zNzE3IDE0MS40NTggMzEuNDExMSAxNDAuNTYyWk0yOS44ODA0IDE0MC41NjJDMzAuMjEzNCAxNDAuNTYyIDMwLjUxOTUgMTQwLjQ2IDMwLjc5ODggMTQwLjI1NkMzMS4wODE3IDE0MC4wNTIgMzEuMjg3NiAxMzkuOCAzMS40MTY1IDEzOS40OTlWMTM5LjEwMkMzMS40MTY1IDEzOC40NSAzMS4yNzUxIDEzNy45MiAzMC45OTIyIDEzNy41MTJDMzAuNzA5MyAxMzcuMTA0IDMwLjM1MTIgMTM2Ljg5OSAyOS45MTggMTM2Ljg5OUMyOS40ODExIDEzNi44OTkgMjkuMTMwMiAxMzcuMDY4IDI4Ljg2NTIgMTM3LjQwNEMyOC42MDAzIDEzNy43MzcgMjguNDY3OCAxMzguMTc4IDI4LjQ2NzggMTM4LjcyNkMyOC40Njc4IDEzOS4yNTkgMjguNTk0OSAxMzkuNyAyOC44NDkxIDE0MC4wNDdDMjkuMTA2OSAxNDAuMzkxIDI5LjQ1MDcgMTQwLjU2MiAyOS44ODA0IDE0MC41NjJaIiBmaWxsPSJibGFjayIgZmlsbC1vcGFjaXR5PSIwLjg3Ii8+CjxwYXRoIGQ9Ik0zNS41MDM5IDE0MS4zMDlMMzQuODgwOSAxNDEuOTU5VjE0NEgzMy44ODcyVjEzNS43NUgzNC44ODA5VjE0MC43NEwzNS40MTI2IDE0MC4xMDFMMzcuMjIyNyAxMzguMTg4SDM4LjQzMTJMMzYuMTY5OSAxNDAuNjE2TDM4LjY5NDMgMTQ0SDM3LjUyODhMMzUuNTAzOSAxNDEuMzA5WiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC44NyIvPgo8cGF0aCBkPSJNNDEuNzYxMiAxNDQuMTA3QzQwLjk3MzUgMTQ0LjEwNyA0MC4zMzI1IDE0My44NSAzOS44Mzg0IDE0My4zMzRDMzkuMzQ0MiAxNDIuODE1IDM5LjA5NzIgMTQyLjEyMiAzOS4wOTcyIDE0MS4yNTVWMTQxLjA3M0MzOS4wOTcyIDE0MC40OTYgMzkuMjA2NCAxMzkuOTgyIDM5LjQyNDggMTM5LjUzMUMzOS42NDY4IDEzOS4wNzYgMzkuOTU0OCAxMzguNzIyIDQwLjM0ODYgMTM4LjQ2OEM0MC43NDYxIDEzOC4yMSA0MS4xNzU4IDEzOC4wODEgNDEuNjM3NyAxMzguMDgxQzQyLjM5MzIgMTM4LjA4MSA0Mi45ODA1IDEzOC4zMyA0My4zOTk0IDEzOC44MjhDNDMuODE4NCAxMzkuMzI1IDQ0LjAyNzggMTQwLjAzOCA0NC4wMjc4IDE0MC45NjVWMTQxLjM3OUg0MC4wOTA4QzQwLjEwNTEgMTQxLjk1MiA0MC4yNzE2IDE0Mi40MTYgNDAuNTkwMyAxNDIuNzdDNDAuOTEyNiAxNDMuMTIxIDQxLjMyMDggMTQzLjI5NiA0MS44MTQ5IDE0My4yOTZDNDIuMTY1OSAxNDMuMjk2IDQyLjQ2MzEgMTQzLjIyNSA0Mi43MDY1IDE0My4wODJDNDIuOTUgMTQyLjkzOCA0My4xNjMxIDE0Mi43NDkgNDMuMzQ1NyAxNDIuNTEyTDQzLjk1MjYgMTQyLjk4NUM0My40NjU3IDE0My43MzMgNDIuNzM1MiAxNDQuMTA3IDQxLjc2MTIgMTQ0LjEwN1pNNDEuNjM3NyAxMzguODk3QzQxLjIzNjcgMTM4Ljg5NyA0MC45MDAxIDEzOS4wNDQgNDAuNjI3OSAxMzkuMzM4QzQwLjM1NTggMTM5LjYyOCA0MC4xODc1IDE0MC4wMzYgNDAuMTIzIDE0MC41NjJINDMuMDM0MlYxNDAuNDg3QzQzLjAwNTUgMTM5Ljk4MiA0Mi44Njk1IDEzOS41OTIgNDIuNjI2IDEzOS4zMTZDNDIuMzgyNSAxMzkuMDM3IDQyLjA1MzEgMTM4Ljg5NyA0MS42Mzc3IDEzOC44OTdaIiBmaWxsPSJibGFjayIgZmlsbC1vcGFjaXR5PSIwLjg3Ii8+CjxwYXRoIGQ9Ik0xNDYuNDQ1IDU3LjI3NTRIMTQ0LjAwN1Y2NEgxNDIuNjU5VjU3LjI3NTRIMTQwLjI0MlY1Ni4xNzk3SDE0Ni40NDVWNTcuMjc1NFoiIGZpbGw9IiNGRkE1MDAiLz4KPHBhdGggZD0iTTE0OC43MzkgNjRIMTQ3LjQzNFY1OC4xODg1SDE0OC43MzlWNjRaTTE0Ny4zNTMgNTYuNjc5MkMxNDcuMzUzIDU2LjQ3ODcgMTQ3LjQxNiA1Ni4zMTIyIDE0Ny41NDEgNTYuMTc5N0MxNDcuNjcgNTYuMDQ3MiAxNDcuODUzIDU1Ljk4MSAxNDguMDg5IDU1Ljk4MUMxNDguMzI1IDU1Ljk4MSAxNDguNTA4IDU2LjA0NzIgMTQ4LjYzNyA1Ni4xNzk3QzE0OC43NjYgNTYuMzEyMiAxNDguODMgNTYuNDc4NyAxNDguODMgNTYuNjc5MkMxNDguODMgNTYuODc2MSAxNDguNzY2IDU3LjA0MDkgMTQ4LjYzNyA1Ny4xNzMzQzE0OC41MDggNTcuMzAyMiAxNDguMzI1IDU3LjM2NjcgMTQ4LjA4OSA1Ny4zNjY3QzE0Ny44NTMgNTcuMzY2NyAxNDcuNjcgNTcuMzAyMiAxNDcuNTQxIDU3LjE3MzNDMTQ3LjQxNiA1Ny4wNDA5IDE0Ny4zNTMgNTYuODc2MSAxNDcuMzUzIDU2LjY3OTJaIiBmaWxsPSIjRkZBNTAwIi8+CjxwYXRoIGQ9Ik0xNTEuMzkyIDU4LjE4ODVMMTUxLjQzIDU4Ljc5NTRDMTUxLjgzOCA1OC4zMTkyIDE1Mi4zOTYgNTguMDgxMSAxNTMuMTA1IDU4LjA4MTFDMTUzLjg4MiA1OC4wODExIDE1NC40MTQgNTguMzc4MyAxNTQuNzAxIDU4Ljk3MjdDMTU1LjEyMyA1OC4zNzgzIDE1NS43MTggNTguMDgxMSAxNTYuNDg0IDU4LjA4MTFDMTU3LjEyNSA1OC4wODExIDE1Ny42MDEgNTguMjU4MyAxNTcuOTEzIDU4LjYxMjhDMTU4LjIyOCA1OC45NjczIDE1OC4zODkgNTkuNDkwMSAxNTguMzk2IDYwLjE4MTJWNjRIMTU3LjA5MVY2MC4yMTg4QzE1Ny4wOTEgNTkuODQ5OSAxNTcuMDEgNTkuNTc5NiAxNTYuODQ5IDU5LjQwNzdDMTU2LjY4OCA1OS4yMzU4IDE1Ni40MjEgNTkuMTQ5OSAxNTYuMDQ5IDU5LjE0OTlDMTU1Ljc1MiA1OS4xNDk5IDE1NS41MDggNTkuMjMwNSAxNTUuMzE4IDU5LjM5MTZDMTU1LjEzMiA1OS41NDkyIDE1NS4wMDEgNTkuNzU2OCAxNTQuOTI2IDYwLjAxNDZMMTU0LjkzMiA2NEgxNTMuNjI2VjYwLjE3NThDMTUzLjYwOSA1OS40OTE5IDE1My4yNTkgNTkuMTQ5OSAxNTIuNTc5IDU5LjE0OTlDMTUyLjA1NiA1OS4xNDk5IDE1MS42ODYgNTkuMzYzIDE1MS40NjcgNTkuNzg5MVY2NEgxNTAuMTYyVjU4LjE4ODVIMTUxLjM5MloiIGZpbGw9IiNGRkE1MDAiLz4KPHBhdGggZD0iTTE2Mi4yOTUgNjQuMTA3NEMxNjEuNDY4IDY0LjEwNzQgMTYwLjc5NyA2My44NDc4IDE2MC4yODEgNjMuMzI4NkMxNTkuNzY5IDYyLjgwNTggMTU5LjUxMyA2Mi4xMTEyIDE1OS41MTMgNjEuMjQ0NlY2MS4wODM1QzE1OS41MTMgNjAuNTAzNCAxNTkuNjI0IDU5Ljk4NiAxNTkuODQ2IDU5LjUzMTJDMTYwLjA3MiA1OS4wNzI5IDE2MC4zODcgNTguNzE2NiAxNjAuNzkyIDU4LjQ2MjRDMTYxLjE5NiA1OC4yMDgyIDE2MS42NDcgNTguMDgxMSAxNjIuMTQ1IDU4LjA4MTFDMTYyLjkzNiA1OC4wODExIDE2My41NDcgNTguMzMzNSAxNjMuOTc3IDU4LjgzODRDMTY0LjQxIDU5LjM0MzMgMTY0LjYyNiA2MC4wNTc2IDE2NC42MjYgNjAuOTgxNFY2MS41MDc4SDE2MC44MjlDMTYwLjg2OCA2MS45ODc2IDE2MS4wMjggNjIuMzY3MiAxNjEuMzA3IDYyLjY0NjVDMTYxLjU5IDYyLjkyNTggMTYxLjk0NCA2My4wNjU0IDE2Mi4zNzEgNjMuMDY1NEMxNjIuOTY5IDYzLjA2NTQgMTYzLjQ1NiA2Mi44MjM3IDE2My44MzIgNjIuMzQwM0wxNjQuNTM1IDYzLjAxMTdDMTY0LjMwMiA2My4zNTkgMTYzLjk5MSA2My42Mjk0IDE2My42MDEgNjMuODIyOEMxNjMuMjE0IDY0LjAxMjUgMTYyLjc3OSA2NC4xMDc0IDE2Mi4yOTUgNjQuMTA3NFpNMTYyLjE0IDU5LjEyODRDMTYxLjc4MiA1OS4xMjg0IDE2MS40OTIgNTkuMjUzNyAxNjEuMjcgNTkuNTA0NEMxNjEuMDUxIDU5Ljc1NSAxNjAuOTExIDYwLjEwNDIgMTYwLjg1MSA2MC41NTE4SDE2My4zMzdWNjAuNDU1MUMxNjMuMzA5IDYwLjAxODIgMTYzLjE5MiA1OS42ODg4IDE2Mi45ODggNTkuNDY2OEMxNjIuNzg0IDU5LjI0MTIgMTYyLjUwMSA1OS4xMjg0IDE2Mi4xNCA1OS4xMjg0WiIgZmlsbD0iI0ZGQTUwMCIvPgo8cGF0aCBkPSJNMTY1LjQgNjEuMDQwNUMxNjUuNCA2MC40NzEyIDE2NS41MTMgNTkuOTU5MSAxNjUuNzM4IDU5LjUwNDRDMTY1Ljk2NCA1OS4wNDYxIDE2Ni4yODEgNTguNjk1MSAxNjYuNjg5IDU4LjQ1MTdDMTY3LjA5NyA1OC4yMDQ2IDE2Ny41NjYgNTguMDgxMSAxNjguMDk2IDU4LjA4MTFDMTY4Ljg4IDU4LjA4MTEgMTY5LjUxNiA1OC4zMzM1IDE3MC4wMDMgNTguODM4NEMxNzAuNDkzIDU5LjM0MzMgMTcwLjc1OCA2MC4wMTI5IDE3MC43OTggNjAuODQ3MkwxNzAuODAzIDYxLjE1MzNDMTcwLjgwMyA2MS43MjYyIDE3MC42OTIgNjIuMjM4MyAxNzAuNDcgNjIuNjg5NUMxNzAuMjUyIDYzLjE0MDYgMTY5LjkzNyA2My40ODk3IDE2OS41MjUgNjMuNzM2OEMxNjkuMTE3IDYzLjk4MzkgMTY4LjY0NCA2NC4xMDc0IDE2OC4xMDcgNjQuMTA3NEMxNjcuMjg3IDY0LjEwNzQgMTY2LjYzIDYzLjgzNTMgMTY2LjEzNiA2My4yOTFDMTY1LjY0NSA2Mi43NDMyIDE2NS40IDYyLjAxNDUgMTY1LjQgNjEuMTA1VjYxLjA0MDVaTTE2Ni43MDUgNjEuMTUzM0MxNjYuNzA1IDYxLjc1MTMgMTY2LjgyOSA2Mi4yMjA0IDE2Ny4wNzYgNjIuNTYwNUMxNjcuMzIzIDYyLjg5NzEgMTY3LjY2NyA2My4wNjU0IDE2OC4xMDcgNjMuMDY1NEMxNjguNTQ3IDYzLjA2NTQgMTY4Ljg4OSA2Mi44OTM2IDE2OS4xMzMgNjIuNTQ5OEMxNjkuMzggNjIuMjA2MSAxNjkuNTAzIDYxLjcwMyAxNjkuNTAzIDYxLjA0MDVDMTY5LjUwMyA2MC40NTMzIDE2OS4zNzYgNTkuOTg3OCAxNjkuMTIyIDU5LjY0NEMxNjguODcxIDU5LjMwMDMgMTY4LjUyOSA1OS4xMjg0IDE2OC4wOTYgNTkuMTI4NEMxNjcuNjcgNTkuMTI4NCAxNjcuMzMyIDU5LjI5ODUgMTY3LjA4MSA1OS42Mzg3QzE2Ni44MyA1OS45NzUzIDE2Ni43MDUgNjAuNDgwMSAxNjYuNzA1IDYxLjE1MzNaIiBmaWxsPSIjRkZBNTAwIi8+CjxwYXRoIGQ9Ik0xNzUuNDI4IDYzLjQzMDdDMTc1LjA0NSA2My44ODE4IDE3NC41IDY0LjEwNzQgMTczLjc5NSA2NC4xMDc0QzE3My4xNjUgNjQuMTA3NCAxNzIuNjg3IDYzLjkyMyAxNzIuMzYxIDYzLjU1NDJDMTcyLjAzOSA2My4xODU0IDE3MS44NzcgNjIuNjUxOSAxNzEuODc3IDYxLjk1MzZWNTguMTg4NUgxNzMuMTgzVjYxLjkzNzVDMTczLjE4MyA2Mi42NzUxIDE3My40ODkgNjMuMDQzOSAxNzQuMTAxIDYzLjA0MzlDMTc0LjczNSA2My4wNDM5IDE3NS4xNjMgNjIuODE2NiAxNzUuMzg1IDYyLjM2MThWNTguMTg4NUgxNzYuNjlWNjRIMTc1LjQ2TDE3NS40MjggNjMuNDMwN1oiIGZpbGw9IiNGRkE1MDAiLz4KPHBhdGggZD0iTTE3OS42NTUgNTYuNzc1OVY1OC4xODg1SDE4MC42ODFWNTkuMTU1M0gxNzkuNjU1VjYyLjM5OTRDMTc5LjY1NSA2Mi42MjE0IDE3OS42OTggNjIuNzgyNiAxNzkuNzg0IDYyLjg4MjhDMTc5Ljg3MyA2Mi45Nzk1IDE4MC4wMzEgNjMuMDI3OCAxODAuMjU2IDYzLjAyNzhDMTgwLjQwNyA2My4wMjc4IDE4MC41NTkgNjMuMDA5OSAxODAuNzEzIDYyLjk3NDFWNjMuOTgzOUMxODAuNDE2IDY0LjA2NjIgMTgwLjEyOSA2NC4xMDc0IDE3OS44NTQgNjQuMTA3NEMxNzguODUxIDY0LjEwNzQgMTc4LjM1IDYzLjU1NDIgMTc4LjM1IDYyLjQ0NzhWNTkuMTU1M0gxNzcuMzk0VjU4LjE4ODVIMTc4LjM1VjU2Ljc3NTlIMTc5LjY1NVoiIGZpbGw9IiNGRkE1MDAiLz4KPHBhdGggZD0iTTE0NS4zMDEgMTAwLjY4NkgxNDIuMTU0VjEwNEgxNDAuNzk1Vjk2LjE3OTdIMTQ1Ljc2M1Y5Ny4yNzU0SDE0Mi4xNTRWOTkuNjAxMUgxNDUuMzAxVjEwMC42ODZaIiBmaWxsPSIjRkYzRTNFIi8+CjxwYXRoIGQ9Ik0xNTAuMDA2IDEwNEMxNDkuOTQ5IDEwMy44ODkgMTQ5Ljg5OSAxMDMuNzA4IDE0OS44NTYgMTAzLjQ1OEMxNDkuNDQxIDEwMy44OTEgMTQ4LjkzMiAxMDQuMTA3IDE0OC4zMzEgMTA0LjEwN0MxNDcuNzQ3IDEwNC4xMDcgMTQ3LjI3MSAxMDMuOTQxIDE0Ni45MDIgMTAzLjYwOEMxNDYuNTMzIDEwMy4yNzUgMTQ2LjM0OSAxMDIuODYzIDE0Ni4zNDkgMTAyLjM3M0MxNDYuMzQ5IDEwMS43NTMgMTQ2LjU3OCAxMDEuMjc5IDE0Ny4wMzYgMTAwLjk0OUMxNDcuNDk4IDEwMC42MTYgMTQ4LjE1NyAxMDAuNDUgMTQ5LjAxMyAxMDAuNDVIMTQ5LjgxM1YxMDAuMDY4QzE0OS44MTMgOTkuNzY3NiAxNDkuNzI5IDk5LjUyNzcgMTQ5LjU2MSA5OS4zNDg2QzE0OS4zOTIgOTkuMTY2IDE0OS4xMzYgOTkuMDc0NyAxNDguNzkyIDk5LjA3NDdDMTQ4LjQ5NSA5OS4wNzQ3IDE0OC4yNTIgOTkuMTQ5OSAxNDguMDYyIDk5LjMwMDNDMTQ3Ljg3MiA5OS40NDcxIDE0Ny43NzcgOTkuNjM1MSAxNDcuNzc3IDk5Ljg2NDNIMTQ2LjQ3MkMxNDYuNDcyIDk5LjU0NTYgMTQ2LjU3OCA5OS4yNDg0IDE0Ni43ODkgOTguOTcyN0MxNDcgOTguNjkzNCAxNDcuMjg3IDk4LjQ3NDkgMTQ3LjY0OCA5OC4zMTc0QzE0OC4wMTQgOTguMTU5OCAxNDguNDIgOTguMDgxMSAxNDguODY4IDk4LjA4MTFDMTQ5LjU0OCA5OC4wODExIDE1MC4wOSA5OC4yNTI5IDE1MC40OTUgOTguNTk2N0MxNTAuOSA5OC45MzY4IDE1MS4xMDcgOTkuNDE2NyAxNTEuMTE4IDEwMC4wMzZWMTAyLjY1N0MxNTEuMTE4IDEwMy4xOCAxNTEuMTkyIDEwMy41OTcgMTUxLjMzOCAxMDMuOTA5VjEwNEgxNTAuMDA2Wk0xNDguNTcyIDEwMy4wNkMxNDguODMgMTAzLjA2IDE0OS4wNzIgMTAyLjk5NyAxNDkuMjk3IDEwMi44NzJDMTQ5LjUyNyAxMDIuNzQ3IDE0OS42OTggMTAyLjU3OCAxNDkuODEzIDEwMi4zNjdWMTAxLjI3MUgxNDkuMTA5QzE0OC42MjYgMTAxLjI3MSAxNDguMjYzIDEwMS4zNTYgMTQ4LjAxOSAxMDEuNTI0QzE0Ny43NzYgMTAxLjY5MiAxNDcuNjU0IDEwMS45MyAxNDcuNjU0IDEwMi4yMzhDMTQ3LjY1NCAxMDIuNDg5IDE0Ny43MzYgMTAyLjY4OSAxNDcuOTAxIDEwMi44NEMxNDguMDY5IDEwMi45ODcgMTQ4LjI5MyAxMDMuMDYgMTQ4LjU3MiAxMDMuMDZaIiBmaWxsPSIjRkYzRTNFIi8+CjxwYXRoIGQ9Ik0xNTMuODc0IDEwNEgxNTIuNTY4Vjk4LjE4ODVIMTUzLjg3NFYxMDRaTTE1Mi40ODggOTYuNjc5MkMxNTIuNDg4IDk2LjQ3ODcgMTUyLjU1IDk2LjMxMjIgMTUyLjY3NiA5Ni4xNzk3QzE1Mi44MDUgOTYuMDQ3MiAxNTIuOTg3IDk1Ljk4MSAxNTMuMjI0IDk1Ljk4MUMxNTMuNDYgOTUuOTgxIDE1My42NDMgOTYuMDQ3MiAxNTMuNzcxIDk2LjE3OTdDMTUzLjkgOTYuMzEyMiAxNTMuOTY1IDk2LjQ3ODcgMTUzLjk2NSA5Ni42NzkyQzE1My45NjUgOTYuODc2MSAxNTMuOSA5Ny4wNDA5IDE1My43NzEgOTcuMTczM0MxNTMuNjQzIDk3LjMwMjIgMTUzLjQ2IDk3LjM2NjcgMTUzLjIyNCA5Ny4zNjY3QzE1Mi45ODcgOTcuMzY2NyAxNTIuODA1IDk3LjMwMjIgMTUyLjY3NiA5Ny4xNzMzQzE1Mi41NSA5Ny4wNDA5IDE1Mi40ODggOTYuODc2MSAxNTIuNDg4IDk2LjY3OTJaIiBmaWxsPSIjRkYzRTNFIi8+CjxwYXRoIGQ9Ik0xNTYuNjg4IDEwNEgxNTUuMzgzVjk1Ljc1SDE1Ni42ODhWMTA0WiIgZmlsbD0iI0ZGM0UzRSIvPgo8cGF0aCBkPSJNMTYwLjY3MyAxMDQuMTA3QzE1OS44NDYgMTA0LjEwNyAxNTkuMTc1IDEwMy44NDggMTU4LjY1OSAxMDMuMzI5QzE1OC4xNDcgMTAyLjgwNiAxNTcuODkxIDEwMi4xMTEgMTU3Ljg5MSAxMDEuMjQ1VjEwMS4wODNDMTU3Ljg5MSAxMDAuNTAzIDE1OC4wMDIgOTkuOTg2IDE1OC4yMjQgOTkuNTMxMkMxNTguNDUgOTkuMDcyOSAxNTguNzY1IDk4LjcxNjYgMTU5LjE2OSA5OC40NjI0QzE1OS41NzQgOTguMjA4MiAxNjAuMDI1IDk4LjA4MTEgMTYwLjUyMyA5OC4wODExQzE2MS4zMTQgOTguMDgxMSAxNjEuOTI1IDk4LjMzMzUgMTYyLjM1NCA5OC44Mzg0QzE2Mi43ODggOTkuMzQzMyAxNjMuMDA0IDEwMC4wNTggMTYzLjAwNCAxMDAuOTgxVjEwMS41MDhIMTU5LjIwN0MxNTkuMjQ2IDEwMS45ODggMTU5LjQwNiAxMDIuMzY3IDE1OS42ODUgMTAyLjY0NkMxNTkuOTY4IDEwMi45MjYgMTYwLjMyMiAxMDMuMDY1IDE2MC43NDkgMTAzLjA2NUMxNjEuMzQ3IDEwMy4wNjUgMTYxLjgzMyAxMDIuODI0IDE2Mi4yMDkgMTAyLjM0TDE2Mi45MTMgMTAzLjAxMkMxNjIuNjggMTAzLjM1OSAxNjIuMzY5IDEwMy42MjkgMTYxLjk3OSAxMDMuODIzQzE2MS41OTIgMTA0LjAxMyAxNjEuMTU3IDEwNC4xMDcgMTYwLjY3MyAxMDQuMTA3Wk0xNjAuNTE4IDk5LjEyODRDMTYwLjE2IDk5LjEyODQgMTU5Ljg2OSA5OS4yNTM3IDE1OS42NDcgOTkuNTA0NEMxNTkuNDI5IDk5Ljc1NSAxNTkuMjg5IDEwMC4xMDQgMTU5LjIyOSAxMDAuNTUySDE2MS43MTVWMTAwLjQ1NUMxNjEuNjg3IDEwMC4wMTggMTYxLjU3IDk5LjY4ODggMTYxLjM2NiA5OS40NjY4QzE2MS4xNjIgOTkuMjQxMiAxNjAuODc5IDk5LjEyODQgMTYwLjUxOCA5OS4xMjg0WiIgZmlsbD0iI0ZGM0UzRSIvPgo8cGF0aCBkPSJNMTYzLjc3OCAxMDEuMDUxQzE2My43NzggMTAwLjE1NiAxNjMuOTg2IDk5LjQzODIgMTY0LjQwMSA5OC44OTc1QzE2NC44MTYgOTguMzUzMiAxNjUuMzczIDk4LjA4MTEgMTY2LjA3MSA5OC4wODExQzE2Ni42ODcgOTguMDgxMSAxNjcuMTg1IDk4LjI5NTkgMTY3LjU2NCA5OC43MjU2Vjk1Ljc1SDE2OC44N1YxMDRIMTY3LjY4OEwxNjcuNjI0IDEwMy4zOThDMTY3LjIzMyAxMDMuODcxIDE2Ni43MTIgMTA0LjEwNyAxNjYuMDYxIDEwNC4xMDdDMTY1LjM4IDEwNC4xMDcgMTY0LjgyOSAxMDMuODMzIDE2NC40MDYgMTAzLjI4NkMxNjMuOTg3IDEwMi43MzggMTYzLjc3OCAxMDEuOTkzIDE2My43NzggMTAxLjA1MVpNMTY1LjA4MyAxMDEuMTY0QzE2NS4wODMgMTAxLjc1NSAxNjUuMTk2IDEwMi4yMTcgMTY1LjQyMSAxMDIuNTVDMTY1LjY1MSAxMDIuODc5IDE2NS45NzUgMTAzLjA0NCAxNjYuMzk0IDEwMy4wNDRDMTY2LjkyNyAxMDMuMDQ0IDE2Ny4zMTcgMTAyLjgwNiAxNjcuNTY0IDEwMi4zM1Y5OS44NDgxQzE2Ny4zMjUgOTkuMzgyNiAxNjYuOTM4IDk5LjE0OTkgMTY2LjQwNCA5OS4xNDk5QzE2NS45ODIgOTkuMTQ5OSAxNjUuNjU2IDk5LjMxODIgMTY1LjQyNyA5OS42NTQ4QzE2NS4xOTggOTkuOTg3OCAxNjUuMDgzIDEwMC40OTEgMTY1LjA4MyAxMDEuMTY0WiIgZmlsbD0iI0ZGM0UzRSIvPgo8cGF0aCBkPSJNMTQwLjc5NSAxNDRWMTM2LjE4SDE0My4xMDRDMTQzLjc5NiAxMzYuMTggMTQ0LjQwOCAxMzYuMzM0IDE0NC45NDEgMTM2LjY0MkMxNDUuNDc5IDEzNi45NSAxNDUuODk0IDEzNy4zODYgMTQ2LjE4OCAxMzcuOTUyQzE0Ni40ODEgMTM4LjUxOCAxNDYuNjI4IDEzOS4xNjYgMTQ2LjYyOCAxMzkuODk2VjE0MC4yODlDMTQ2LjYyOCAxNDEuMDMgMTQ2LjQ3OSAxNDEuNjgxIDE0Ni4xODIgMTQyLjI0NEMxNDUuODg5IDE0Mi44MDYgMTQ1LjQ2OCAxNDMuMjM5IDE0NC45MiAxNDMuNTQzQzE0NC4zNzYgMTQzLjg0OCAxNDMuNzUxIDE0NCAxNDMuMDQ1IDE0NEgxNDAuNzk1Wk0xNDIuMTU0IDEzNy4yNzVWMTQyLjkxNUgxNDMuMDRDMTQzLjc1MyAxNDIuOTE1IDE0NC4yOTkgMTQyLjY5MyAxNDQuNjc4IDE0Mi4yNDlDMTQ1LjA2MSAxNDEuODAxIDE0NS4yNTcgMTQxLjE2IDE0NS4yNjQgMTQwLjMyNlYxMzkuODkxQzE0NS4yNjQgMTM5LjA0MiAxNDUuMDc5IDEzOC4zOTQgMTQ0LjcxIDEzNy45NDdDMTQ0LjM0MiAxMzcuNDk5IDE0My44MDYgMTM3LjI3NSAxNDMuMTA0IDEzNy4yNzVIMTQyLjE1NFoiIGZpbGw9IiM0Q0FGNTAiLz4KPHBhdGggZD0iTTE1MC40MTUgMTQ0LjEwN0MxNDkuNTg3IDE0NC4xMDcgMTQ4LjkxNiAxNDMuODQ4IDE0OC40IDE0My4zMjlDMTQ3Ljg4OCAxNDIuODA2IDE0Ny42MzIgMTQyLjExMSAxNDcuNjMyIDE0MS4yNDVWMTQxLjA4M0MxNDcuNjMyIDE0MC41MDMgMTQ3Ljc0MyAxMzkuOTg2IDE0Ny45NjUgMTM5LjUzMUMxNDguMTkxIDEzOS4wNzMgMTQ4LjUwNiAxMzguNzE3IDE0OC45MTEgMTM4LjQ2MkMxNDkuMzE1IDEzOC4yMDggMTQ5Ljc2NiAxMzguMDgxIDE1MC4yNjQgMTM4LjA4MUMxNTEuMDU2IDEzOC4wODEgMTUxLjY2NiAxMzguMzMzIDE1Mi4wOTYgMTM4LjgzOEMxNTIuNTI5IDEzOS4zNDMgMTUyLjc0NiAxNDAuMDU4IDE1Mi43NDYgMTQwLjk4MVYxNDEuNTA4SDE0OC45NDhDMTQ4Ljk4OCAxNDEuOTg4IDE0OS4xNDcgMTQyLjM2NyAxNDkuNDI2IDE0Mi42NDZDMTQ5LjcwOSAxNDIuOTI2IDE1MC4wNjQgMTQzLjA2NSAxNTAuNDkgMTQzLjA2NUMxNTEuMDg4IDE0My4wNjUgMTUxLjU3NSAxNDIuODI0IDE1MS45NTEgMTQyLjM0TDE1Mi42NTQgMTQzLjAxMkMxNTIuNDIyIDE0My4zNTkgMTUyLjExIDE0My42MjkgMTUxLjcyIDE0My44MjNDMTUxLjMzMyAxNDQuMDEzIDE1MC44OTggMTQ0LjEwNyAxNTAuNDE1IDE0NC4xMDdaTTE1MC4yNTkgMTM5LjEyOEMxNDkuOTAxIDEzOS4xMjggMTQ5LjYxMSAxMzkuMjU0IDE0OS4zODkgMTM5LjUwNEMxNDkuMTcgMTM5Ljc1NSAxNDkuMDMxIDE0MC4xMDQgMTQ4Ljk3IDE0MC41NTJIMTUxLjQ1N1YxNDAuNDU1QzE1MS40MjggMTQwLjAxOCAxNTEuMzEyIDEzOS42ODkgMTUxLjEwNyAxMzkuNDY3QzE1MC45MDMgMTM5LjI0MSAxNTAuNjIgMTM5LjEyOCAxNTAuMjU5IDEzOS4xMjhaIiBmaWxsPSIjNENBRjUwIi8+CjxwYXRoIGQ9Ik0xNTUuMTUyIDE0NEgxNTMuODQ3VjEzNS43NUgxNTUuMTUyVjE0NFoiIGZpbGw9IiM0Q0FGNTAiLz4KPHBhdGggZD0iTTE1Ny45NjYgMTQ0SDE1Ni42NjFWMTM4LjE4OEgxNTcuOTY2VjE0NFpNMTU2LjU4MSAxMzYuNjc5QzE1Ni41ODEgMTM2LjQ3OSAxNTYuNjQzIDEzNi4zMTIgMTU2Ljc2OSAxMzYuMThDMTU2Ljg5NyAxMzYuMDQ3IDE1Ny4wOCAxMzUuOTgxIDE1Ny4zMTYgMTM1Ljk4MUMxNTcuNTUzIDEzNS45ODEgMTU3LjczNSAxMzYuMDQ3IDE1Ny44NjQgMTM2LjE4QzE1Ny45OTMgMTM2LjMxMiAxNTguMDU4IDEzNi40NzkgMTU4LjA1OCAxMzYuNjc5QzE1OC4wNTggMTM2Ljg3NiAxNTcuOTkzIDEzNy4wNDEgMTU3Ljg2NCAxMzcuMTczQzE1Ny43MzUgMTM3LjMwMiAxNTcuNTUzIDEzNy4zNjcgMTU3LjMxNiAxMzcuMzY3QzE1Ny4wOCAxMzcuMzY3IDE1Ni44OTcgMTM3LjMwMiAxNTYuNzY5IDEzNy4xNzNDMTU2LjY0MyAxMzcuMDQxIDE1Ni41ODEgMTM2Ljg3NiAxNTYuNTgxIDEzNi42NzlaIiBmaWxsPSIjNENBRjUwIi8+CjxwYXRoIGQ9Ik0xNjEuNDQxIDE0Mi4zNDZMMTYyLjY3MSAxMzguMTg4SDE2NC4wMkwxNjIuMDA1IDE0NEgxNjAuODcyTDE1OC44NDIgMTM4LjE4OEgxNjAuMTk1TDE2MS40NDEgMTQyLjM0NloiIGZpbGw9IiM0Q0FGNTAiLz4KPHBhdGggZD0iTTE2Ny4zMjMgMTQ0LjEwN0MxNjYuNDk2IDE0NC4xMDcgMTY1LjgyNCAxNDMuODQ4IDE2NS4zMDkgMTQzLjMyOUMxNjQuNzk3IDE0Mi44MDYgMTY0LjU0MSAxNDIuMTExIDE2NC41NDEgMTQxLjI0NVYxNDEuMDgzQzE2NC41NDEgMTQwLjUwMyAxNjQuNjUyIDEzOS45ODYgMTY0Ljg3NCAxMzkuNTMxQzE2NS4wOTkgMTM5LjA3MyAxNjUuNDE0IDEzOC43MTcgMTY1LjgxOSAxMzguNDYyQzE2Ni4yMjMgMTM4LjIwOCAxNjYuNjc1IDEzOC4wODEgMTY3LjE3MiAxMzguMDgxQzE2Ny45NjQgMTM4LjA4MSAxNjguNTc0IDEzOC4zMzMgMTY5LjAwNCAxMzguODM4QzE2OS40MzcgMTM5LjM0MyAxNjkuNjU0IDE0MC4wNTggMTY5LjY1NCAxNDAuOTgxVjE0MS41MDhIMTY1Ljg1NkMxNjUuODk2IDE0MS45ODggMTY2LjA1NSAxNDIuMzY3IDE2Ni4zMzQgMTQyLjY0NkMxNjYuNjE3IDE0Mi45MjYgMTY2Ljk3MiAxNDMuMDY1IDE2Ny4zOTggMTQzLjA2NUMxNjcuOTk2IDE0My4wNjUgMTY4LjQ4MyAxNDIuODI0IDE2OC44NTkgMTQyLjM0TDE2OS41NjIgMTQzLjAxMkMxNjkuMzMgMTQzLjM1OSAxNjkuMDE4IDE0My42MjkgMTY4LjYyOCAxNDMuODIzQzE2OC4yNDEgMTQ0LjAxMyAxNjcuODA2IDE0NC4xMDcgMTY3LjMyMyAxNDQuMTA3Wk0xNjcuMTY3IDEzOS4xMjhDMTY2LjgwOSAxMzkuMTI4IDE2Ni41MTkgMTM5LjI1NCAxNjYuMjk3IDEzOS41MDRDMTY2LjA3OCAxMzkuNzU1IDE2NS45MzkgMTQwLjEwNCAxNjUuODc4IDE0MC41NTJIMTY4LjM2NVYxNDAuNDU1QzE2OC4zMzYgMTQwLjAxOCAxNjguMjIgMTM5LjY4OSAxNjguMDE2IDEzOS40NjdDMTY3LjgxMiAxMzkuMjQxIDE2Ny41MjkgMTM5LjEyOCAxNjcuMTY3IDEzOS4xMjhaIiBmaWxsPSIjNENBRjUwIi8+CjxwYXRoIGQ9Ik0xNzMuNzE0IDEzOS4zODFDMTczLjU0MiAxMzkuMzUyIDE3My4zNjUgMTM5LjMzOCAxNzMuMTgzIDEzOS4zMzhDMTcyLjU4NSAxMzkuMzM4IDE3Mi4xODIgMTM5LjU2NyAxNzEuOTc0IDE0MC4wMjVWMTQ0SDE3MC42NjlWMTM4LjE4OEgxNzEuOTE1TDE3MS45NDcgMTM4LjgzOEMxNzIuMjYyIDEzOC4zMzMgMTcyLjY5OSAxMzguMDgxIDE3My4yNTggMTM4LjA4MUMxNzMuNDQ0IDEzOC4wODEgMTczLjU5OCAxMzguMTA2IDE3My43MiAxMzguMTU2TDE3My43MTQgMTM5LjM4MVoiIGZpbGw9IiM0Q0FGNTAiLz4KPHBhdGggZD0iTTE3Ni45OTEgMTQ0LjEwN0MxNzYuMTY0IDE0NC4xMDcgMTc1LjQ5MiAxNDMuODQ4IDE3NC45NzcgMTQzLjMyOUMxNzQuNDY1IDE0Mi44MDYgMTc0LjIwOCAxNDIuMTExIDE3NC4yMDggMTQxLjI0NVYxNDEuMDgzQzE3NC4yMDggMTQwLjUwMyAxNzQuMzE5IDEzOS45ODYgMTc0LjU0MiAxMzkuNTMxQzE3NC43NjcgMTM5LjA3MyAxNzUuMDgyIDEzOC43MTcgMTc1LjQ4NyAxMzguNDYyQzE3NS44OTEgMTM4LjIwOCAxNzYuMzQzIDEzOC4wODEgMTc2Ljg0IDEzOC4wODFDMTc3LjYzMiAxMzguMDgxIDE3OC4yNDIgMTM4LjMzMyAxNzguNjcyIDEzOC44MzhDMTc5LjEwNSAxMzkuMzQzIDE3OS4zMjIgMTQwLjA1OCAxNzkuMzIyIDE0MC45ODFWMTQxLjUwOEgxNzUuNTI0QzE3NS41NjQgMTQxLjk4OCAxNzUuNzIzIDE0Mi4zNjcgMTc2LjAwMiAxNDIuNjQ2QzE3Ni4yODUgMTQyLjkyNiAxNzYuNjQgMTQzLjA2NSAxNzcuMDY2IDE0My4wNjVDMTc3LjY2NCAxNDMuMDY1IDE3OC4xNTEgMTQyLjgyNCAxNzguNTI3IDE0Mi4zNEwxNzkuMjMgMTQzLjAxMkMxNzguOTk4IDE0My4zNTkgMTc4LjY4NiAxNDMuNjI5IDE3OC4yOTYgMTQzLjgyM0MxNzcuOTA5IDE0NC4wMTMgMTc3LjQ3NCAxNDQuMTA3IDE3Ni45OTEgMTQ0LjEwN1pNMTc2LjgzNSAxMzkuMTI4QzE3Ni40NzcgMTM5LjEyOCAxNzYuMTg3IDEzOS4yNTQgMTc1Ljk2NSAxMzkuNTA0QzE3NS43NDYgMTM5Ljc1NSAxNzUuNjA3IDE0MC4xMDQgMTc1LjU0NiAxNDAuNTUySDE3OC4wMzNWMTQwLjQ1NUMxNzguMDA0IDE0MC4wMTggMTc3Ljg4OCAxMzkuNjg5IDE3Ny42ODQgMTM5LjQ2N0MxNzcuNDc5IDEzOS4yNDEgMTc3LjE5NyAxMzkuMTI4IDE3Ni44MzUgMTM5LjEyOFoiIGZpbGw9IiM0Q0FGNTAiLz4KPHBhdGggZD0iTTE4MC4wOTUgMTQxLjA1MUMxODAuMDk1IDE0MC4xNTYgMTgwLjMwMyAxMzkuNDM4IDE4MC43MTggMTM4Ljg5N0MxODEuMTM0IDEzOC4zNTMgMTgxLjY5IDEzOC4wODEgMTgyLjM4OSAxMzguMDgxQzE4My4wMDUgMTM4LjA4MSAxODMuNTAyIDEzOC4yOTYgMTgzLjg4MiAxMzguNzI2VjEzNS43NUgxODUuMTg3VjE0NEgxODQuMDA1TDE4My45NDEgMTQzLjM5OEMxODMuNTUxIDE0My44NzEgMTgzLjAzIDE0NC4xMDcgMTgyLjM3OCAxNDQuMTA3QzE4MS42OTggMTQ0LjEwNyAxODEuMTQ2IDE0My44MzMgMTgwLjcyNCAxNDMuMjg2QzE4MC4zMDUgMTQyLjczOCAxODAuMDk1IDE0MS45OTMgMTgwLjA5NSAxNDEuMDUxWk0xODEuNCAxNDEuMTY0QzE4MS40IDE0MS43NTUgMTgxLjUxMyAxNDIuMjE3IDE4MS43MzkgMTQyLjU1QzE4MS45NjggMTQyLjg3OSAxODIuMjkyIDE0My4wNDQgMTgyLjcxMSAxNDMuMDQ0QzE4My4yNDQgMTQzLjA0NCAxODMuNjM1IDE0Mi44MDYgMTgzLjg4MiAxNDIuMzNWMTM5Ljg0OEMxODMuNjQyIDEzOS4zODMgMTgzLjI1NSAxMzkuMTUgMTgyLjcyMiAxMzkuMTVDMTgyLjI5OSAxMzkuMTUgMTgxLjk3MyAxMzkuMzE4IDE4MS43NDQgMTM5LjY1NUMxODEuNTE1IDEzOS45ODggMTgxLjQgMTQwLjQ5MSAxODEuNCAxNDEuMTY0WiIgZmlsbD0iIzRDQUY1MCIvPgo8cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0iTTE5NiAxSDRDMi4zNDMxNCAxIDEgMi4zNDMxNCAxIDRWMTU2QzEgMTU3LjY1NyAyLjM0MzE0IDE1OSA0IDE1OUgxOTZDMTk3LjY1NyAxNTkgMTk5IDE1Ny42NTcgMTk5IDE1NlY0QzE5OSAyLjM0MzE1IDE5Ny42NTcgMSAxOTYgMVpNNCAwSDE5NkMxOTguMjA5IDAgMjAwIDEuNzkwODYgMjAwIDRWMTU2QzIwMCAxNTguMjA5IDE5OC4yMDkgMTYwIDE5NiAxNjBINEMxLjc5MDg2IDE2MCAwIDE1OC4yMDkgMCAxNTZWNEMwIDEuNzkwODYgMS43OTA4NiAwIDQgMFoiIGZpbGw9IiNFMEUwRTAiLz4KPC9zdmc+Cg==", + "description": "Displays Persistent RPC requests that match selected alias and filter with the ability of pagination and sending persistent RPC requests.", + "descriptor": { + "type": "rpc", + "sizeX": 7.5, + "sizeY": 4, + "resources": [], + "templateHtml": "", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n}\n\nself.onResize = function() {\n}\n\nself.onDestroy = function() {\n}\n", + "settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"PersistentTableSettings\",\n \"properties\": {\n \"enableStickyHeader\": {\n \"title\": \"Always display header\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"enableFilter\": {\n \"title\": \"Enable filter\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"allowSendRequest\": {\n \"title\": \"Allow send RPC request\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"enableStickyAction\": {\n \"title\": \"Always display actions column\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"displayDetails\": {\n \"title\": \"Display request details\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"allowDelete\": {\n \"title\": \"Allow delete request\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"displayPagination\": {\n \"title\": \"Display pagination\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"defaultPageSize\": {\n \"title\": \"Default page size\",\n \"type\": \"number\",\n \"default\": 10\n },\n \"defaultSortOrder\": {\n \"title\": \"Default sort order\",\n \"type\": \"string\",\n \"default\": \"-createdTime\"\n },\n \"displayColumns\": {\n \"title\": \"Columns for display\",\n \"type\": \"array\",\n \"minItems\": 1\n }\n },\n \"required\": [\"displayColumns\"]\n },\n \"uiSchema\": {\n \"type\": \"VerticalLayout\",\n \"elements\": [\n {\n \"type\": \"Control\",\n \"scope\": \"#/schema/properties/enableStickyHeader\"\n },\n {\n \"type\": \"Control\",\n \"scope\": \"#/schema/properties/enableFilter\"\n }\n ]\n },\n \"form\": [\n [\n \"enableStickyHeader\",\n \"enableFilter\",\n \"allowSendRequest\",\n \"enableStickyAction\",\n \"displayDetails\",\n \"allowDelete\",\n \"displayPagination\",\n \"defaultPageSize\",\n \"defaultSortOrder\"\n ],\n [\n {\n \"key\": \"displayColumns\",\n \"type\": \"rc-select\",\n \"multiple\": true,\n \"default\": [\"rpcId\", \"messageType\", \"status\", \"method\", \"createdTime\", \"expirationTime\"],\n \"items\": [\n {\n \"value\": \"rpcId\",\n \"label\": \"RPC ID\"\n },\n {\n \"value\": \"messageType\",\n \"label\": \"Message type\"\n },\n {\n \"value\": \"status\",\n \"label\": \"Status\"\n },\n {\n \"value\": \"method\",\n \"label\": \"Method\"\n },\n {\n \"value\": \"createdTime\",\n \"label\": \"Created time\"\n },\n {\n \"value\": \"expirationTime\",\n \"label\": \"Expiration time\"\n }\n ]\n }\n ]\n ],\n \"groupInfoes\": [{\n \"formIndex\": 0,\n \"GroupTitle\": \"General settings\"\n }, {\n \"formIndex\": 1,\n \"GroupTitle\": \"Columns settings\"\n }]\n}", + "dataKeySettingsSchema": "{}\n", + "defaultConfig": "{\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"enableStickyAction\":true,\"enableFilter\":true,\"displayPagination\":true,\"defaultPageSize\":10,\"enableStickyHeader\":true,\"displayColumns\":[\"rpcId\",\"messageType\",\"status\",\"method\",\"createdTime\",\"expirationTime\"],\"displayDetails\":true,\"defaultSortOrder\":\"-createdTime\",\"allowSendRequest\":true,\"allowDelete\":true},\"title\":\"Persistent table\",\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400,\"padding\":\"5px 10px\"},\"targetDeviceAliasIds\":[]}" + } } ] } \ No newline at end of file diff --git a/application/src/main/java/org/thingsboard/server/controller/RpcV2Controller.java b/application/src/main/java/org/thingsboard/server/controller/RpcV2Controller.java index 389a767cdf..844795593c 100644 --- a/application/src/main/java/org/thingsboard/server/controller/RpcV2Controller.java +++ b/application/src/main/java/org/thingsboard/server/controller/RpcV2Controller.java @@ -177,8 +177,8 @@ public class RpcV2Controller extends AbstractRpcController { @RequestParam int pageSize, @ApiParam(value = PAGE_NUMBER_DESCRIPTION, required = true) @RequestParam int page, - @ApiParam(value = "Status of the RPC", required = true, allowableValues = RPC_STATUS_ALLOWABLE_VALUES) - @RequestParam RpcStatus rpcStatus, + @ApiParam(value = "Status of the RPC", allowableValues = RPC_STATUS_ALLOWABLE_VALUES) + @RequestParam(required = false) RpcStatus rpcStatus, @ApiParam(value = RPC_TEXT_SEARCH_DESCRIPTION) @RequestParam(required = false) String textSearch, @ApiParam(value = SORT_PROPERTY_DESCRIPTION, allowableValues = RPC_SORT_PROPERTY_ALLOWABLE_VALUES) @@ -194,7 +194,12 @@ public class RpcV2Controller extends AbstractRpcController { accessValidator.validate(getCurrentUser(), Operation.RPC_CALL, deviceId, new HttpValidationCallback(response, new FutureCallback<>() { @Override public void onSuccess(@Nullable DeferredResult result) { - PageData rpcCalls = rpcService.findAllByDeviceIdAndStatus(tenantId, deviceId, rpcStatus, pageLink); + PageData rpcCalls; + if (rpcStatus != null) { + rpcCalls = rpcService.findAllByDeviceIdAndStatus(tenantId, deviceId, rpcStatus, pageLink); + } else { + rpcCalls = rpcService.findAllByDeviceId(tenantId, deviceId, pageLink); + } response.setResult(new ResponseEntity<>(rpcCalls, HttpStatus.OK)); } diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/rpc/RpcService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/rpc/RpcService.java index 4bdb1a169d..46b82077a3 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/rpc/RpcService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/rpc/RpcService.java @@ -35,5 +35,7 @@ public interface RpcService { ListenableFuture findRpcByIdAsync(TenantId tenantId, RpcId id); + PageData findAllByDeviceId(TenantId tenantId, DeviceId deviceId, PageLink pageLink); + PageData findAllByDeviceIdAndStatus(TenantId tenantId, DeviceId deviceId, RpcStatus rpcStatus, PageLink pageLink); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/rpc/BaseRpcService.java b/dao/src/main/java/org/thingsboard/server/dao/rpc/BaseRpcService.java index 02b4bbe433..a5538f56d7 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/rpc/BaseRpcService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/rpc/BaseRpcService.java @@ -82,7 +82,15 @@ public class BaseRpcService implements RpcService { log.trace("Executing findAllByDeviceIdAndStatus, tenantId [{}], deviceId [{}], rpcStatus [{}], pageLink [{}]", tenantId, deviceId, rpcStatus, pageLink); validateId(tenantId, INCORRECT_TENANT_ID + tenantId); validatePageLink(pageLink); - return rpcDao.findAllByDeviceId(tenantId, deviceId, rpcStatus, pageLink); + return rpcDao.findAllByDeviceIdAndStatus(tenantId, deviceId, rpcStatus, pageLink); + } + + @Override + public PageData findAllByDeviceId(TenantId tenantId, DeviceId deviceId, PageLink pageLink) { + log.trace("Executing findAllByDeviceIdAndStatus, tenantId [{}], deviceId [{}], pageLink [{}]", tenantId, deviceId, pageLink); + validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validatePageLink(pageLink); + return rpcDao.findAllByDeviceId(tenantId, deviceId, pageLink); } private PaginatedRemover tenantRpcRemover = diff --git a/dao/src/main/java/org/thingsboard/server/dao/rpc/RpcDao.java b/dao/src/main/java/org/thingsboard/server/dao/rpc/RpcDao.java index 63af784dbb..3f34db3907 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/rpc/RpcDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/rpc/RpcDao.java @@ -24,7 +24,9 @@ import org.thingsboard.server.common.data.rpc.RpcStatus; import org.thingsboard.server.dao.Dao; public interface RpcDao extends Dao { - PageData findAllByDeviceId(TenantId tenantId, DeviceId deviceId, RpcStatus rpcStatus, PageLink pageLink); + PageData findAllByDeviceId(TenantId tenantId, DeviceId deviceId, PageLink pageLink); + + PageData findAllByDeviceIdAndStatus(TenantId tenantId, DeviceId deviceId, RpcStatus rpcStatus, PageLink pageLink); PageData findAllRpcByTenantId(TenantId tenantId, PageLink pageLink); diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/rpc/JpaRpcDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/rpc/JpaRpcDao.java index 221ef17361..6791de4122 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/rpc/JpaRpcDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/rpc/JpaRpcDao.java @@ -50,7 +50,12 @@ public class JpaRpcDao extends JpaAbstractDao implements RpcDao } @Override - public PageData findAllByDeviceId(TenantId tenantId, DeviceId deviceId, RpcStatus rpcStatus, PageLink pageLink) { + public PageData findAllByDeviceId(TenantId tenantId, DeviceId deviceId, PageLink pageLink) { + return DaoUtil.toPageData(rpcRepository.findAllByTenantIdAndDeviceId(tenantId.getId(), deviceId.getId(), DaoUtil.toPageable(pageLink))); + } + + @Override + public PageData findAllByDeviceIdAndStatus(TenantId tenantId, DeviceId deviceId, RpcStatus rpcStatus, PageLink pageLink) { return DaoUtil.toPageData(rpcRepository.findAllByTenantIdAndDeviceIdAndStatus(tenantId.getId(), deviceId.getId(), rpcStatus, DaoUtil.toPageable(pageLink))); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/rpc/RpcRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/rpc/RpcRepository.java index 76b67b3823..0ca33dbb2e 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/rpc/RpcRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/rpc/RpcRepository.java @@ -26,6 +26,8 @@ import org.thingsboard.server.dao.model.sql.RpcEntity; import java.util.UUID; public interface RpcRepository extends CrudRepository { + Page findAllByTenantIdAndDeviceId(UUID tenantId, UUID deviceId, Pageable pageable); + Page findAllByTenantIdAndDeviceIdAndStatus(UUID tenantId, UUID deviceId, RpcStatus status, Pageable pageable); Page findAllByTenantId(UUID tenantId, Pageable pageable); diff --git a/ui-ngx/src/app/core/api/widget-api.models.ts b/ui-ngx/src/app/core/api/widget-api.models.ts index d053efb601..c0bab56e4e 100644 --- a/ui-ngx/src/app/core/api/widget-api.models.ts +++ b/ui-ngx/src/app/core/api/widget-api.models.ts @@ -55,6 +55,8 @@ import { TranslateService } from '@ngx-translate/core'; import { AlarmDataService } from '@core/api/alarm-data.service'; import { IDashboardController } from '@home/components/dashboard-page/dashboard-page.models'; import { PopoverPlacement } from '@shared/components/popover.models'; +import { PageLink } from '@shared/models/page/page-link'; +import { PersistentRpc, RpcStatus } from '@shared/models/rpc.models'; export interface TimewindowFunctions { onUpdateTimewindow: (startTimeMs: number, endTimeMs: number, interval?: number) => void; @@ -71,9 +73,9 @@ export interface WidgetSubscriptionApi { export interface RpcApi { sendOneWayCommand: (method: string, params?: any, timeout?: number, persistent?: boolean, - persistentPollingInterval?: number, requestUUID?: string) => Observable; + persistentPollingInterval?: number, retries?: number, additionalInfo?: any, requestUUID?: string) => Observable; sendTwoWayCommand: (method: string, params?: any, timeout?: number, persistent?: boolean, - persistentPollingInterval?: number, requestUUID?: string) => Observable; + persistentPollingInterval?: number, retries?: number, additionalInfo?: any, requestUUID?: string) => Observable; completedCommand: () => void; } @@ -287,6 +289,8 @@ export interface IWidgetSubscription { comparisonEnabled?: boolean; comparisonTimeWindow?: WidgetTimewindow; + persistentRequests?: PageData; + alarms?: PageData; alarmSource?: Datasource; @@ -313,11 +317,13 @@ export interface IWidgetSubscription { updateTimewindowConfig(newTimewindow: Timewindow): void; sendOneWayCommand(method: string, params?: any, timeout?: number, persistent?: boolean, - persistentPollingInterval?: number, requestUUID?: string): Observable; + persistentPollingInterval?: number, retries?: number, additionalInfo?: any, requestUUID?: string): Observable; sendTwoWayCommand(method: string, params?: any, timeout?: number, persistent?: boolean, - persistentPollingInterval?: number, requestUUID?: string): Observable; + persistentPollingInterval?: number, retries?: number, additionalInfo?: any, requestUUID?: string): Observable; clearRpcError(): void; + subscribeForPersistentRequests(pageLink: PageLink, keyFileter: RpcStatus): Observable; + subscribe(): void; subscribeAllForPaginatedData(pageLink: EntityDataPageLink, diff --git a/ui-ngx/src/app/core/api/widget-subscription.ts b/ui-ngx/src/app/core/api/widget-subscription.ts index 21f95a9158..c548b0fbab 100644 --- a/ui-ngx/src/app/core/api/widget-subscription.ts +++ b/ui-ngx/src/app/core/api/widget-subscription.ts @@ -70,6 +70,7 @@ import { import { distinct, filter, map, switchMap, takeUntil } from 'rxjs/operators'; import { AlarmDataListener } from '@core/api/alarm-data.service'; import { RpcStatus } from '@shared/models/rpc.models'; +import { PageLink } from '@shared/models/page/page-link'; const moment = moment_; @@ -656,13 +657,13 @@ export class WidgetSubscription implements IWidgetSubscription { } sendOneWayCommand(method: string, params?: any, timeout?: number, persistent?: boolean, - persistentPollingInterval?: number, requestUUID?: string): Observable { - return this.sendCommand(true, method, params, timeout, persistent, persistentPollingInterval, requestUUID); + persistentPollingInterval?: number, retries?: number, additionalInfo?: any, requestUUID?: string): Observable { + return this.sendCommand(true, method, params, timeout, persistent, persistentPollingInterval, retries, additionalInfo, requestUUID); } sendTwoWayCommand(method: string, params?: any, timeout?: number, persistent?: boolean, - persistentPollingInterval?: number, requestUUID?: string): Observable { - return this.sendCommand(false, method, params, timeout, persistent, persistentPollingInterval, requestUUID); + persistentPollingInterval?: number, retries?: number, additionalInfo?: any, requestUUID?: string): Observable { + return this.sendCommand(false, method, params, timeout, persistent, persistentPollingInterval, retries, additionalInfo, requestUUID); } clearRpcError(): void { @@ -679,7 +680,8 @@ export class WidgetSubscription implements IWidgetSubscription { } sendCommand(oneWayElseTwoWay: boolean, method: string, params?: any, timeout?: number, - persistent?: boolean, persistentPollingInterval?: number, requestUUID?: string): Observable { + persistent?: boolean, persistentPollingInterval?: number, retries?: number, + additionalInfo?: any, requestUUID?: string): Observable { if (!this.rpcEnabled) { return throwError(new Error('Rpc disabled!')); } else { @@ -692,6 +694,8 @@ export class WidgetSubscription implements IWidgetSubscription { method, params, persistent, + retries, + additionalInfo, requestUUID }; if (timeout && timeout > 0) { @@ -777,6 +781,15 @@ export class WidgetSubscription implements IWidgetSubscription { } } + subscribeForPersistentRequests(pageLink: PageLink, keyFilter: RpcStatus): Observable { + if (!this.rpcEnabled) { + return throwError(new Error('Rpc disabled!')); + } else if (!this.targetDeviceId) { + return throwError(new Error('Target device is not set!')); + } + return this.ctx.deviceService.getPersistedRpcRequests(this.targetDeviceId, pageLink, keyFilter); + } + private extractRejectionErrorText(rejection: HttpErrorResponse) { let error = null; if (rejection.error) { diff --git a/ui-ngx/src/app/core/http/device.service.ts b/ui-ngx/src/app/core/http/device.service.ts index c8ea7e65ff..9b5119d317 100644 --- a/ui-ngx/src/app/core/http/device.service.ts +++ b/ui-ngx/src/app/core/http/device.service.ts @@ -31,7 +31,7 @@ import { import { EntitySubtype } from '@app/shared/models/entity-type.models'; import { AuthService } from '@core/auth/auth.service'; import { BulkImportRequest, BulkImportResult } from '@home/components/import-export/import-export.models'; -import { PersistentRpc } from '@shared/models/rpc.models'; +import { PersistentRpc, RpcStatus } from '@shared/models/rpc.models'; @Injectable({ providedIn: 'root' @@ -143,6 +143,17 @@ export class DeviceService { return this.http.get(`/api/rpc/persistent/${rpcId}`, defaultHttpOptionsFromConfig(config)); } + public deletePersistedRpc(rpcId: string, config?: RequestConfig) { + return this.http.delete(`/api/rpc/persistent/${rpcId}`, defaultHttpOptionsFromConfig(config)); + } + + public getPersistedRpcRequests(deviceId: string, pageLink: PageLink, + keyFilter: RpcStatus, config?: RequestConfig): Observable> { + const rpcStatus = keyFilter ? '&rpcStatus=' + keyFilter : ''; + return this.http.get>(`/api/rpc/persistent/device/${deviceId}${pageLink.toQuery()}${rpcStatus}`, + defaultHttpOptionsFromConfig(config)); + } + public findByQuery(query: DeviceSearchQuery, config?: RequestConfig): Observable> { return this.http.post>('/api/devices', query, defaultHttpOptionsFromConfig(config)); diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-add-dialog.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-add-dialog.component.html new file mode 100644 index 0000000000..7e0ae3056f --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-add-dialog.component.html @@ -0,0 +1,92 @@ + +
+ +

{{ 'widgets.persistent-table.add-title' | translate }}

+ + +
+ + +
+
+
+
+ + {{ 'widgets.persistent-table.message-types.' + persistentFormGroup.get('oneWayElseTwoWay').value | translate }} + +
+
+ + widgets.persistent-table.method + + + {{'widgets.persistent-table.method-error' | translate}} + + + {{'widgets.persistent-table.white-space-error' | translate}} + + + + widgets.persistent-table.retries + + +
+
+ + +
+ + + + widgets.persistent-table.additional-info + + + + + + + +
+
+
+
+ + +
+ +
+
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-add-dialog.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-add-dialog.component.scss new file mode 100644 index 0000000000..e450fa4a09 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-add-dialog.component.scss @@ -0,0 +1,30 @@ +/** + * Copyright © 2016-2021 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. + */ +:host { + .add-dialog ::ng-deep { + + .params-json-editor, + .additional-json-editor { + .tb-json-object-panel { + margin: 0 0 16px; + } + + .mat-expansion-panel-body { + padding-bottom: 0 !important; + } + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-add-dialog.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-add-dialog.component.ts new file mode 100644 index 0000000000..c4e87c5e6b --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-add-dialog.component.ts @@ -0,0 +1,77 @@ +/// +/// Copyright © 2016-2021 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { Component, OnInit } from '@angular/core'; +import { DialogComponent } from '@shared/components/dialog.component'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { Router } from '@angular/router'; +import { MatDialogRef } from '@angular/material/dialog'; +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { RequestData } from '@shared/models/rpc.models'; + +@Component({ + selector: 'tb-persistent-add-dialog', + templateUrl: './persistent-add-dialog.component.html', + styleUrls: ['./persistent-add-dialog.component.scss'] +}) + +export class PersistentAddDialogComponent extends DialogComponent implements OnInit { + + public persistentFormGroup: FormGroup; + + private requestData: RequestData = { + persistentUpdated: false + }; + + constructor(protected store: Store, + protected router: Router, + public dialogRef: MatDialogRef, + private fb: FormBuilder) { + super(store, router, dialogRef); + + this.persistentFormGroup = this.fb.group( + { + method: ['', [Validators.required, Validators.pattern(/^\S+$/)]], + oneWayElseTwoWay: [false], + retries: [null, [Validators.pattern(/^-?[0-9]+$/), Validators.min(0)]], + params: [{}], + additionalInfo: [{}] + } + ); + } + + save() { + if (this.persistentFormGroup.valid) { + this.requestData = { + persistentUpdated: true, + method: this.persistentFormGroup.get('method').value, + oneWayElseTwoWay: this.persistentFormGroup.get('oneWayElseTwoWay').value, + params: this.persistentFormGroup.get('params').value, + additionalInfo: this.persistentFormGroup.get('additionalInfo').value, + retries: this.persistentFormGroup.get('retries').value + }; + this.close(); + } + } + + ngOnInit(): void { + } + + close(): void { + this.dialogRef.close(this.requestData); + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-details-dialog.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-details-dialog.component.html new file mode 100644 index 0000000000..3a9451db67 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-details-dialog.component.html @@ -0,0 +1,118 @@ + +
+ +

{{ persistentFormGroup.get('rpcId').value }}

+ + +
+ + +
+
+
+
+ + widgets.persistent-table.created-time + + + + widgets.persistent-table.expiration-time + + +
+
+ + widgets.persistent-table.message-type + + + + widgets.persistent-table.status + + + + widgets.persistent-table.method + + + + widgets.persistent-table.retries + + +
+ + + + + + {{ 'widgets.persistent-table.response' | translate }} + + + + + + + + {{ 'widgets.persistent-table.params' | translate }} + + + + + + + + + + {{ 'widgets.persistent-table.additional-info' | translate }} + + + + + + + + +
+
+
+ + +
+ +
+
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-details-dialog.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-details-dialog.component.scss new file mode 100644 index 0000000000..ec456d5d9e --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-details-dialog.component.scss @@ -0,0 +1,33 @@ +/** + * Copyright © 2016-2021 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. + */ +:host { + .rpc-dialog ::ng-deep { + .mat-expansion-panel-body { + padding-bottom: 0 !important; + } + + .tb-json-object-panel { + margin: 0 0 16px 0; + } + } + .tb-audit-log-response-data { + width: 100%; + min-width: 400px; + height: 100%; + min-height: 100px; + border: 1px solid #c0c0c0; + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-details-dialog.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-details-dialog.component.ts new file mode 100644 index 0000000000..552175b905 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-details-dialog.component.ts @@ -0,0 +1,151 @@ +/// +/// Copyright © 2016-2021 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { Component, ElementRef, Inject, OnInit, ViewChild } from '@angular/core'; +import { DialogComponent } from '@shared/components/dialog.component'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { Router } from '@angular/router'; +import { DatePipe } from '@angular/common'; +import { TranslateService } from '@ngx-translate/core'; +import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; +import { FormBuilder, FormGroup } from '@angular/forms'; +import { DeviceService } from '@core/http/device.service'; +import { + PersistentRpc, + rpcStatusColors, + RpcStatus, + rpcStatusTranslation +} from '@shared/models/rpc.models'; +import { isDefinedAndNotNull } from '@core/utils'; +import { NULL_UUID } from '@shared/models/id/has-uuid'; +import { DialogService } from '@core/services/dialog.service'; + +export interface PersistentDetailsDialogData { + persistentRequest: PersistentRpc; + allowDelete: boolean; +} + +@Component({ + selector: 'tb-persistent-details-dialog', + templateUrl: './persistent-details-dialog.component.html', + styleUrls: ['./persistent-details-dialog.component.scss'] +}) + +export class PersistentDetailsDialogComponent extends DialogComponent implements OnInit { + + @ViewChild('responseDataEditor', {static: true}) + responseDataEditorElmRef: ElementRef; + + public persistentFormGroup: FormGroup; + public rpcStatusColorsMap = rpcStatusColors; + public rpcStatus = RpcStatus; + public allowDelete: boolean; + + private persistentUpdated = false; + private responseData: string; + + constructor(protected store: Store, + protected router: Router, + private datePipe: DatePipe, + private translate: TranslateService, + @Inject(MAT_DIALOG_DATA) public data: PersistentDetailsDialogData, + public dialogRef: MatDialogRef, + private dialogService: DialogService, + private deviceService: DeviceService, + private fb: FormBuilder) { + super(store, router, dialogRef); + + this.allowDelete = data.allowDelete; + + this.persistentFormGroup = this.fb.group( + { + rpcId: [''], + createdTime: [''], + expirationTime: [''], + messageType: [''], + status: [''], + method: [''], + params: [''], + retries: [''], + response: [''], + additionalInfo: [null] + } + ); + this.loadPersistentFields(data.persistentRequest); + this.responseData = JSON.stringify(data.persistentRequest.response, null, 2); + } + + loadPersistentFields(request: PersistentRpc) { + this.persistentFormGroup.get('rpcId') + .patchValue(this.translate.instant('widgets.persistent-table.details-title') + request.id.id); + this.persistentFormGroup.get('createdTime') + .patchValue(this.datePipe.transform(request.createdTime, 'yyyy-MM-dd HH:mm:ss')); + this.persistentFormGroup.get('expirationTime') + .patchValue(this.datePipe.transform(request.expirationTime, 'yyyy-MM-dd HH:mm:ss')); + this.persistentFormGroup.get('messageType') + .patchValue(this.translate.instant('widgets.persistent-table.message-types.' + request.request.oneway) + ); + this.persistentFormGroup.get('status') + .patchValue(this.translate.instant(rpcStatusTranslation.get(request.status))); + this.persistentFormGroup.get('method') + .patchValue(request.request.body.method); + if (isDefinedAndNotNull(request.request.retries)) { + this.persistentFormGroup.get('retries') + .patchValue(request.request.retries); + } + if (isDefinedAndNotNull(request.response)) { + this.persistentFormGroup.get('response') + .patchValue(request.response); + } + if (isDefinedAndNotNull(request.request.body.params)) { + this.persistentFormGroup.get('params') + .patchValue(JSON.parse(request.request.body.params)); + } + if (isDefinedAndNotNull(request.additionalInfo)) { + this.persistentFormGroup.get('additionalInfo') + .patchValue(request.additionalInfo); + } + } + + ngOnInit(): void { + } + + close(): void { + this.dialogRef.close(this.persistentUpdated); + } + + deleteRpcRequest() { + const persistentRpc = this.data.persistentRequest; + if (persistentRpc && persistentRpc.id && persistentRpc.id.id !== NULL_UUID) { + this.dialogService.confirm( + this.translate.instant('widgets.persistent-table.delete-request-title'), + this.translate.instant('widgets.persistent-table.delete-request-text'), + this.translate.instant('action.no'), + this.translate.instant('action.yes') + ).subscribe((res) => { + if (res) { + if (res) { + this.deviceService.deletePersistedRpc(persistentRpc.id.id).subscribe(() => { + this.persistentUpdated = true; + this.close(); + }); + } + } + }); + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-filter-panel.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-filter-panel.component.html new file mode 100644 index 0000000000..ffbcab5749 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-filter-panel.component.html @@ -0,0 +1,44 @@ + +
+ + widgets.persistent-table.rpc-status-list + + + {{ 'widgets.persistent-table.rpc-search-status-all' | translate }} + + + {{ rpcSearchStatusTranslationMap.get(searchStatus) | translate }} + + + +
+ + +
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-filter-panel.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-filter-panel.component.scss new file mode 100644 index 0000000000..63f832d78c --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-filter-panel.component.scss @@ -0,0 +1,36 @@ +/** + * Copyright © 2016-2021 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. + */ +:host { + width: 100%; + height: 100%; + min-width: 300px; + overflow: hidden; + background: #fff; + border-radius: 4px; + box-shadow: + 0 7px 8px -4px rgba(0, 0, 0, .2), + 0 13px 19px 2px rgba(0, 0, 0, .14), + 0 5px 24px 4px rgba(0, 0, 0, .12); + + .mat-content { + overflow: hidden; + background-color: #fff; + } + + .mat-padding { + padding: 16px; + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-filter-panel.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-filter-panel.component.ts new file mode 100644 index 0000000000..d60c80ac43 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-filter-panel.component.ts @@ -0,0 +1,71 @@ +/// +/// Copyright © 2016-2021 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { Component, Inject, InjectionToken } from '@angular/core'; +import { FormBuilder, FormGroup } from '@angular/forms'; +import { OverlayRef } from '@angular/cdk/overlay'; +import { RpcStatus, rpcStatusTranslation } from '@shared/models/rpc.models'; + +export const PERSISTENT_FILTER_PANEL_DATA = new InjectionToken('AlarmFilterPanelData'); + +export interface PersistentFilterPanelData { + rpcStatus: RpcStatus; +} + +@Component({ + selector: 'tb-persistent-filter-panel', + templateUrl: './persistent-filter-panel.component.html', + styleUrls: ['./persistent-filter-panel.component.scss'] +}) +export class PersistentFilterPanelComponent { + + public persistentFilterFormGroup: FormGroup; + public result: PersistentFilterPanelData; + public rpcSearchStatusTranslationMap = rpcStatusTranslation; + + public persistentSearchStatuses = [ + RpcStatus.QUEUED, + RpcStatus.SENT, + RpcStatus.DELIVERED, + RpcStatus.SUCCESSFUL, + RpcStatus.TIMEOUT, + RpcStatus.EXPIRED, + RpcStatus.FAILED + ]; + + constructor(@Inject(PERSISTENT_FILTER_PANEL_DATA) + public data: PersistentFilterPanelData, + public overlayRef: OverlayRef, + private fb: FormBuilder) { + this.persistentFilterFormGroup = this.fb.group( + { + rpcStatus: this.data.rpcStatus + } + ); + } + + update() { + this.result = { + rpcStatus: this.persistentFilterFormGroup.get('rpcStatus').value + }; + this.overlayRef.dispose(); + } + + cancel() { + this.overlayRef.dispose(); + } +} + diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-table.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-table.component.html new file mode 100644 index 0000000000..c4fc2428be --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-table.component.html @@ -0,0 +1,125 @@ + +
+
+
+ + + + {{ 'widgets.persistent-table.rpc-id' | translate }} + + + {{ column.id.id }} + + + + + {{ 'widgets.persistent-table.created-time' | translate }} + + + {{ column.createdTime | date:'yyyy-MM-dd HH:mm:ss' }} + + + + + {{ 'widgets.persistent-table.expiration-time' | translate }} + + + {{ column.expirationTime | date:'yyyy-MM-dd HH:mm:ss' }} + + + + + {{ 'widgets.persistent-table.status' | translate }} + + + {{ rpcStatusTranslation.get(column.status) | translate }} + + + + + {{ 'widgets.persistent-table.message-type' | translate }} + + + {{ 'widgets.persistent-table.message-types.' + column.request.oneway | translate }} + + + + + {{ 'widgets.persistent-table.method' | translate }} + + + {{ column.request.body.method }} + + + + + + +
+ + + +
+
+ + + + + + +
+
+
+ + +
+ {{ noDataDisplayMessageText }} + {{ 'common.loading' | translate }} +
+ + +
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-table.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-table.component.scss new file mode 100644 index 0000000000..ec079f54cb --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-table.component.scss @@ -0,0 +1,52 @@ +/** + * Copyright © 2016-2021 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. + */ +:host { + width: 100%; + height: 100%; + .tb-table-widget { + .table-container { + position: relative; + } + + .mat-table { + .mat-row { + &.invisible { + visibility: hidden; + } + } + } + + span.no-data-found { + position: absolute; + top: 60px; + bottom: 0; + left: 0; + right: 0; + } + + .column-id { + min-width: 250px; + max-width: 250px; + width: 250px; + } + + .column-time { + min-width: 120px; + max-width: 120px; + width: 120px; + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-table.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-table.component.ts new file mode 100644 index 0000000000..8f5ccab14b --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-table.component.ts @@ -0,0 +1,476 @@ +/// +/// Copyright © 2016-2021 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { + Component, + ElementRef, + Injector, + Input, + OnInit, + StaticProvider, + ViewChild, + ViewContainerRef +} from '@angular/core'; +import { PageComponent } from '@shared/components/page.component'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { WidgetContext } from '@home/models/widget-component.models'; +import { WidgetConfig } from '@shared/models/widget.models'; +import { IWidgetSubscription } from '@core/api/widget-api.models'; +import { BehaviorSubject, merge, Observable, of, ReplaySubject } from 'rxjs'; +import { catchError, map, tap } from 'rxjs/operators'; +import { + constructTableCssString, noDataMessage, + TableCellButtonActionDescriptor, + TableWidgetSettings +} from '@home/components/widget/lib/table-widget.models'; +import cssjs from '@core/css/css'; +import { UtilsService } from '@core/services/utils.service'; +import { TranslateService } from '@ngx-translate/core'; +import { hashCode, isDefined, isNumber } from '@core/utils'; +import { CollectionViewer, DataSource } from '@angular/cdk/collections'; +import { emptyPageData, PageData } from '@shared/models/page/page-data'; +import { + PersistentRpc, + PersistentRpcData, RequestData, + RpcStatus, + rpcStatusColors, rpcStatusTranslation +} from '@shared/models/rpc.models'; +import { PageLink } from '@shared/models/page/page-link'; +import { Direction, SortOrder, sortOrderFromString } from '@shared/models/page/sort-order'; +import { MatPaginator } from '@angular/material/paginator'; +import { MatSort } from '@angular/material/sort'; +import { NULL_UUID } from '@shared/models/id/has-uuid'; +import { DialogService } from '@core/services/dialog.service'; +import { DeviceService } from '@core/http/device.service'; +import { MatDialog } from '@angular/material/dialog'; +import { + PersistentDetailsDialogComponent, + PersistentDetailsDialogData +} from '@home/components/widget/lib/rpc/persistent-details-dialog.component'; +import { ConnectedPosition, Overlay, OverlayConfig, OverlayRef } from '@angular/cdk/overlay'; +import { ComponentPortal } from '@angular/cdk/portal'; +import { + PERSISTENT_FILTER_PANEL_DATA, PersistentFilterPanelComponent, PersistentFilterPanelData +} from '@home/components/widget/lib/rpc/persistent-filter-panel.component'; +import { PersistentAddDialogComponent } from '@home/components/widget/lib/rpc/persistent-add-dialog.component'; + +interface PersistentTableWidgetSettings extends TableWidgetSettings { + defaultSortOrder: string; + defaultPageSize: number; + displayPagination: boolean; + enableStickyAction: boolean; + enableStickyHeader: boolean; + enableFilter: boolean; + displayColumns: string[]; + displayDetails: boolean; + allowDelete: boolean; + allowSendRequest: boolean; +} + +interface PersistentTableWidgetActionDescriptor extends TableCellButtonActionDescriptor { + details?: boolean; + delete?: boolean; +} + +@Component({ + selector: 'tb-persistent-table-widget', + templateUrl: './persistent-table.component.html', + styleUrls: ['./persistent-table.component.scss' , '../table-widget.scss'] +}) + +export class PersistentTableComponent extends PageComponent implements OnInit { + + @Input() + ctx: WidgetContext; + + @ViewChild(MatPaginator) paginator: MatPaginator; + @ViewChild(MatSort) sort: MatSort; + + private settings: PersistentTableWidgetSettings; + private widgetConfig: WidgetConfig; + private subscription: IWidgetSubscription; + private enableFilterAction = true; + private allowSendRequest = true; + private defaultPageSize = 10; + private defaultSortOrder = '-createdTime'; + private rpcStatusFilter: RpcStatus | null = null; + private displayDetails = true; + private allowDelete = true; + private displayTableColumns: string[]; + + public persistentDatasource: PersistentDatasource; + public noDataDisplayMessageText: string; + public rpcStatusColor = rpcStatusColors; + public rpcStatusTranslation = rpcStatusTranslation; + public displayPagination = true; + public enableStickyHeader = true; + public enableStickyAction = true; + public pageLink: PageLink; + public pageSizeOptions; + public actionCellButtonAction: PersistentTableWidgetActionDescriptor[] = []; + public displayedColumns: string[]; + + constructor(protected store: Store, + private elementRef: ElementRef, + private overlay: Overlay, + private viewContainerRef: ViewContainerRef, + private utils: UtilsService, + private translate: TranslateService, + private dialogService: DialogService, + private deviceService: DeviceService, + private dialog: MatDialog) { + super(store); + } + + ngOnInit() { + this.ctx.$scope.persistentTableWidget = this; + this.settings = this.ctx.settings; + this.widgetConfig = this.ctx.widgetConfig; + this.subscription = this.ctx.defaultSubscription; + this.initializeConfig(); + this.ctx.updateWidgetParams(); + } + + ngAfterViewInit(): void { + if (this.displayPagination) { + this.sort.sortChange.subscribe(() => this.paginator.pageIndex = 0); + } + ((this.displayPagination ? merge(this.sort.sortChange, this.paginator.page) : this.sort.sortChange) as Observable) + .pipe( + tap(() => this.updateData()) + ) + .subscribe(); + this.updateData(); + } + + private initializeConfig() { + + this.displayPagination = isDefined(this.settings.displayPagination) ? this.settings.displayPagination : true; + this.enableStickyHeader = isDefined(this.settings.enableStickyHeader) ? this.settings.enableStickyHeader : true; + this.displayTableColumns = isDefined(this.settings.displayColumns) ? this.settings.displayColumns : []; + this.enableStickyAction = isDefined(this.settings.enableStickyAction) ? this.settings.enableStickyAction : true; + this.enableFilterAction = isDefined(this.settings.enableFilter) ? this.settings.enableFilter : true; + this.displayDetails = isDefined(this.settings.displayDetails) ? this.settings.displayDetails : true; + this.allowDelete = isDefined(this.settings.allowDelete) ? this.settings.allowDelete : true; + this.allowSendRequest = isDefined(this.settings.allowSendRequest) ? this.settings.allowSendRequest : true; + + this.noDataDisplayMessageText = + noDataMessage(this.widgetConfig.noDataDisplayMessage, 'widgets.persistent-table.no-request-prompt', this.utils, this.translate); + + this.displayedColumns = [...this.displayTableColumns]; + + const pageSize = this.settings.defaultPageSize; + if (isDefined(pageSize) && isNumber(pageSize) && pageSize > 0) { + this.defaultPageSize = pageSize; + } + this.pageSizeOptions = [this.defaultPageSize, this.defaultPageSize * 2, this.defaultPageSize * 3]; + if (this.settings.defaultSortOrder && this.settings.defaultSortOrder.length) { + this.defaultSortOrder = this.settings.defaultSortOrder; + } + const sortOrder: SortOrder = sortOrderFromString(this.defaultSortOrder); + this.pageLink = new PageLink(this.defaultPageSize, 0, null, sortOrder); + this.pageLink.pageSize = this.displayPagination ? this.defaultPageSize : 1024; + + + this.ctx.widgetActions = [ + { + name: 'widgets.persistent-table.add', + show: this.allowSendRequest, + icon: 'add', + onAction: $event => this.addPersistentRpcRequest($event) + }, + { + name: 'widgets.persistent-table.refresh', + show: true, + icon: 'refresh', + onAction: () => this.reloadPersistentRequests() + }, + { + name: 'widgets.persistent-table.filter', + show: this.enableFilterAction, + icon: 'filter_list', + onAction: $event => this.editFilter($event) + } + ]; + + if (this.settings.displayDetails) { + this.actionCellButtonAction.push( + { + displayName: this.translate.instant('widgets.persistent-table.details'), + icon: 'more_horiz', + details: true + } as PersistentTableWidgetActionDescriptor + ); + } + if (this.settings.allowDelete) { + this.actionCellButtonAction.push( + { + displayName: this.translate.instant('widgets.persistent-table.delete'), + icon: 'delete', + delete: true + } as PersistentTableWidgetActionDescriptor + ); + } + if (this.actionCellButtonAction.length) { + this.displayedColumns.push('actions'); + } + + this.persistentDatasource = new PersistentDatasource(this.translate, this.subscription); + + const cssString = constructTableCssString(this.widgetConfig); + const cssParser = new cssjs(); + cssParser.testMode = false; + const namespace = 'persistent-table-' + hashCode(cssString); + cssParser.cssPreviewNamespace = namespace; + cssParser.createStyleElement(namespace, cssString); + $(this.elementRef.nativeElement).addClass(namespace); + } + + private updateData() { + if (this.displayPagination) { + this.pageLink.page = this.paginator.pageIndex; + this.pageLink.pageSize = this.paginator.pageSize; + } else { + this.pageLink.page = 0; + } + if (this.settings.defaultSortOrder && this.settings.defaultSortOrder.length) { + this.defaultSortOrder = this.utils.customTranslation(this.settings.defaultSortOrder, this.settings.defaultSortOrder); + } + this.pageLink.sortOrder.property = this.sort.active; + this.pageLink.sortOrder.direction = Direction[this.sort.direction.toUpperCase()]; + this.persistentDatasource.loadPersistent(this.pageLink, this.rpcStatusFilter); + this.ctx.detectChanges(); + } + + public onDataUpdated() { + this.ctx.detectChanges(); + } + + reloadPersistentRequests() { + if (this.displayPagination) { + this.paginator.pageIndex = 0; + } + this.updateData(); + } + + deleteRpcRequest($event: Event, persistentRpc: PersistentRpc) { + if ($event) { + $event.stopPropagation(); + } + if (persistentRpc && persistentRpc.id && persistentRpc.id.id !== NULL_UUID) { + this.dialogService.confirm( + this.translate.instant('widgets.persistent-table.delete-request-title'), + this.translate.instant('widgets.persistent-table.delete-request-text'), + this.translate.instant('action.no'), + this.translate.instant('action.yes') + ).subscribe((res) => { + if (res) { + if (res) { + this.deviceService.deletePersistedRpc(persistentRpc.id.id).subscribe(() => { + this.reloadPersistentRequests(); + }); + } + } + }); + } + } + + openRequestDetails($event: Event, persistentRpc: PersistentRpc) { + if ($event) { + $event.stopPropagation(); + } + if (persistentRpc && persistentRpc.id && persistentRpc.id.id !== NULL_UUID) { + this.dialog.open + (PersistentDetailsDialogComponent, + { + disableClose: true, + panelClass: ['tb-dialog', 'tb-fullscreen-dialog'], + data: { + persistentRequest: persistentRpc, + allowDelete: this.allowDelete + } + }).afterClosed().subscribe( + (res) => { + if (res) { + this.reloadPersistentRequests(); + } + } + ); + } + } + + addPersistentRpcRequest($event: Event){ + if ($event) { + $event.stopPropagation(); + } + this.dialog.open + (PersistentAddDialogComponent, + { + disableClose: true, + panelClass: ['tb-dialog', 'tb-fullscreen-dialog'] + }).afterClosed().subscribe( + (requestData) => { + if (requestData.persistentUpdated) { + this.sendRequests(requestData); + } + } + ); + } + + private sendRequests(requestData: RequestData) { + let commandPromise; + if (requestData.oneWayElseTwoWay) { + commandPromise = this.ctx.controlApi.sendOneWayCommand( + requestData.method, + requestData.params, null, + true, null, + requestData.retries, + requestData.additionalInfo + ); + } else { + commandPromise = this.ctx.controlApi.sendTwoWayCommand( + requestData.method, + requestData.params, + null, + true, null, + requestData.retries, + requestData.additionalInfo + ); + } + commandPromise.subscribe( + () => { + this.reloadPersistentRequests(); + } + ); + } + + public onActionButtonClick($event: Event, persistentRpc: PersistentRpc, actionDescriptor: PersistentTableWidgetActionDescriptor) { + if (actionDescriptor.details) { + this.openRequestDetails($event, persistentRpc); + } + if (actionDescriptor.delete) { + this.deleteRpcRequest($event, persistentRpc); + } + } + + private editFilter($event: Event) { + if ($event) { + $event.stopPropagation(); + } + const target = $event.target || $event.srcElement || $event.currentTarget; + const config = new OverlayConfig(); + config.backdropClass = 'cdk-overlay-transparent-backdrop'; + config.hasBackdrop = true; + const connectedPosition: ConnectedPosition = { + originX: 'end', + originY: 'bottom', + overlayX: 'end', + overlayY: 'top' + }; + config.positionStrategy = this.overlay.position().flexibleConnectedTo(target as HTMLElement) + .withPositions([connectedPosition]); + + const overlayRef = this.overlay.create(config); + overlayRef.backdropClick().subscribe(() => { + overlayRef.dispose(); + }); + const providers: StaticProvider[] = [ + { + provide: PERSISTENT_FILTER_PANEL_DATA, + useValue: { + rpcStatus: this.rpcStatusFilter + } as PersistentFilterPanelData + }, + { + provide: OverlayRef, + useValue: overlayRef + } + ]; + const injector = Injector.create({parent: this.viewContainerRef.injector, providers}); + const componentRef = overlayRef.attach(new ComponentPortal(PersistentFilterPanelComponent, + this.viewContainerRef, injector)); + componentRef.onDestroy(() => { + if (componentRef.instance.result) { + const result = componentRef.instance.result; + this.rpcStatusFilter = result.rpcStatus; + this.reloadPersistentRequests(); + } + }); + this.ctx.detectChanges(); + } +} + +class PersistentDatasource implements DataSource { + + private persistentSubject = new BehaviorSubject([]); + private pageDataSubject = new BehaviorSubject>(emptyPageData()); + + public dataLoading = true; + public pageData$ = this.pageDataSubject.asObservable(); + + constructor(private translate: TranslateService, + private subscription: IWidgetSubscription) { + } + + connect(collectionViewer: CollectionViewer): Observable> { + return this.persistentSubject.asObservable(); + } + + disconnect(collectionViewer: CollectionViewer): void { + this.persistentSubject.complete(); + this.pageDataSubject.complete(); + } + + reset() { + const pageData = emptyPageData(); + this.persistentSubject.next(pageData.data); + this.pageDataSubject.next(pageData); + } + + loadPersistent(pageLink: PageLink, keyFilter: RpcStatus) { + this.dataLoading = true; + + const result = new ReplaySubject>(); + this.fetchEntities(pageLink, keyFilter).pipe( + catchError(() => of(emptyPageData())), + ).subscribe( + (pageData) => { + this.persistentSubject.next(pageData.data); + this.pageDataSubject.next(pageData); + result.next(pageData); + this.dataLoading = false; + } + ); + return result; + } + + fetchEntities(pageLink: PageLink, keyFilter: RpcStatus): Observable> { + return this.subscription.subscribeForPersistentRequests(pageLink, keyFilter); + } + + isEmpty(): Observable { + return this.persistentSubject.pipe( + map((requests) => !requests.length) + ); + } + + total(): Observable { + return this.pageDataSubject.pipe( + map((pageData) => pageData.totalElements) + ); + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/rpc-widgets.module.ts b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/rpc-widgets.module.ts index 330429ce10..a4da8f4b82 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/rpc-widgets.module.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/rpc-widgets.module.ts @@ -21,6 +21,10 @@ import { LedIndicatorComponent } from '@home/components/widget/lib/rpc/led-indic import { RoundSwitchComponent } from '@home/components/widget/lib/rpc/round-switch.component'; import { SwitchComponent } from '@home/components/widget/lib/rpc/switch.component'; import { KnobComponent } from '@home/components/widget/lib/rpc/knob.component'; +import { PersistentTableComponent } from '@home/components/widget/lib/rpc/persistent-table.component'; +import { PersistentDetailsDialogComponent } from '@home/components/widget/lib/rpc/persistent-details-dialog.component'; +import { PersistentFilterPanelComponent } from '@home/components/widget/lib/rpc/persistent-filter-panel.component'; +import { PersistentAddDialogComponent } from '@home/components/widget/lib/rpc/persistent-add-dialog.component'; @NgModule({ declarations: @@ -28,7 +32,11 @@ import { KnobComponent } from '@home/components/widget/lib/rpc/knob.component'; LedIndicatorComponent, RoundSwitchComponent, SwitchComponent, - KnobComponent + KnobComponent, + PersistentTableComponent, + PersistentDetailsDialogComponent, + PersistentAddDialogComponent, + PersistentFilterPanelComponent ], imports: [ CommonModule, @@ -38,7 +46,10 @@ import { KnobComponent } from '@home/components/widget/lib/rpc/knob.component'; LedIndicatorComponent, RoundSwitchComponent, SwitchComponent, - KnobComponent + KnobComponent, + PersistentTableComponent, + PersistentDetailsDialogComponent, + PersistentAddDialogComponent ] }) export class RpcWidgetsModule { } diff --git a/ui-ngx/src/app/modules/home/models/widget-component.models.ts b/ui-ngx/src/app/modules/home/models/widget-component.models.ts index 2a530c8b4e..f63f53d032 100644 --- a/ui-ngx/src/app/modules/home/models/widget-component.models.ts +++ b/ui-ngx/src/app/modules/home/models/widget-component.models.ts @@ -196,16 +196,18 @@ export class WidgetContext { }; controlApi: RpcApi = { - sendOneWayCommand: (method, params, timeout, persistent, requestUUID) => { + sendOneWayCommand: (method, params, timeout, persistent, + retries, additionalInfo, requestUUID) => { if (this.defaultSubscription) { - return this.defaultSubscription.sendOneWayCommand(method, params, timeout, persistent, requestUUID); + return this.defaultSubscription.sendOneWayCommand(method, params, timeout, persistent, retries, additionalInfo, requestUUID); } else { return of(null); } }, - sendTwoWayCommand: (method, params, timeout, persistent, requestUUID) => { + sendTwoWayCommand: (method, params, timeout, persistent, + retries, additionalInfo, requestUUID) => { if (this.defaultSubscription) { - return this.defaultSubscription.sendTwoWayCommand(method, params, timeout, persistent, requestUUID); + return this.defaultSubscription.sendTwoWayCommand(method, params, timeout, persistent, retries, additionalInfo, requestUUID); } else { return of(null); } diff --git a/ui-ngx/src/app/shared/components/json-object-view.component.html b/ui-ngx/src/app/shared/components/json-object-view.component.html new file mode 100644 index 0000000000..379c557b44 --- /dev/null +++ b/ui-ngx/src/app/shared/components/json-object-view.component.html @@ -0,0 +1,22 @@ + +
+ + +
+
diff --git a/ui-ngx/src/app/shared/components/json-object-view.component.scss b/ui-ngx/src/app/shared/components/json-object-view.component.scss new file mode 100644 index 0000000000..2b754b0ef2 --- /dev/null +++ b/ui-ngx/src/app/shared/components/json-object-view.component.scss @@ -0,0 +1,27 @@ +/** + * Copyright © 2016-2021 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. + */ +:host { + #tb-json-view { + width: 100%; + height: 100%; + margin-bottom: 16px; + border: 1px solid #c0c0c0; + + &:not(.fill-height) { + min-height: 100px; + } + } +} diff --git a/ui-ngx/src/app/shared/components/json-object-view.component.ts b/ui-ngx/src/app/shared/components/json-object-view.component.ts new file mode 100644 index 0000000000..5ce00b0189 --- /dev/null +++ b/ui-ngx/src/app/shared/components/json-object-view.component.ts @@ -0,0 +1,166 @@ +/// +/// Copyright © 2016-2021 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { Component, ElementRef, forwardRef, Input, OnInit, Renderer2, ViewChild } from '@angular/core'; +import { NG_VALUE_ACCESSOR } from '@angular/forms'; +import { Ace } from 'ace-builds'; +import { coerceBooleanProperty } from '@angular/cdk/coercion'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { RafService } from '@core/services/raf.service'; +import { isDefinedAndNotNull, isUndefined } from '@core/utils'; +import { getAce } from '@shared/models/ace/ace.models'; + +@Component({ + selector: 'tb-json-object-view', + templateUrl: './json-object-view.component.html', + styleUrls: ['./json-object-view.component.scss'], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => JsonObjectViewComponent), + multi: true + } + ] +}) +export class JsonObjectViewComponent implements OnInit { + + @ViewChild('jsonViewer', {static: true}) + jsonViewerElmRef: ElementRef; + + private jsonViewer: Ace.Editor; + private viewerElement: Ace.Editor; + private propagateChange = null; + private modelValue: any; + private contentValue: string; + + @Input() label: string; + + @Input() fillHeight: boolean; + + @Input() editorStyle: { [klass: string]: any }; + + @Input() sort: (key: string, value: any) => any; + + private widthValue: boolean; + + get autoWidth(): boolean { + return this.widthValue; + } + + @Input() + set autoWidth(value: boolean) { + this.widthValue = coerceBooleanProperty(value); + } + + private heigthValue: boolean; + + get autoHeight(): boolean { + return this.heigthValue; + } + + @Input() + set autoHeight(value: boolean) { + this.heigthValue = coerceBooleanProperty(value); + } + + constructor(public elementRef: ElementRef, + protected store: Store, + private raf: RafService, + private renderer: Renderer2) { + } + + ngOnInit(): void { + this.viewerElement = this.jsonViewerElmRef.nativeElement; + let editorOptions: Partial = { + mode: 'ace/mode/java', + theme: 'ace/theme/github', + showGutter: false, + showPrintMargin: false, + readOnly: true + }; + + const advancedOptions = { + enableSnippets: false, + enableBasicAutocompletion: false, + enableLiveAutocompletion: false + }; + + editorOptions = {...editorOptions, ...advancedOptions}; + getAce().subscribe( + (ace) => { + this.jsonViewer = ace.edit(this.viewerElement, editorOptions); + this.jsonViewer.session.setUseWrapMode(false); + this.jsonViewer.setValue(this.contentValue ? this.contentValue : '', -1); + if (this.contentValue && (this.widthValue || this.heigthValue)) { + this.updateEditorSize(this.viewerElement, this.contentValue, this.jsonViewer); + } + } + ); + } + + updateEditorSize(editorElement: any, content: string, editor: Ace.Editor) { + let newHeight = 200; + let newWidth = 600; + if (content && content.length > 0) { + const lines = content.split('\n'); + newHeight = 17 * lines.length + 17; + let maxLineLength = 0; + lines.forEach((row) => { + const line = row.replace(/\t/g, ' ').replace(/\n/g, ''); + const lineLength = line.length; + maxLineLength = Math.max(maxLineLength, lineLength); + }); + newWidth = 8 * maxLineLength + 16; + } + if (this.heigthValue) { + // this.renderer.setStyle(editorElement, 'minHeight', newHeight.toString() + 'px'); + this.renderer.setStyle(editorElement, 'height', newHeight.toString() + 'px'); + } + if (this.widthValue) { + this.renderer.setStyle(editorElement, 'width', newWidth.toString() + 'px'); + } + editor.resize(); + } + registerOnChange(fn: any): void { + this.propagateChange = fn; + } + + registerOnTouched(fn: any): void { + } + + writeValue(value: any): void { + this.modelValue = value; + this.contentValue = ''; + try { + if (isDefinedAndNotNull(this.modelValue)) { + this.contentValue = JSON.stringify(this.modelValue, isUndefined(this.sort) ? undefined : + (key, objectValue) => { + return this.sort(key, objectValue); + }, 2); + } + } catch (e) { + // + } + if (this.jsonViewer) { + this.jsonViewer.setValue(this.contentValue ? this.contentValue : '', -1); + if (this.contentValue && (this.widthValue || this.heigthValue)) { + this.updateEditorSize(this.viewerElement, this.contentValue, this.jsonViewer); + } + } + } + +} diff --git a/ui-ngx/src/app/shared/models/rpc.models.ts b/ui-ngx/src/app/shared/models/rpc.models.ts index 75ac132394..61a94a401e 100644 --- a/ui-ngx/src/app/shared/models/rpc.models.ts +++ b/ui-ngx/src/app/shared/models/rpc.models.ts @@ -17,15 +17,42 @@ import { TenantId } from '@shared/models/id/tenant-id'; import { RpcId } from '@shared/models/id/rpc-id'; import { DeviceId } from '@shared/models/id/device-id'; +import { TableCellButtonActionDescriptor } from '@home/components/widget/lib/table-widget.models'; export enum RpcStatus { QUEUED = 'QUEUED', DELIVERED = 'DELIVERED', SUCCESSFUL = 'SUCCESSFUL', TIMEOUT = 'TIMEOUT', - FAILED = 'FAILED' + FAILED = 'FAILED', + SENT = 'SENT', + EXPIRED = 'EXPIRED' } +export const rpcStatusColors = new Map( + [ + [RpcStatus.QUEUED, 'black'], + [RpcStatus.DELIVERED, 'green'], + [RpcStatus.SUCCESSFUL, 'green'], + [RpcStatus.TIMEOUT, 'orange'], + [RpcStatus.FAILED, 'red'], + [RpcStatus.SENT, 'green'], + [RpcStatus.EXPIRED, 'red'] + ] +); + +export const rpcStatusTranslation = new Map( + [ + [RpcStatus.QUEUED, 'widgets.persistent-table.rpc-status.QUEUED'], + [RpcStatus.DELIVERED, 'widgets.persistent-table.rpc-status.DELIVERED'], + [RpcStatus.SUCCESSFUL, 'widgets.persistent-table.rpc-status.SUCCESSFUL'], + [RpcStatus.TIMEOUT, 'widgets.persistent-table.rpc-status.TIMEOUT'], + [RpcStatus.FAILED, 'widgets.persistent-table.rpc-status.FAILED'], + [RpcStatus.SENT, 'widgets.persistent-table.rpc-status.SENT'], + [RpcStatus.EXPIRED, 'widgets.persistent-table.rpc-status.EXPIRED'] + ] +); + export interface PersistentRpc { id: RpcId; createdTime: number; @@ -34,7 +61,29 @@ export interface PersistentRpc { response: any; request: { id: string; + oneway: boolean; + body: { + method: string; + params: string; + }; + retries: null | number; }; deviceId: DeviceId; tenantId: TenantId; + additionalInfo?: string; +} + +export interface PersistentRpcData extends PersistentRpc { + actionCellButtons?: TableCellButtonActionDescriptor[]; + hasActions?: boolean; +} + +export interface RequestData { + persistentUpdated: boolean; + method?: string; + oneWayElseTwoWay?: boolean; + persistentPollingInterval?: number; + retries?: number; + params?: object; + additionalInfo?: object; } diff --git a/ui-ngx/src/app/shared/shared.module.ts b/ui-ngx/src/app/shared/shared.module.ts index 90b80393dd..3941dd902a 100644 --- a/ui-ngx/src/app/shared/shared.module.ts +++ b/ui-ngx/src/app/shared/shared.module.ts @@ -94,6 +94,7 @@ import { SocialSharePanelComponent } from '@shared/components/socialshare-panel. import { RelationTypeAutocompleteComponent } from '@shared/components/relation/relation-type-autocomplete.component'; import { EntityListSelectComponent } from '@shared/components/entity/entity-list-select.component'; import { JsonObjectEditComponent } from '@shared/components/json-object-edit.component'; +import { JsonObjectViewComponent, } from '@shared/components/json-object-view.component'; import { FooterFabButtonsComponent } from '@shared/components/footer-fab-buttons.component'; import { CircularProgressDirective } from '@shared/components/circular-progress.directive'; import { @@ -231,6 +232,7 @@ export function MarkedOptionsFactory(markedOptionsService: MarkedOptionsService) RelationTypeAutocompleteComponent, SocialSharePanelComponent, JsonObjectEditComponent, + JsonObjectViewComponent, JsonContentComponent, JsFuncComponent, FabTriggerDirective, @@ -376,6 +378,7 @@ export function MarkedOptionsFactory(markedOptionsService: MarkedOptionsService) RelationTypeAutocompleteComponent, SocialSharePanelComponent, JsonObjectEditComponent, + JsonObjectViewComponent, JsonContentComponent, JsFuncComponent, FabTriggerDirective, diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index bd14f73e27..79f8e18030 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -3238,7 +3238,49 @@ "update-timeseries": "Update timeseries", "value": "Value" }, - "invalid-qr-code-text": "Invalid input text for QR code. Input should have a string type" + "invalid-qr-code-text": "Invalid input text for QR code. Input should have a string type", + "persistent-table": { + "rpc-id": "RPC ID", + "message-type": "Message type", + "method": "Method", + "params": "Params", + "created-time": "Created time", + "expiration-time": "Expiration time", + "retries": "Retries", + "status": "Status", + "filter": "Filter", + "refresh": "Refresh", + "add": "Add RPC request", + "details": "Details", + "delete": "Delete", + "delete-request-title": "Delete Persistent RPC request", + "delete-request-text": "Are you sure you want to delete request?", + "details-title": "Details RPC ID: ", + "additional-info": "Additional info", + "response": "Response", + "any-status": "Any status", + "rpc-status-list": "RPC status list", + "no-request-prompt": "No request to display", + "send-request": "Send request", + "add-title": "Create Persistent RPC request", + "method-error": "Method is required.", + "timeout-error": "Min timeout value is 5000 (5 seconds).", + "white-space-error": "White space is not allowed.", + "rpc-status": { + "QUEUED": "QUEUED", + "SENT": "SENT", + "DELIVERED": "DELIVERED", + "SUCCESSFUL": "SUCCESSFUL", + "TIMEOUT": "TIMEOUT", + "EXPIRED": "EXPIRED", + "FAILED": "FAILED" + }, + "rpc-search-status-all": "ALL", + "message-types": { + "false": "Two-way", + "true": "One-way" + } + } }, "icon": { "icon": "Icon", diff --git a/ui-ngx/src/assets/locale/locale.constant-ru_RU.json b/ui-ngx/src/assets/locale/locale.constant-ru_RU.json index 66d99281bb..dfc5226d6d 100644 --- a/ui-ngx/src/assets/locale/locale.constant-ru_RU.json +++ b/ui-ngx/src/assets/locale/locale.constant-ru_RU.json @@ -1787,6 +1787,47 @@ "update-attribute": "Обновить атрибут", "update-timeseries": "Обновить телеметрию", "value": "Значение" + }, + "persistent-table": { + "rpc-id": "RPC ID", + "message-type": "Тип сообщения", + "method": "Метод", + "params": "Параметры", + "created-time": "Время создания", + "expiration-time": "Время жизни", + "retries": "Повторные попытки", + "status": "Статус", + "filter": "Фильтр", + "refresh": "Обновить", + "add": "Добавить RPC запрос", + "details": "Детали", + "delete": "Удалить", + "delete-request-title": "Удалить RPC запрос", + "delete-request-text": "Вы точно хотите удалить RPC запрос?", + "details-title": "Детали RPC ID: ", + "additional-info": "Дополнительная информация", + "response": "Ответ", + "any-status": "Любой статус", + "rpc-status-list": "Список RPC статусов", + "no-request-prompt": "Запросы не найдены", + "send-request": "Отправить запрос", + "add-title": "Добавить новый RPC запрос", + "method-error": "Метод обязателен.", + "white-space-error": "Пробелы не допускаются.", + "rpc-status": { + "QUEUED": "В ОЧЕРЕДИ", + "SENT": "ОТПРАВЛЕННО", + "DELIVERED": "ДОСТАВЛЕННО", + "SUCCESSFUL": "УСПЕШНО", + "TIMEOUT": "ВРЕМЯ ИСТЕКЛО", + "EXPIRED": "ПРОСРОЧЕНО", + "FAILED": "НЕУДАЧНО" + }, + "rpc-search-status-all": "ВСЕ", + "message-types": { + "false": "Двусторонний", + "true": "Односторонний" + } } }, "icon": { diff --git a/ui-ngx/src/assets/locale/locale.constant-uk_UA.json b/ui-ngx/src/assets/locale/locale.constant-uk_UA.json index 43691918dd..be31aa7375 100644 --- a/ui-ngx/src/assets/locale/locale.constant-uk_UA.json +++ b/ui-ngx/src/assets/locale/locale.constant-uk_UA.json @@ -2359,6 +2359,47 @@ "update-attribute": "Оновити атрибут", "update-timeseries": "Оновити телеметрію", "value": "Значення" + }, + "persistent-table": { + "rpc-id": "RPC ID", + "message-type": "Тип повідомлення", + "method": "Метод", + "params": "Параметри", + "created-time": "Час створення", + "expiration-time": "Час життя", + "retries": "Повторні спроби", + "status": "Статус", + "filter": "Фільтр", + "refresh": "Оновити", + "add": "Додати RPC запит", + "details": "Деталі", + "delete": "Видалити", + "delete-request-title": "Видалити RPC запит", + "delete-request-text": "Ви впевнені, що хочете видалити RPC запит?", + "details-title": "Деталі RPC ID: ", + "additional-info": "Додаткова інформація", + "response": "Відповідь", + "any-status": "Будь-який статус", + "rpc-status-list": "Список RPC статусів", + "no-request-prompt": "Запитів не знайдено", + "send-request": "Відправити запит", + "add-title": "Додати новий RPC запит", + "method-error": "Необхідно вказати метод.", + "white-space-error": "Пробіли не допускаються.", + "rpc-status": { + "QUEUED": "В ЧЕРЗІ", + "SENT": "ВІДПРАВЛЕНО", + "DELIVERED": "ДОСТАВЛЕННО", + "SUCCESSFUL": "УСПІШНО", + "TIMEOUT": "ЧАС МИНУВ", + "EXPIRED": "ПРОСРОЧЕНО", + "FAILED": "НЕ ВДАЛО" + }, + "rpc-search-status-all": "ВСІ", + "message-types": { + "false": "Двусторонній", + "true": "Односторонній" + } } }, "white-labeling": { From 721906b9214128966c318fa2fc14536c18deef9c Mon Sep 17 00:00:00 2001 From: ArtemDzhereleiko Date: Mon, 29 Nov 2021 17:46:38 +0200 Subject: [PATCH 009/798] UI: Add error message for widget --- .../src/app/core/api/widget-subscription.ts | 33 ++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/ui-ngx/src/app/core/api/widget-subscription.ts b/ui-ngx/src/app/core/api/widget-subscription.ts index c548b0fbab..1634bb7b87 100644 --- a/ui-ngx/src/app/core/api/widget-subscription.ts +++ b/ui-ngx/src/app/core/api/widget-subscription.ts @@ -787,7 +787,38 @@ export class WidgetSubscription implements IWidgetSubscription { } else if (!this.targetDeviceId) { return throwError(new Error('Target device is not set!')); } - return this.ctx.deviceService.getPersistedRpcRequests(this.targetDeviceId, pageLink, keyFilter); + const rpcSubject: Subject = new Subject(); + + this.ctx.deviceService.getPersistedRpcRequests(this.targetDeviceId, pageLink, keyFilter).subscribe( + (responseBody) => { + rpcSubject.next(responseBody); + rpcSubject.complete(); + }, + (rejection: HttpErrorResponse) => { + const index = this.executingSubjects.indexOf(rpcSubject); + if (index >= 0) { + this.executingSubjects.splice( index, 1 ); + } + this.executingRpcRequest = this.executingSubjects.length > 0; + this.callbacks.rpcStateChanged(this); + if (!this.executingRpcRequest || rejection.status === 504) { + this.rpcRejection = rejection; + if (rejection.status === 504) { + this.rpcErrorText = 'Request Timeout.'; + } else { + this.rpcErrorText = 'Error : ' + rejection.status + ' - ' + rejection.statusText; + const error = this.extractRejectionErrorText(rejection); + if (error) { + this.rpcErrorText += '
'; + this.rpcErrorText += error; + } + } + this.callbacks.onRpcFailed(this); + } + rpcSubject.error(rejection); + } + ); + return rpcSubject.asObservable(); } private extractRejectionErrorText(rejection: HttpErrorResponse) { From ec10a425b878d155417705ca5e536ae4f951070c Mon Sep 17 00:00:00 2001 From: Rene Moser Date: Tue, 30 Nov 2021 17:01:40 +0100 Subject: [PATCH 010/798] Add option to configure spring's server.forward_headers_strategy --- application/src/main/resources/thingsboard.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index 69bfd06bc8..813e75ee57 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -19,6 +19,8 @@ server: address: "${HTTP_BIND_ADDRESS:0.0.0.0}" # Server bind port port: "${HTTP_BIND_PORT:8080}" + # Server forward headers strategy + forward_headers_strategy: "${HTTP_FORWARD_HEADERS_STRATEGY:NONE}" # Server SSL configuration ssl: # Enable/disable SSL support From 05408d357ed2cbd396a40a1dc9b16930923e687c Mon Sep 17 00:00:00 2001 From: ArtemDzhereleiko Date: Wed, 1 Dec 2021 11:32:32 +0200 Subject: [PATCH 011/798] UI: On tables hide page size option on mobile view --- .../attribute/attribute-table.component.html | 1 + .../attribute/attribute-table.component.ts | 23 ++++++++++-- .../entity/entities-table.component.html | 1 + .../entity/entities-table.component.ts | 21 ++++++++++- .../relation/relation-table.component.html | 3 +- .../relation/relation-table.component.ts | 35 +++++++++++++++++-- .../lib/alarms-table-widget.component.html | 3 +- .../lib/alarms-table-widget.component.ts | 15 ++++++++ .../lib/entities-table-widget.component.html | 3 +- .../lib/entities-table-widget.component.ts | 20 +++++++++++ .../timeseries-table-widget.component.html | 3 +- .../lib/timeseries-table-widget.component.ts | 15 ++++++++ 12 files changed, 133 insertions(+), 10 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/attribute/attribute-table.component.html b/ui-ngx/src/app/modules/home/components/attribute/attribute-table.component.html index ead92fda03..c0612646f3 100644 --- a/ui-ngx/src/app/modules/home/components/attribute/attribute-table.component.html +++ b/ui-ngx/src/app/modules/home/components/attribute/attribute-table.component.html @@ -196,6 +196,7 @@ [pageIndex]="pageLink.page" [pageSize]="pageLink.pageSize" [pageSizeOptions]="[10, 20, 30]" + [hidePageSize]="hidePageSize" showFirstLastButtons> , private attributeService: AttributeService, private telemetryWsService: TelemetryWebsocketService, @@ -184,7 +189,8 @@ export class AttributeTableComponent extends PageComponent implements AfterViewI private dashboardUtils: DashboardUtilsService, private widgetService: WidgetService, private zone: NgZone, - private cd: ChangeDetectorRef) { + private cd: ChangeDetectorRef, + private breakpointObserver: BreakpointObserver) { super(store); this.dirtyValue = !this.activeValue; const sortOrder: SortOrder = { property: 'key', direction: Direction.ASC }; @@ -193,6 +199,19 @@ export class AttributeTableComponent extends PageComponent implements AfterViewI } ngOnInit() { + this.breakpointObserverSubscription$ = this.breakpointObserver + .observe(MediaBreakpoints['gt-xs']).subscribe( + () => { + this.hidePageSize = !this.breakpointObserver.isMatched(MediaBreakpoints['gt-xs']); + this.cd.detectChanges(); + } + ); + } + + ngOnDestroy() { + if (this.breakpointObserverSubscription$) { + this.breakpointObserverSubscription$.unsubscribe(); + } } attributeScopeChanged(attributeScope: TelemetryType) { diff --git a/ui-ngx/src/app/modules/home/components/entity/entities-table.component.html b/ui-ngx/src/app/modules/home/components/entity/entities-table.component.html index c4f470bbd2..a7b15670bb 100644 --- a/ui-ngx/src/app/modules/home/components/entity/entities-table.component.html +++ b/ui-ngx/src/app/modules/home/components/entity/entities-table.component.html @@ -266,6 +266,7 @@ [pageIndex]="pageLink.page" [pageSize]="pageLink.pageSize" [pageSizeOptions]="pageSizeOptions" + [hidePageSize]="hidePageSize" showFirstLastButtons> diff --git a/ui-ngx/src/app/modules/home/components/entity/entities-table.component.ts b/ui-ngx/src/app/modules/home/components/entity/entities-table.component.ts index 49386db131..cca386570d 100644 --- a/ui-ngx/src/app/modules/home/components/entity/entities-table.component.ts +++ b/ui-ngx/src/app/modules/home/components/entity/entities-table.component.ts @@ -64,6 +64,8 @@ import { DomSanitizer, SafeHtml } from '@angular/platform-browser'; import { TbAnchorComponent } from '@shared/components/tb-anchor.component'; import { isDefined, isUndefined } from '@core/utils'; import { HasUUID } from '@shared/models/id/has-uuid'; +import { MediaBreakpoints } from '@shared/models/constants'; +import { BreakpointObserver } from '@angular/cdk/layout'; @Component({ selector: 'tb-entities-table', @@ -120,6 +122,9 @@ export class EntitiesTableComponent extends PageComponent implements AfterViewIn private updateDataSubscription: Subscription; private viewInited = false; + private breakpointObserverSubscription$: Subscription; + public hidePageSize = true; + constructor(protected store: Store, public route: ActivatedRoute, public translate: TranslateService, @@ -127,7 +132,8 @@ export class EntitiesTableComponent extends PageComponent implements AfterViewIn private dialogService: DialogService, private domSanitizer: DomSanitizer, private cd: ChangeDetectorRef, - private componentFactoryResolver: ComponentFactoryResolver) { + private componentFactoryResolver: ComponentFactoryResolver, + private breakpointObserver: BreakpointObserver) { super(store); } @@ -137,6 +143,19 @@ export class EntitiesTableComponent extends PageComponent implements AfterViewIn } else { this.init(this.route.snapshot.data.entitiesTableConfig); } + this.breakpointObserverSubscription$ = this.breakpointObserver + .observe(MediaBreakpoints['gt-xs']).subscribe( + () => { + this.hidePageSize = !this.breakpointObserver.isMatched(MediaBreakpoints['gt-xs']); + this.cd.detectChanges(); + } + ); + } + + ngOnDestroy() { + if (this.breakpointObserverSubscription$) { + this.breakpointObserverSubscription$.unsubscribe(); + } } ngOnChanges(changes: SimpleChanges): void { diff --git a/ui-ngx/src/app/modules/home/components/relation/relation-table.component.html b/ui-ngx/src/app/modules/home/components/relation/relation-table.component.html index 01c2e0e4ab..b5ae8c2d52 100644 --- a/ui-ngx/src/app/modules/home/components/relation/relation-table.component.html +++ b/ui-ngx/src/app/modules/home/components/relation/relation-table.component.html @@ -165,6 +165,7 @@ + [pageSizeOptions]="[10, 20, 30]" + [hidePageSize]="hidePageSize"> diff --git a/ui-ngx/src/app/modules/home/components/relation/relation-table.component.ts b/ui-ngx/src/app/modules/home/components/relation/relation-table.component.ts index b10ed82249..2ea0ccfa12 100644 --- a/ui-ngx/src/app/modules/home/components/relation/relation-table.component.ts +++ b/ui-ngx/src/app/modules/home/components/relation/relation-table.component.ts @@ -14,7 +14,16 @@ /// limitations under the License. /// -import { AfterViewInit, ChangeDetectionStrategy, Component, ElementRef, Input, OnInit, ViewChild } from '@angular/core'; +import { + AfterViewInit, + ChangeDetectionStrategy, + ChangeDetectorRef, + Component, + ElementRef, + Input, + OnInit, + ViewChild +} from '@angular/core'; import { PageComponent } from '@shared/components/page.component'; import { PageLink } from '@shared/models/page/page-link'; import { MatPaginator } from '@angular/material/paginator'; @@ -26,7 +35,7 @@ import { MatDialog } from '@angular/material/dialog'; import { DialogService } from '@core/services/dialog.service'; import { EntityRelationService } from '@core/http/entity-relation.service'; import { Direction, SortOrder } from '@shared/models/page/sort-order'; -import { forkJoin, fromEvent, merge, Observable } from 'rxjs'; +import { forkJoin, fromEvent, merge, Observable, Subscription } from 'rxjs'; import { debounceTime, distinctUntilChanged, tap } from 'rxjs/operators'; import { EntityRelation, @@ -38,6 +47,8 @@ import { import { EntityId } from '@shared/models/id/entity-id'; import { RelationsDatasource } from '../../models/datasource/relation-datasource'; import { RelationDialogComponent, RelationDialogData } from '@home/components/relation/relation-dialog.component'; +import { MediaBreakpoints } from '@shared/models/constants'; +import { BreakpointObserver } from '@angular/cdk/layout'; @Component({ selector: 'tb-relation-table', @@ -65,6 +76,9 @@ export class RelationTableComponent extends PageComponent implements AfterViewIn viewsInited = false; + private breakpointObserverSubscription$: Subscription; + public hidePageSize = true; + @Input() set active(active: boolean) { if (this.activeValue !== active) { @@ -100,7 +114,9 @@ export class RelationTableComponent extends PageComponent implements AfterViewIn private entityRelationService: EntityRelationService, public translate: TranslateService, public dialog: MatDialog, - private dialogService: DialogService) { + private dialogService: DialogService, + private cd: ChangeDetectorRef, + private breakpointObserver: BreakpointObserver) { super(store); this.dirtyValue = !this.activeValue; const sortOrder: SortOrder = { property: 'type', direction: Direction.ASC }; @@ -111,6 +127,19 @@ export class RelationTableComponent extends PageComponent implements AfterViewIn } ngOnInit() { + this.breakpointObserverSubscription$ = this.breakpointObserver + .observe(MediaBreakpoints['gt-xs']).subscribe( + () => { + this.hidePageSize = !this.breakpointObserver.isMatched(MediaBreakpoints['gt-xs']); + this.cd.detectChanges(); + } + ); + } + + ngOnDestroy() { + if (this.breakpointObserverSubscription$) { + this.breakpointObserverSubscription$.unsubscribe(); + } } updateColumns() { diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/alarms-table-widget.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/alarms-table-widget.component.html index a86da23f59..872e040bad 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/alarms-table-widget.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/alarms-table-widget.component.html @@ -15,7 +15,7 @@ limitations under the License. --> -
+
@@ -147,6 +147,7 @@ [pageIndex]="pageLink.page" [pageSize]="pageLink.pageSize" [pageSizeOptions]="pageSizeOptions" + [hidePageSize]="hidePageSize" showFirstLastButtons>
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/alarms-table-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/alarms-table-widget.component.ts index 7b32d84e71..73c7ea20cd 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/alarms-table-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/alarms-table-widget.component.ts @@ -123,6 +123,7 @@ import { } from '@home/components/widget/lib/alarm-filter-panel.component'; import { entityFields } from '@shared/models/entity.models'; import { coerceBooleanProperty } from '@angular/cdk/coercion'; +import { ResizeObserver } from '@juggle/resize-observer'; interface AlarmsTableWidgetSettings extends TableWidgetSettings { alarmsTitle: string; @@ -152,6 +153,7 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit, @Input() ctx: WidgetContext; + @ViewChild('alarmWidgetContainer', {static: true}) alarmWidgetContainerRef: ElementRef; @ViewChild('searchInput') searchInputField: ElementRef; @ViewChild(MatPaginator) paginator: MatPaginator; @ViewChild(MatSort) sort: MatSort; @@ -168,6 +170,7 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit, public displayedColumns: string[] = []; public alarmsDatasource: AlarmsDatasource; public noDataDisplayMessageText: string; + public hidePageSize = false; private setCellButtonAction: boolean; private cellContentCache: Array = []; @@ -177,6 +180,7 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit, private settings: AlarmsTableWidgetSettings; private widgetConfig: WidgetConfig; private subscription: IWidgetSubscription; + private widgetResize$: ResizeObserver; private alarmsTitlePattern: string; @@ -257,6 +261,14 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit, this.widgetTimewindowChanged$ = this.ctx.defaultSubscription.widgetTimewindowChanged$.subscribe( () => this.pageLink.page = 0 ); + this.widgetResize$ = new ResizeObserver(() => { + const showHidePageSize = this.ctx.$container[0].offsetWidth < 450; + if (showHidePageSize !== this.hidePageSize) { + this.hidePageSize = showHidePageSize; + this.ctx.detectChanges(); + } + }); + this.widgetResize$.observe(this.alarmWidgetContainerRef.nativeElement); } } @@ -265,6 +277,9 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit, this.widgetTimewindowChanged$.unsubscribe(); this.widgetTimewindowChanged$ = null; } + if (this.widgetResize$) { + this.widgetResize$.disconnect(); + } } ngAfterViewInit(): void { diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/entities-table-widget.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/entities-table-widget.component.html index 74ccd53ec2..7f8ae9b9b1 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/entities-table-widget.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/entities-table-widget.component.html @@ -15,7 +15,7 @@ limitations under the License. --> -
+
@@ -106,6 +106,7 @@ [pageIndex]="pageLink.page" [pageSize]="pageLink.pageSize" [pageSizeOptions]="pageSizeOptions" + [hidePageSize]="hidePageSize" showFirstLastButtons>
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/entities-table-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/entities-table-widget.component.ts index bd52f2ab9b..a89c1ebe82 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/entities-table-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/entities-table-widget.component.ts @@ -107,6 +107,7 @@ import { sortItems } from '@shared/models/page/page-link'; import { entityFields } from '@shared/models/entity.models'; import { DatePipe } from '@angular/common'; import { coerceBooleanProperty } from '@angular/cdk/coercion'; +import { ResizeObserver } from '@juggle/resize-observer'; interface EntitiesTableWidgetSettings extends TableWidgetSettings { entitiesTitle: string; @@ -129,6 +130,7 @@ export class EntitiesTableWidgetComponent extends PageComponent implements OnIni @Input() ctx: WidgetContext; + @ViewChild('entitiesWidgetContainer', {static: true}) entitiesWidgetContainerRef: ElementRef; @ViewChild('searchInput') searchInputField: ElementRef; @ViewChild(MatPaginator) paginator: MatPaginator; @ViewChild(MatSort) sort: MatSort; @@ -144,6 +146,7 @@ export class EntitiesTableWidgetComponent extends PageComponent implements OnIni public displayedColumns: string[] = []; public entityDatasource: EntityDatasource; public noDataDisplayMessageText: string; + public hidePageSize = false; private setCellButtonAction: boolean; private cellContentCache: Array = []; @@ -153,6 +156,7 @@ export class EntitiesTableWidgetComponent extends PageComponent implements OnIni private settings: EntitiesTableWidgetSettings; private widgetConfig: WidgetConfig; private subscription: IWidgetSubscription; + private widgetResize$: ResizeObserver; private entitiesTitlePattern: string; @@ -211,6 +215,22 @@ export class EntitiesTableWidgetComponent extends PageComponent implements OnIni this.initializeConfig(); this.updateDatasources(); this.ctx.updateWidgetParams(); + if (this.displayPagination) { + this.widgetResize$ = new ResizeObserver(() => { + const showHidePageSize = this.ctx.$container[0].offsetWidth < 450; + if (showHidePageSize !== this.hidePageSize) { + this.hidePageSize = showHidePageSize; + this.ctx.detectChanges(); + } + }); + this.widgetResize$.observe(this.entitiesWidgetContainerRef.nativeElement); + } + } + + ngOnDestroy(): void { + if (this.widgetResize$) { + this.widgetResize$.disconnect(); + } } ngAfterViewInit(): void { diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/timeseries-table-widget.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/timeseries-table-widget.component.html index 8c3b9084b0..236d81036c 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/timeseries-table-widget.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/timeseries-table-widget.component.html @@ -15,7 +15,7 @@ limitations under the License. --> -
+
@@ -112,6 +112,7 @@ [pageIndex]="source.pageLink.page" [pageSize]="source.pageLink.pageSize" [pageSizeOptions]="pageSizeOptions" + [hidePageSize]="hidePageSize" showFirstLastButtons> diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/timeseries-table-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/timeseries-table-widget.component.ts index de8cec8dc7..183832ae90 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/timeseries-table-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/timeseries-table-widget.component.ts @@ -71,6 +71,7 @@ import { SubscriptionEntityInfo } from '@core/api/widget-api.models'; import { DatePipe } from '@angular/common'; import { parseData } from '@home/components/widget/lib/maps/common-maps-utils'; import { coerceBooleanProperty } from '@angular/cdk/coercion'; +import { ResizeObserver } from '@juggle/resize-observer'; export interface TimeseriesTableWidgetSettings extends TableWidgetSettings { showTimestamp: boolean; @@ -115,6 +116,7 @@ export class TimeseriesTableWidgetComponent extends PageComponent implements OnI @Input() ctx: WidgetContext; + @ViewChild('timeseriesWidgetContainer', {static: true}) timeseriesWidgetContainerRef: ElementRef; @ViewChild('searchInput') searchInputField: ElementRef; @ViewChildren(MatPaginator) paginators: QueryList; @ViewChildren(MatSort) sorts: QueryList; @@ -128,6 +130,7 @@ export class TimeseriesTableWidgetComponent extends PageComponent implements OnI public sources: TimeseriesTableSource[]; public sourceIndex: number; public noDataDisplayMessageText: string; + public hidePageSize = false; private setCellButtonAction: boolean; private cellContentCache: Array = []; @@ -150,6 +153,7 @@ export class TimeseriesTableWidgetComponent extends PageComponent implements OnI private subscriptions: Subscription[] = []; private widgetTimewindowChanged$: Subscription; + private widgetResize$: ResizeObserver; private searchAction: WidgetAction = { name: 'action.search', @@ -190,6 +194,14 @@ export class TimeseriesTableWidgetComponent extends PageComponent implements OnI }); } ); + this.widgetResize$ = new ResizeObserver(() => { + const showHidePageSize = this.ctx.$container[0].offsetWidth < 450; + if (showHidePageSize !== this.hidePageSize) { + this.hidePageSize = showHidePageSize; + this.ctx.detectChanges(); + } + }); + this.widgetResize$.observe(this.timeseriesWidgetContainerRef.nativeElement); } } @@ -198,6 +210,9 @@ export class TimeseriesTableWidgetComponent extends PageComponent implements OnI this.widgetTimewindowChanged$.unsubscribe(); this.widgetTimewindowChanged$ = null; } + if (this.widgetResize$) { + this.widgetResize$.disconnect(); + } } ngAfterViewInit(): void { From 5f11fa9b6cd04370d85a7075fe00ae85880e8035 Mon Sep 17 00:00:00 2001 From: ArtemDzhereleiko Date: Wed, 1 Dec 2021 12:23:06 +0200 Subject: [PATCH 012/798] UI: On tables hide page size option on mobile view --- .../home/components/widget/lib/alarms-table-widget.component.ts | 2 +- .../components/widget/lib/entities-table-widget.component.ts | 2 +- .../components/widget/lib/timeseries-table-widget.component.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/alarms-table-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/alarms-table-widget.component.ts index 73c7ea20cd..8f003141e8 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/alarms-table-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/alarms-table-widget.component.ts @@ -262,7 +262,7 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit, () => this.pageLink.page = 0 ); this.widgetResize$ = new ResizeObserver(() => { - const showHidePageSize = this.ctx.$container[0].offsetWidth < 450; + const showHidePageSize = this.ctx.$container[0].offsetWidth < 500; if (showHidePageSize !== this.hidePageSize) { this.hidePageSize = showHidePageSize; this.ctx.detectChanges(); diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/entities-table-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/entities-table-widget.component.ts index a89c1ebe82..2c887a1668 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/entities-table-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/entities-table-widget.component.ts @@ -217,7 +217,7 @@ export class EntitiesTableWidgetComponent extends PageComponent implements OnIni this.ctx.updateWidgetParams(); if (this.displayPagination) { this.widgetResize$ = new ResizeObserver(() => { - const showHidePageSize = this.ctx.$container[0].offsetWidth < 450; + const showHidePageSize = this.ctx.$container[0].offsetWidth < 500; if (showHidePageSize !== this.hidePageSize) { this.hidePageSize = showHidePageSize; this.ctx.detectChanges(); diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/timeseries-table-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/timeseries-table-widget.component.ts index 183832ae90..cc35c481a8 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/timeseries-table-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/timeseries-table-widget.component.ts @@ -195,7 +195,7 @@ export class TimeseriesTableWidgetComponent extends PageComponent implements OnI } ); this.widgetResize$ = new ResizeObserver(() => { - const showHidePageSize = this.ctx.$container[0].offsetWidth < 450; + const showHidePageSize = this.ctx.$container[0].offsetWidth < 500; if (showHidePageSize !== this.hidePageSize) { this.hidePageSize = showHidePageSize; this.ctx.detectChanges(); From 12dcbf0e5e7e73abd3920b18dfaa74273a7f4006 Mon Sep 17 00:00:00 2001 From: ArtemDzhereleiko Date: Wed, 1 Dec 2021 12:37:30 +0200 Subject: [PATCH 013/798] UI: Hide pageSizeOption on persistent table widget --- .../lib/rpc/persistent-table.component.html | 3 ++- .../lib/rpc/persistent-table.component.ts | 20 +++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-table.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-table.component.html index c4fc2428be..fd86fd92cc 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-table.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-table.component.html @@ -15,7 +15,7 @@ limitations under the License. --> -
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-table.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-table.component.ts index 8f5ccab14b..f2c6f9e13f 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-table.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-table.component.ts @@ -67,6 +67,7 @@ import { PERSISTENT_FILTER_PANEL_DATA, PersistentFilterPanelComponent, PersistentFilterPanelData } from '@home/components/widget/lib/rpc/persistent-filter-panel.component'; import { PersistentAddDialogComponent } from '@home/components/widget/lib/rpc/persistent-add-dialog.component'; +import { ResizeObserver } from '@juggle/resize-observer'; interface PersistentTableWidgetSettings extends TableWidgetSettings { defaultSortOrder: string; @@ -97,6 +98,7 @@ export class PersistentTableComponent extends PageComponent implements OnInit { @Input() ctx: WidgetContext; + @ViewChild('persistentWidgetContainer', {static: true}) persistentWidgetContainerRef: ElementRef; @ViewChild(MatPaginator) paginator: MatPaginator; @ViewChild(MatSort) sort: MatSort; @@ -111,6 +113,7 @@ export class PersistentTableComponent extends PageComponent implements OnInit { private displayDetails = true; private allowDelete = true; private displayTableColumns: string[]; + private widgetResize$: ResizeObserver; public persistentDatasource: PersistentDatasource; public noDataDisplayMessageText: string; @@ -123,6 +126,7 @@ export class PersistentTableComponent extends PageComponent implements OnInit { public pageSizeOptions; public actionCellButtonAction: PersistentTableWidgetActionDescriptor[] = []; public displayedColumns: string[]; + public hidePageSize = false; constructor(protected store: Store, private elementRef: ElementRef, @@ -143,6 +147,22 @@ export class PersistentTableComponent extends PageComponent implements OnInit { this.subscription = this.ctx.defaultSubscription; this.initializeConfig(); this.ctx.updateWidgetParams(); + if (this.displayPagination) { + this.widgetResize$ = new ResizeObserver(() => { + const showHidePageSize = this.ctx.$container[0].offsetWidth < 500; + if (showHidePageSize !== this.hidePageSize) { + this.hidePageSize = showHidePageSize; + this.ctx.detectChanges(); + } + }); + this.widgetResize$.observe(this.persistentWidgetContainerRef.nativeElement); + } + } + + ngOnDestroy() { + if (this.widgetResize$) { + this.widgetResize$.disconnect(); + } } ngAfterViewInit(): void { From 33508f558e531b10801408b6b42a415ec368a96b Mon Sep 17 00:00:00 2001 From: ArtemDzhereleiko Date: Thu, 2 Dec 2021 13:18:42 +0200 Subject: [PATCH 014/798] UI: Hide pageSizeOption on persistent table widget --- .../components/widget/lib/rpc/persistent-table.component.ts | 3 ++- ui-ngx/src/app/shared/models/constants.ts | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-table.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-table.component.ts index f2c6f9e13f..c7d4da0295 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-table.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-table.component.ts @@ -68,6 +68,7 @@ import { } from '@home/components/widget/lib/rpc/persistent-filter-panel.component'; import { PersistentAddDialogComponent } from '@home/components/widget/lib/rpc/persistent-add-dialog.component'; import { ResizeObserver } from '@juggle/resize-observer'; +import { hidePageSizePixelValue } from '@shared/models/constants'; interface PersistentTableWidgetSettings extends TableWidgetSettings { defaultSortOrder: string; @@ -149,7 +150,7 @@ export class PersistentTableComponent extends PageComponent implements OnInit { this.ctx.updateWidgetParams(); if (this.displayPagination) { this.widgetResize$ = new ResizeObserver(() => { - const showHidePageSize = this.ctx.$container[0].offsetWidth < 500; + const showHidePageSize = this.persistentWidgetContainerRef.nativeElement.offsetWidth < hidePageSizePixelValue; if (showHidePageSize !== this.hidePageSize) { this.hidePageSize = showHidePageSize; this.ctx.detectChanges(); diff --git a/ui-ngx/src/app/shared/models/constants.ts b/ui-ngx/src/app/shared/models/constants.ts index 03b38345ca..4699a6338c 100644 --- a/ui-ngx/src/app/shared/models/constants.ts +++ b/ui-ngx/src/app/shared/models/constants.ts @@ -241,6 +241,7 @@ export const contentTypesMap = new Map( ] ); +export const hidePageSizePixelValue = 550; export const customTranslationsPrefix = 'custom.'; export const i18nPrefix = 'i18n'; From 76dccab3f7326698d03f6f3c1473e35e0237d7d5 Mon Sep 17 00:00:00 2001 From: ArtemDzhereleiko Date: Thu, 2 Dec 2021 13:53:00 +0200 Subject: [PATCH 015/798] UI: On tables hide page size option on mobile view --- .../attribute/attribute-table.component.html | 2 +- .../attribute/attribute-table.component.ts | 33 ++++++++++--------- .../entity/entities-table.component.html | 2 +- .../entity/entities-table.component.ts | 32 +++++++++--------- .../relation/relation-table.component.html | 2 +- .../relation/relation-table.component.ts | 33 ++++++++++--------- .../manage-widget-actions.component.html | 2 +- .../action/manage-widget-actions.component.ts | 31 +++++++++++++++-- .../lib/alarms-table-widget.component.ts | 3 +- .../lib/entities-table-widget.component.ts | 3 +- .../lib/timeseries-table-widget.component.ts | 3 +- ui-ngx/src/app/shared/models/constants.ts | 1 + 12 files changed, 91 insertions(+), 56 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/attribute/attribute-table.component.html b/ui-ngx/src/app/modules/home/components/attribute/attribute-table.component.html index c0612646f3..493bfe3e2d 100644 --- a/ui-ngx/src/app/modules/home/components/attribute/attribute-table.component.html +++ b/ui-ngx/src/app/modules/home/components/attribute/attribute-table.component.html @@ -15,7 +15,7 @@ limitations under the License. --> -
+
diff --git a/ui-ngx/src/app/modules/home/components/attribute/attribute-table.component.ts b/ui-ngx/src/app/modules/home/components/attribute/attribute-table.component.ts index e4a41251d8..1bd4dea53f 100644 --- a/ui-ngx/src/app/modules/home/components/attribute/attribute-table.component.ts +++ b/ui-ngx/src/app/modules/home/components/attribute/attribute-table.component.ts @@ -38,7 +38,7 @@ import { TranslateService } from '@ngx-translate/core'; import { MatDialog } from '@angular/material/dialog'; import { DialogService } from '@core/services/dialog.service'; import { Direction, SortOrder } from '@shared/models/page/sort-order'; -import { fromEvent, merge, Subscription } from 'rxjs'; +import { fromEvent, merge } from 'rxjs'; import { debounceTime, distinctUntilChanged, tap } from 'rxjs/operators'; import { EntityId } from '@shared/models/id/entity-id'; import { @@ -84,8 +84,8 @@ import { } from '@home/components/attribute/add-widget-to-dashboard-dialog.component'; import { deepClone } from '@core/utils'; import { Filters } from '@shared/models/query/query.models'; -import { MediaBreakpoints } from '@shared/models/constants'; -import { BreakpointObserver } from '@angular/cdk/layout'; +import { hidePageSizePixelValue } from '@shared/models/constants'; +import { ResizeObserver } from '@juggle/resize-observer'; @Component({ @@ -168,13 +168,14 @@ export class AttributeTableComponent extends PageComponent implements AfterViewI @Input() entityName: string; + @ViewChild('attributeTableContainer', {static: true}) attributeTableContainerRef: ElementRef; @ViewChild('searchInput') searchInputField: ElementRef; @ViewChild(MatPaginator) paginator: MatPaginator; @ViewChild(MatSort) sort: MatSort; - private breakpointObserverSubscription$: Subscription; - public hidePageSize = true; + public hidePageSize = false; + private widgetResize$: ResizeObserver; constructor(protected store: Store, private attributeService: AttributeService, @@ -189,8 +190,7 @@ export class AttributeTableComponent extends PageComponent implements AfterViewI private dashboardUtils: DashboardUtilsService, private widgetService: WidgetService, private zone: NgZone, - private cd: ChangeDetectorRef, - private breakpointObserver: BreakpointObserver) { + private cd: ChangeDetectorRef) { super(store); this.dirtyValue = !this.activeValue; const sortOrder: SortOrder = { property: 'key', direction: Direction.ASC }; @@ -199,18 +199,19 @@ export class AttributeTableComponent extends PageComponent implements AfterViewI } ngOnInit() { - this.breakpointObserverSubscription$ = this.breakpointObserver - .observe(MediaBreakpoints['gt-xs']).subscribe( - () => { - this.hidePageSize = !this.breakpointObserver.isMatched(MediaBreakpoints['gt-xs']); - this.cd.detectChanges(); - } - ); + this.widgetResize$ = new ResizeObserver(() => { + const showHidePageSize = this.attributeTableContainerRef.nativeElement.offsetWidth < hidePageSizePixelValue; + if (showHidePageSize !== this.hidePageSize) { + this.hidePageSize = showHidePageSize; + this.cd.detectChanges(); + } + }); + this.widgetResize$.observe(this.attributeTableContainerRef.nativeElement); } ngOnDestroy() { - if (this.breakpointObserverSubscription$) { - this.breakpointObserverSubscription$.unsubscribe(); + if (this.widgetResize$) { + this.widgetResize$.disconnect(); } } diff --git a/ui-ngx/src/app/modules/home/components/entity/entities-table.component.html b/ui-ngx/src/app/modules/home/components/entity/entities-table.component.html index a7b15670bb..aa86660ebe 100644 --- a/ui-ngx/src/app/modules/home/components/entity/entities-table.component.html +++ b/ui-ngx/src/app/modules/home/components/entity/entities-table.component.html @@ -32,7 +32,7 @@ -
+
diff --git a/ui-ngx/src/app/modules/home/components/entity/entities-table.component.ts b/ui-ngx/src/app/modules/home/components/entity/entities-table.component.ts index cca386570d..1851d5e085 100644 --- a/ui-ngx/src/app/modules/home/components/entity/entities-table.component.ts +++ b/ui-ngx/src/app/modules/home/components/entity/entities-table.component.ts @@ -64,8 +64,8 @@ import { DomSanitizer, SafeHtml } from '@angular/platform-browser'; import { TbAnchorComponent } from '@shared/components/tb-anchor.component'; import { isDefined, isUndefined } from '@core/utils'; import { HasUUID } from '@shared/models/id/has-uuid'; -import { MediaBreakpoints } from '@shared/models/constants'; -import { BreakpointObserver } from '@angular/cdk/layout'; +import { ResizeObserver } from '@juggle/resize-observer'; +import { hidePageSizePixelValue } from '@shared/models/constants'; @Component({ selector: 'tb-entities-table', @@ -111,6 +111,8 @@ export class EntitiesTableComponent extends PageComponent implements AfterViewIn isDetailsOpen = false; detailsPanelOpened = new EventEmitter(); + @ViewChild('entitiesTableContainer', {static: true}) entitiesTableContainerRef: ElementRef; + @ViewChild('entityTableHeader', {static: true}) entityTableHeaderAnchor: TbAnchorComponent; @ViewChild('searchInput') searchInputField: ElementRef; @@ -122,8 +124,8 @@ export class EntitiesTableComponent extends PageComponent implements AfterViewIn private updateDataSubscription: Subscription; private viewInited = false; - private breakpointObserverSubscription$: Subscription; - public hidePageSize = true; + private widgetResize$: ResizeObserver; + public hidePageSize = false; constructor(protected store: Store, public route: ActivatedRoute, @@ -132,8 +134,7 @@ export class EntitiesTableComponent extends PageComponent implements AfterViewIn private dialogService: DialogService, private domSanitizer: DomSanitizer, private cd: ChangeDetectorRef, - private componentFactoryResolver: ComponentFactoryResolver, - private breakpointObserver: BreakpointObserver) { + private componentFactoryResolver: ComponentFactoryResolver) { super(store); } @@ -143,18 +144,19 @@ export class EntitiesTableComponent extends PageComponent implements AfterViewIn } else { this.init(this.route.snapshot.data.entitiesTableConfig); } - this.breakpointObserverSubscription$ = this.breakpointObserver - .observe(MediaBreakpoints['gt-xs']).subscribe( - () => { - this.hidePageSize = !this.breakpointObserver.isMatched(MediaBreakpoints['gt-xs']); - this.cd.detectChanges(); - } - ); + this.widgetResize$ = new ResizeObserver(() => { + const showHidePageSize = this.entitiesTableContainerRef.nativeElement.offsetWidth < hidePageSizePixelValue; + if (showHidePageSize !== this.hidePageSize) { + this.hidePageSize = showHidePageSize; + this.cd.detectChanges(); + } + }); + this.widgetResize$.observe(this.entitiesTableContainerRef.nativeElement); } ngOnDestroy() { - if (this.breakpointObserverSubscription$) { - this.breakpointObserverSubscription$.unsubscribe(); + if (this.widgetResize$) { + this.widgetResize$.disconnect(); } } diff --git a/ui-ngx/src/app/modules/home/components/relation/relation-table.component.html b/ui-ngx/src/app/modules/home/components/relation/relation-table.component.html index b5ae8c2d52..edf5d1d04d 100644 --- a/ui-ngx/src/app/modules/home/components/relation/relation-table.component.html +++ b/ui-ngx/src/app/modules/home/components/relation/relation-table.component.html @@ -15,7 +15,7 @@ limitations under the License. --> -
+
diff --git a/ui-ngx/src/app/modules/home/components/relation/relation-table.component.ts b/ui-ngx/src/app/modules/home/components/relation/relation-table.component.ts index 2ea0ccfa12..8f0b503717 100644 --- a/ui-ngx/src/app/modules/home/components/relation/relation-table.component.ts +++ b/ui-ngx/src/app/modules/home/components/relation/relation-table.component.ts @@ -35,7 +35,7 @@ import { MatDialog } from '@angular/material/dialog'; import { DialogService } from '@core/services/dialog.service'; import { EntityRelationService } from '@core/http/entity-relation.service'; import { Direction, SortOrder } from '@shared/models/page/sort-order'; -import { forkJoin, fromEvent, merge, Observable, Subscription } from 'rxjs'; +import { forkJoin, fromEvent, merge, Observable } from 'rxjs'; import { debounceTime, distinctUntilChanged, tap } from 'rxjs/operators'; import { EntityRelation, @@ -47,8 +47,8 @@ import { import { EntityId } from '@shared/models/id/entity-id'; import { RelationsDatasource } from '../../models/datasource/relation-datasource'; import { RelationDialogComponent, RelationDialogData } from '@home/components/relation/relation-dialog.component'; -import { MediaBreakpoints } from '@shared/models/constants'; -import { BreakpointObserver } from '@angular/cdk/layout'; +import { hidePageSizePixelValue } from '@shared/models/constants'; +import { ResizeObserver } from '@juggle/resize-observer'; @Component({ selector: 'tb-relation-table', @@ -76,8 +76,8 @@ export class RelationTableComponent extends PageComponent implements AfterViewIn viewsInited = false; - private breakpointObserverSubscription$: Subscription; - public hidePageSize = true; + private widgetResize$: ResizeObserver; + public hidePageSize = false; @Input() set active(active: boolean) { @@ -105,6 +105,7 @@ export class RelationTableComponent extends PageComponent implements AfterViewIn } } + @ViewChild('relationTableContainer', {static: true}) relationTableContainerRef: ElementRef; @ViewChild('searchInput') searchInputField: ElementRef; @ViewChild(MatPaginator) paginator: MatPaginator; @@ -115,8 +116,7 @@ export class RelationTableComponent extends PageComponent implements AfterViewIn public translate: TranslateService, public dialog: MatDialog, private dialogService: DialogService, - private cd: ChangeDetectorRef, - private breakpointObserver: BreakpointObserver) { + private cd: ChangeDetectorRef) { super(store); this.dirtyValue = !this.activeValue; const sortOrder: SortOrder = { property: 'type', direction: Direction.ASC }; @@ -127,18 +127,19 @@ export class RelationTableComponent extends PageComponent implements AfterViewIn } ngOnInit() { - this.breakpointObserverSubscription$ = this.breakpointObserver - .observe(MediaBreakpoints['gt-xs']).subscribe( - () => { - this.hidePageSize = !this.breakpointObserver.isMatched(MediaBreakpoints['gt-xs']); - this.cd.detectChanges(); - } - ); + this.widgetResize$ = new ResizeObserver(() => { + const showHidePageSize = this.relationTableContainerRef.nativeElement.offsetWidth < hidePageSizePixelValue; + if (showHidePageSize !== this.hidePageSize) { + this.hidePageSize = showHidePageSize; + this.cd.detectChanges(); + } + }); + this.widgetResize$.observe(this.relationTableContainerRef.nativeElement); } ngOnDestroy() { - if (this.breakpointObserverSubscription$) { - this.breakpointObserverSubscription$.unsubscribe(); + if (this.widgetResize$) { + this.widgetResize$.disconnect(); } } diff --git a/ui-ngx/src/app/modules/home/components/widget/action/manage-widget-actions.component.html b/ui-ngx/src/app/modules/home/components/widget/action/manage-widget-actions.component.html index 53523bc0cf..d0c1db566c 100644 --- a/ui-ngx/src/app/modules/home/components/widget/action/manage-widget-actions.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/action/manage-widget-actions.component.html @@ -15,7 +15,7 @@ limitations under the License. --> -
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/action/manage-widget-actions.component.ts b/ui-ngx/src/app/modules/home/components/widget/action/manage-widget-actions.component.ts index d3fdd47bba..5173e00954 100644 --- a/ui-ngx/src/app/modules/home/components/widget/action/manage-widget-actions.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/action/manage-widget-actions.component.ts @@ -14,7 +14,17 @@ /// limitations under the License. /// -import { AfterViewInit, Component, ElementRef, forwardRef, Input, OnDestroy, OnInit, ViewChild } from '@angular/core'; +import { + AfterViewInit, + ChangeDetectorRef, + Component, + ElementRef, + forwardRef, + Input, + OnDestroy, + OnInit, + ViewChild +} from '@angular/core'; import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; import { TranslateService } from '@ngx-translate/core'; import { PageComponent } from '@shared/components/page.component'; @@ -43,6 +53,8 @@ import { } from '@home/components/widget/action/widget-action-dialog.component'; import { deepClone } from '@core/utils'; import { widgetType } from '@shared/models/widget.models'; +import { ResizeObserver } from '@juggle/resize-observer'; +import { hidePageSizePixelValue } from '@shared/models/constants'; @Component({ selector: 'tb-manage-widget-actions', @@ -73,7 +85,10 @@ export class ManageWidgetActionsComponent extends PageComponent implements OnIni viewsInited = false; dirtyValue = false; + public hidePageSize = false; + private widgetResize$: ResizeObserver; + @ViewChild('manageActionWidgetContainer', {static: true}) manageActionWidgetContainerRef: ElementRef; @ViewChild('searchInput') searchInputField: ElementRef; @ViewChild(MatPaginator) paginator: MatPaginator; @@ -85,7 +100,8 @@ export class ManageWidgetActionsComponent extends PageComponent implements OnIni private translate: TranslateService, private utils: UtilsService, private dialog: MatDialog, - private dialogs: DialogService) { + private dialogs: DialogService, + private cd: ChangeDetectorRef) { super(store); const sortOrder: SortOrder = { property: 'actionSourceName', direction: Direction.ASC }; this.pageLink = new PageLink(10, 0, null, sortOrder); @@ -94,9 +110,20 @@ export class ManageWidgetActionsComponent extends PageComponent implements OnIni } ngOnInit(): void { + this.widgetResize$ = new ResizeObserver(() => { + const showHidePageSize = this.manageActionWidgetContainerRef.nativeElement.offsetWidth < hidePageSizePixelValue; + if (showHidePageSize !== this.hidePageSize) { + this.hidePageSize = showHidePageSize; + this.cd.detectChanges(); + } + }); + this.widgetResize$.observe(this.manageActionWidgetContainerRef.nativeElement); } ngOnDestroy(): void { + if (this.widgetResize$) { + this.widgetResize$.disconnect(); + } } ngAfterViewInit() { diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/alarms-table-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/alarms-table-widget.component.ts index 8f003141e8..e12fcfcebd 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/alarms-table-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/alarms-table-widget.component.ts @@ -124,6 +124,7 @@ import { import { entityFields } from '@shared/models/entity.models'; import { coerceBooleanProperty } from '@angular/cdk/coercion'; import { ResizeObserver } from '@juggle/resize-observer'; +import { hidePageSizePixelValue } from '@shared/models/constants'; interface AlarmsTableWidgetSettings extends TableWidgetSettings { alarmsTitle: string; @@ -262,7 +263,7 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit, () => this.pageLink.page = 0 ); this.widgetResize$ = new ResizeObserver(() => { - const showHidePageSize = this.ctx.$container[0].offsetWidth < 500; + const showHidePageSize = this.alarmWidgetContainerRef.nativeElement.offsetWidth < hidePageSizePixelValue; if (showHidePageSize !== this.hidePageSize) { this.hidePageSize = showHidePageSize; this.ctx.detectChanges(); diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/entities-table-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/entities-table-widget.component.ts index 2c887a1668..32a07cecb8 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/entities-table-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/entities-table-widget.component.ts @@ -108,6 +108,7 @@ import { entityFields } from '@shared/models/entity.models'; import { DatePipe } from '@angular/common'; import { coerceBooleanProperty } from '@angular/cdk/coercion'; import { ResizeObserver } from '@juggle/resize-observer'; +import { hidePageSizePixelValue } from '@shared/models/constants'; interface EntitiesTableWidgetSettings extends TableWidgetSettings { entitiesTitle: string; @@ -217,7 +218,7 @@ export class EntitiesTableWidgetComponent extends PageComponent implements OnIni this.ctx.updateWidgetParams(); if (this.displayPagination) { this.widgetResize$ = new ResizeObserver(() => { - const showHidePageSize = this.ctx.$container[0].offsetWidth < 500; + const showHidePageSize = this.entitiesWidgetContainerRef.nativeElement.offsetWidth < hidePageSizePixelValue; if (showHidePageSize !== this.hidePageSize) { this.hidePageSize = showHidePageSize; this.ctx.detectChanges(); diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/timeseries-table-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/timeseries-table-widget.component.ts index cc35c481a8..a5a2cbc12e 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/timeseries-table-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/timeseries-table-widget.component.ts @@ -72,6 +72,7 @@ import { DatePipe } from '@angular/common'; import { parseData } from '@home/components/widget/lib/maps/common-maps-utils'; import { coerceBooleanProperty } from '@angular/cdk/coercion'; import { ResizeObserver } from '@juggle/resize-observer'; +import { hidePageSizePixelValue } from '@shared/models/constants'; export interface TimeseriesTableWidgetSettings extends TableWidgetSettings { showTimestamp: boolean; @@ -195,7 +196,7 @@ export class TimeseriesTableWidgetComponent extends PageComponent implements OnI } ); this.widgetResize$ = new ResizeObserver(() => { - const showHidePageSize = this.ctx.$container[0].offsetWidth < 500; + const showHidePageSize = this.timeseriesWidgetContainerRef.nativeElement.offsetWidth < hidePageSizePixelValue; if (showHidePageSize !== this.hidePageSize) { this.hidePageSize = showHidePageSize; this.ctx.detectChanges(); diff --git a/ui-ngx/src/app/shared/models/constants.ts b/ui-ngx/src/app/shared/models/constants.ts index 03b38345ca..4699a6338c 100644 --- a/ui-ngx/src/app/shared/models/constants.ts +++ b/ui-ngx/src/app/shared/models/constants.ts @@ -241,6 +241,7 @@ export const contentTypesMap = new Map( ] ); +export const hidePageSizePixelValue = 550; export const customTranslationsPrefix = 'custom.'; export const i18nPrefix = 'i18n'; From f7911cc1acf0a4e6563835b2b716fd7cbb25634a Mon Sep 17 00:00:00 2001 From: Viacheslav Klimov Date: Wed, 1 Dec 2021 13:47:05 +0200 Subject: [PATCH 016/798] SMPP SMS sender --- application/pom.xml | 4 + .../service/sms/DefaultSmsSenderFactory.java | 4 + .../service/sms/smpp/SmppSmsSender.java | 154 ++++++++++++++++++ .../config/SmppSmsProviderConfiguration.java | 63 +++++++ .../sms/config/SmsProviderConfiguration.java | 4 +- .../data/sms/config/SmsProviderType.java | 3 +- pom.xml | 6 + 7 files changed, 236 insertions(+), 2 deletions(-) create mode 100644 application/src/main/java/org/thingsboard/server/service/sms/smpp/SmppSmsSender.java create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/sms/config/SmppSmsProviderConfiguration.java diff --git a/application/pom.xml b/application/pom.xml index cc3bcef183..81fd6593c9 100644 --- a/application/pom.xml +++ b/application/pom.xml @@ -249,6 +249,10 @@ io.grpc grpc-stub + + org.opensmpp + opensmpp-core + org.thingsboard springfox-boot-starter diff --git a/application/src/main/java/org/thingsboard/server/service/sms/DefaultSmsSenderFactory.java b/application/src/main/java/org/thingsboard/server/service/sms/DefaultSmsSenderFactory.java index c4b56dbb03..d86899219e 100644 --- a/application/src/main/java/org/thingsboard/server/service/sms/DefaultSmsSenderFactory.java +++ b/application/src/main/java/org/thingsboard/server/service/sms/DefaultSmsSenderFactory.java @@ -19,9 +19,11 @@ import org.springframework.stereotype.Component; import org.thingsboard.rule.engine.api.sms.SmsSender; import org.thingsboard.rule.engine.api.sms.SmsSenderFactory; import org.thingsboard.server.common.data.sms.config.AwsSnsSmsProviderConfiguration; +import org.thingsboard.server.common.data.sms.config.SmppSmsProviderConfiguration; import org.thingsboard.server.common.data.sms.config.SmsProviderConfiguration; import org.thingsboard.server.common.data.sms.config.TwilioSmsProviderConfiguration; import org.thingsboard.server.service.sms.aws.AwsSmsSender; +import org.thingsboard.server.service.sms.smpp.SmppSmsSender; import org.thingsboard.server.service.sms.twilio.TwilioSmsSender; @Component @@ -34,6 +36,8 @@ public class DefaultSmsSenderFactory implements SmsSenderFactory { return new AwsSmsSender((AwsSnsSmsProviderConfiguration)config); case TWILIO: return new TwilioSmsSender((TwilioSmsProviderConfiguration)config); + case SMPP: + return new SmppSmsSender((SmppSmsProviderConfiguration) config); default: throw new RuntimeException("Unknown SMS provider type " + config.getType()); } diff --git a/application/src/main/java/org/thingsboard/server/service/sms/smpp/SmppSmsSender.java b/application/src/main/java/org/thingsboard/server/service/sms/smpp/SmppSmsSender.java new file mode 100644 index 0000000000..eb39b1d8ca --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/sms/smpp/SmppSmsSender.java @@ -0,0 +1,154 @@ +package org.thingsboard.server.service.sms.smpp; + +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.exception.ExceptionUtils; +import org.smpp.Connection; +import org.smpp.Data; +import org.smpp.Session; +import org.smpp.TCPIPConnection; +import org.smpp.TimeoutException; +import org.smpp.WrongSessionStateException; +import org.smpp.pdu.Address; +import org.smpp.pdu.BindReceiver; +import org.smpp.pdu.BindRequest; +import org.smpp.pdu.BindResponse; +import org.smpp.pdu.BindTransciever; +import org.smpp.pdu.BindTransmitter; +import org.smpp.pdu.PDUException; +import org.smpp.pdu.SubmitSM; +import org.smpp.pdu.SubmitSMResp; +import org.thingsboard.rule.engine.api.sms.exception.SmsException; +import org.thingsboard.server.common.data.sms.config.SmppSmsProviderConfiguration; +import org.thingsboard.server.service.sms.AbstractSmsSender; + +import java.io.IOException; +import java.util.Optional; + +@Slf4j +public class SmppSmsSender extends AbstractSmsSender { + private final SmppSmsProviderConfiguration config; + + private Session smppSession; + + public SmppSmsSender(SmppSmsProviderConfiguration config) { + this.config = config; + } + + @Override + public int sendSms(String numberTo, String message) throws SmsException { + try { + checkSmppSession(); + + SubmitSM request = new SubmitSM(); + if (StringUtils.isNotEmpty(config.getServiceType())) { + request.setServiceType(config.getServiceType()); + } + if (StringUtils.isNotEmpty(config.getSourceAddress())) { + request.setSourceAddr(new Address(config.getTon(), config.getNpi(), config.getSourceAddress())); + } + numberTo = prepareNumber(numberTo); + request.setDestAddr(new Address(config.getDestinationTon(), config.getDestinationNpi(), numberTo)); + request.setShortMessage(message); + request.setDataCoding(Optional.ofNullable(config.getCodingScheme()).orElse((byte) 0)); + request.setReplaceIfPresentFlag((byte) 0); + request.setEsmClass((byte) 0); + request.setProtocolId((byte) 0); + request.setPriorityFlag((byte) 0); + request.setRegisteredDelivery((byte) 0); + request.setSmDefaultMsgId((byte) 0); + + SubmitSMResp response = smppSession.submit(request); + + log.info("SMPP submit command status: {}", response.getCommandStatus()); + } catch (Exception e) { + throw new RuntimeException(e); + } + + return countMessageSegments(message); + } + + public synchronized void checkSmppSession() { + if (smppSession == null || !smppSession.isOpened()) { + smppSession = initSmppSession(); + } + } + + private Session initSmppSession() { + try { + Connection connection = new TCPIPConnection(config.getHost(), config.getPort()); + Session session = new Session(connection); + + BindRequest bindRequest; + if (config.getBindType() == null) { + bindRequest = new BindTransmitter(); + } else { + switch (config.getBindType()) { + case TX: + bindRequest = new BindTransmitter(); + break; + case RX: + bindRequest = new BindReceiver(); + break; + case TRX: + bindRequest = new BindTransciever(); + break; + default: + throw new UnsupportedOperationException("Unsupported bind type " + config.getBindType()); + } + } + + bindRequest.setSystemId(config.getSystemId()); + bindRequest.setPassword(config.getPassword()); + + byte interfaceVersion; + switch (config.getProtocolVersion()) { + case "3.3": + interfaceVersion = Data.SMPP_V33; + break; + case "3.4": + interfaceVersion = Data.SMPP_V34; + break; + default: + throw new UnsupportedOperationException("Unsupported SMPP version: " + config.getProtocolVersion()); + } + bindRequest.setInterfaceVersion(interfaceVersion); + + if (StringUtils.isNotEmpty(config.getSystemType())) { + bindRequest.setSystemType(config.getSystemType()); + } + if (StringUtils.isNotEmpty(config.getAddressRange())) { + bindRequest.setAddressRange(config.getDestinationTon(), config.getDestinationNpi(), config.getAddressRange()); + } + + BindResponse bindResponse = session.bind(bindRequest); + log.debug("SMPP bind response: {}", bindResponse.debugString()); + + if (bindResponse.getCommandStatus() != 0) { + throw new IllegalStateException("Error status when binding: " + bindResponse.getCommandStatus()); + } + + return session; + } catch (Exception e) { + throw new IllegalArgumentException("Failed to establish SMPP session: " + ExceptionUtils.getRootCauseMessage(e)); + } + } + + private String prepareNumber(String number) { + if (config.getDestinationTon() == Data.GSM_TON_INTERNATIONAL) { + return StringUtils.removeStart(number, "+"); + } + return number; + } + + @Override + public void destroy() { + try { + smppSession.unbind(); + smppSession.close(); + } catch (TimeoutException | PDUException | IOException | WrongSessionStateException e) { + throw new RuntimeException(e); + } + + } +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/sms/config/SmppSmsProviderConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/sms/config/SmppSmsProviderConfiguration.java new file mode 100644 index 0000000000..f5fccc8e0e --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/sms/config/SmppSmsProviderConfiguration.java @@ -0,0 +1,63 @@ +package org.thingsboard.server.common.data.sms.config; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +@Data +public class SmppSmsProviderConfiguration implements SmsProviderConfiguration { + @ApiModelProperty(allowableValues = "3.3, 3.4") + private String protocolVersion; + + private String host; + private Integer port; + + private String systemId; + private String password; + + @ApiModelProperty(required = false) + private String systemType; + @ApiModelProperty(value = "TX - Transmitter, RX - Receiver, TRX - Transciever. By default TX is used", required = false) + private SmppBindType bindType; + @ApiModelProperty(required = false) + private String serviceType; + + @ApiModelProperty(required = false) + private Byte ton; + @ApiModelProperty(required = false) + private Byte npi; + @ApiModelProperty(required = false) + private String sourceAddress; + + @ApiModelProperty(required = false) + private Byte destinationTon; + @ApiModelProperty(required = false) + private Byte destinationNpi; + @ApiModelProperty(required = false) + private String addressRange; + + @ApiModelProperty(allowableValues = "0-10,13-14", + value = "0 - SMSC Default Alphabet (ASCII for short and long code and to GSM for toll-free, used as default)\n" + + "1 - IA5 (ASCII for short and long code, Latin 9 for toll-free (ISO-8859-9))\n" + + "2 - Octet Unspecified (8-bit binary)\n" + + "3 - Latin 1 (ISO-8859-1)\n" + + "4 - Octet Unspecified (8-bit binary)\n" + + "5 - JIS (X 0208-1990)\n" + + "6 - Cyrillic (ISO-8859-5)\n" + + "7 - Latin/Hebrew (ISO-8859-8)\n" + + "8 - UCS2/UTF-16 (ISO/IEC-10646)\n" + + "9 - Pictogram Encoding\n" + + "10 - Music Codes (ISO-2022-JP)\n" + + "13 - Extended Kanji JIS (X 0212-1990)\n" + + "14 - Korean Graphic Character Set (KS C 5601/KS X 1001)", required = false) + private Byte codingScheme; + + @Override + public SmsProviderType getType() { + return SmsProviderType.SMPP; + } + + public enum SmppBindType { + TX, RX, TRX + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/sms/config/SmsProviderConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/sms/config/SmsProviderConfiguration.java index 34f187b005..08968534b7 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/sms/config/SmsProviderConfiguration.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/sms/config/SmsProviderConfiguration.java @@ -27,7 +27,9 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo; property = "type") @JsonSubTypes({ @JsonSubTypes.Type(value = AwsSnsSmsProviderConfiguration.class, name = "AWS_SNS"), - @JsonSubTypes.Type(value = TwilioSmsProviderConfiguration.class, name = "TWILIO")}) + @JsonSubTypes.Type(value = TwilioSmsProviderConfiguration.class, name = "TWILIO"), + @JsonSubTypes.Type(value = SmppSmsProviderConfiguration.class, name = "SMPP") +}) public interface SmsProviderConfiguration { @JsonIgnore diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/sms/config/SmsProviderType.java b/common/data/src/main/java/org/thingsboard/server/common/data/sms/config/SmsProviderType.java index 33ccf440ab..f6390433cb 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/sms/config/SmsProviderType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/sms/config/SmsProviderType.java @@ -17,5 +17,6 @@ package org.thingsboard.server.common.data.sms.config; public enum SmsProviderType { AWS_SNS, - TWILIO + TWILIO, + SMPP } diff --git a/pom.xml b/pom.xml index 7503bcc4c0..86a33271af 100755 --- a/pom.xml +++ b/pom.xml @@ -130,6 +130,7 @@ 1.16.0 1.12 + 3.0.0 @@ -1872,6 +1873,11 @@ ${zeroturnaround.version} test + + org.opensmpp + opensmpp-core + ${opensmpp.version} + From 7b0309ca683b6893014de41a9926d30ca9c3e22d Mon Sep 17 00:00:00 2001 From: Viacheslav Klimov Date: Thu, 2 Dec 2021 16:06:29 +0200 Subject: [PATCH 017/798] SMPP SMS sender refactoring, API docs --- .../service/sms/smpp/SmppSmsSender.java | 69 ++++++++++++----- .../config/SmppSmsProviderConfiguration.java | 76 ++++++++++++++++--- 2 files changed, 114 insertions(+), 31 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/sms/smpp/SmppSmsSender.java b/application/src/main/java/org/thingsboard/server/service/sms/smpp/SmppSmsSender.java index eb39b1d8ca..e89a346773 100644 --- a/application/src/main/java/org/thingsboard/server/service/sms/smpp/SmppSmsSender.java +++ b/application/src/main/java/org/thingsboard/server/service/sms/smpp/SmppSmsSender.java @@ -1,3 +1,18 @@ +/** + * Copyright © 2016-2021 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.service.sms.smpp; import lombok.extern.slf4j.Slf4j; @@ -32,7 +47,26 @@ public class SmppSmsSender extends AbstractSmsSender { private Session smppSession; public SmppSmsSender(SmppSmsProviderConfiguration config) { + if (config.getBindType() == null) { + config.setBindType(SmppSmsProviderConfiguration.SmppBindType.TX); + } + if (StringUtils.isNotEmpty(config.getSourceAddress())) { + if (config.getSourceTon() == null) { + config.setSourceTon((byte) 5); + } + if (config.getSourceNpi() == null) { + config.setSourceNpi((byte) 0); + } + } + if (config.getDestinationTon() == null) { + config.setDestinationTon((byte) 5); + } + if (config.getDestinationNpi() == null) { + config.setDestinationNpi((byte) 0); + } + this.config = config; + initSmppSession(); } @Override @@ -45,10 +79,9 @@ public class SmppSmsSender extends AbstractSmsSender { request.setServiceType(config.getServiceType()); } if (StringUtils.isNotEmpty(config.getSourceAddress())) { - request.setSourceAddr(new Address(config.getTon(), config.getNpi(), config.getSourceAddress())); + request.setSourceAddr(new Address(config.getSourceTon(), config.getSourceNpi(), config.getSourceAddress())); } - numberTo = prepareNumber(numberTo); - request.setDestAddr(new Address(config.getDestinationTon(), config.getDestinationNpi(), numberTo)); + request.setDestAddr(new Address(config.getDestinationTon(), config.getDestinationNpi(), prepareNumber(numberTo))); request.setShortMessage(message); request.setDataCoding(Optional.ofNullable(config.getCodingScheme()).orElse((byte) 0)); request.setReplaceIfPresentFlag((byte) 0); @@ -69,7 +102,7 @@ public class SmppSmsSender extends AbstractSmsSender { } public synchronized void checkSmppSession() { - if (smppSession == null || !smppSession.isOpened()) { + if (!smppSession.isOpened()) { smppSession = initSmppSession(); } } @@ -80,22 +113,18 @@ public class SmppSmsSender extends AbstractSmsSender { Session session = new Session(connection); BindRequest bindRequest; - if (config.getBindType() == null) { - bindRequest = new BindTransmitter(); - } else { - switch (config.getBindType()) { - case TX: - bindRequest = new BindTransmitter(); - break; - case RX: - bindRequest = new BindReceiver(); - break; - case TRX: - bindRequest = new BindTransciever(); - break; - default: - throw new UnsupportedOperationException("Unsupported bind type " + config.getBindType()); - } + switch (config.getBindType()) { + case TX: + bindRequest = new BindTransmitter(); + break; + case RX: + bindRequest = new BindReceiver(); + break; + case TRX: + bindRequest = new BindTransciever(); + break; + default: + throw new UnsupportedOperationException("Unsupported bind type " + config.getBindType()); } bindRequest.setSystemId(config.getSystemId()); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/sms/config/SmppSmsProviderConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/sms/config/SmppSmsProviderConfiguration.java index f5fccc8e0e..4d9b53e10c 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/sms/config/SmppSmsProviderConfiguration.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/sms/config/SmppSmsProviderConfiguration.java @@ -1,3 +1,18 @@ +/** + * Copyright © 2016-2021 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.sms.config; import io.swagger.annotations.ApiModelProperty; @@ -5,34 +20,73 @@ import lombok.Data; @Data public class SmppSmsProviderConfiguration implements SmsProviderConfiguration { - @ApiModelProperty(allowableValues = "3.3, 3.4") + @ApiModelProperty(value = "SMPP version", allowableValues = "3.3, 3.4", required = true) private String protocolVersion; + @ApiModelProperty(value = "SMPP host", required = true) private String host; + @ApiModelProperty(value = "SMPP port", required = true) private Integer port; + @ApiModelProperty(value = "System ID", required = true) private String systemId; + @ApiModelProperty(value = "Password", required = true) private String password; - @ApiModelProperty(required = false) + @ApiModelProperty(value = "System type", required = false) private String systemType; @ApiModelProperty(value = "TX - Transmitter, RX - Receiver, TRX - Transciever. By default TX is used", required = false) private SmppBindType bindType; - @ApiModelProperty(required = false) + @ApiModelProperty(value = "Service type", required = false) private String serviceType; - @ApiModelProperty(required = false) - private Byte ton; - @ApiModelProperty(required = false) - private Byte npi; - @ApiModelProperty(required = false) + @ApiModelProperty(value = "Source address", required = false) private String sourceAddress; + @ApiModelProperty(value = "Source TON (Type of Number). Needed is source address is set. 5 by default.\n" + + "0 - Unknown\n" + + "1 - International\n" + + "2 - National\n" + + "3 - Network Specific\n" + + "4 - Subscriber Number\n" + + "5 - Alphanumeric\n" + + "6 - Abbreviated", required = false) + private Byte sourceTon; + @ApiModelProperty(value = "Source NPI (Numbering Plan Identification). Needed is source address is set. 0 by default.\n" + + "0 - Unknown\n" + + "1 - ISDN/telephone numbering plan (E163/E164)\n" + + "3 - Data numbering plan (X.121)\n" + + "4 - Telex numbering plan (F.69)\n" + + "6 - Land Mobile (E.212) =6\n" + + "8 - National numbering plan\n" + + "9 - Private numbering plan\n" + + "10 - ERMES numbering plan (ETSI DE/PS 3 01-3)\n" + + "13 - Internet (IP)\n" + + "18 - WAP Client Id (to be defined by WAP Forum)", required = false) + private Byte sourceNpi; - @ApiModelProperty(required = false) + @ApiModelProperty(value = "Destination TON (Type of Number). 5 by default.\n" + + "0 - Unknown\n" + + "1 - International\n" + + "2 - National\n" + + "3 - Network Specific\n" + + "4 - Subscriber Number\n" + + "5 - Alphanumeric\n" + + "6 - Abbreviated", required = false) private Byte destinationTon; - @ApiModelProperty(required = false) + @ApiModelProperty(value = "Destination NPI (Numbering Plan Identification). 0 by default.\n" + + "0 - Unknown\n" + + "1 - ISDN/telephone numbering plan (E163/E164)\n" + + "3 - Data numbering plan (X.121)\n" + + "4 - Telex numbering plan (F.69)\n" + + "6 - Land Mobile (E.212) =6\n" + + "8 - National numbering plan\n" + + "9 - Private numbering plan\n" + + "10 - ERMES numbering plan (ETSI DE/PS 3 01-3)\n" + + "13 - Internet (IP)\n" + + "18 - WAP Client Id (to be defined by WAP Forum)", required = false) private Byte destinationNpi; - @ApiModelProperty(required = false) + + @ApiModelProperty(value = "Address range", required = false) private String addressRange; @ApiModelProperty(allowableValues = "0-10,13-14", From 73a55a2c34f1d2d818a456669f67eff76781e0ef Mon Sep 17 00:00:00 2001 From: Andrii Shvaika Date: Mon, 6 Dec 2021 16:24:33 +0200 Subject: [PATCH 018/798] Ignore no longer valid messages when processing strategy is completed or timed-out. --- .../actors/ruleChain/DefaultTbContext.java | 10 +++++++ .../RuleChainActorMessageProcessor.java | 28 +++++++++++++++---- .../actors/ruleChain/RuleNodeActor.java | 14 ++++++++-- .../RuleNodeActorMessageProcessor.java | 4 +-- .../actors/shared/ComponentMsgProcessor.java | 12 +++++++- .../service/queue/TbMsgPackCallback.java | 5 ++++ .../queue/TbMsgPackProcessingContext.java | 3 ++ .../thingsboard/server/common/msg/TbMsg.java | 10 ++++++- .../common/msg/queue/TbMsgCallback.java | 14 +++++++++- 9 files changed, 87 insertions(+), 13 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java index 38d6f4bccb..609c087a51 100644 --- a/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java +++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java @@ -167,6 +167,11 @@ class DefaultTbContext implements TbContext { } private void enqueue(TopicPartitionInfo tpi, TbMsg tbMsg, Consumer onFailure, Runnable onSuccess) { + if (!tbMsg.isValid()) { + log.trace("[{}] Skip invalid message: {}", getTenantId(), tbMsg); + onFailure.accept(new IllegalArgumentException("Source message is no longer valid!")); + return; + } TransportProtos.ToRuleEngineMsg msg = TransportProtos.ToRuleEngineMsg.newBuilder() .setTenantIdMSB(getTenantId().getId().getMostSignificantBits()) .setTenantIdLSB(getTenantId().getId().getLeastSignificantBits()) @@ -235,6 +240,11 @@ class DefaultTbContext implements TbContext { } private void enqueueForTellNext(TopicPartitionInfo tpi, String queueName, TbMsg source, Set relationTypes, String failureMessage, Runnable onSuccess, Consumer onFailure) { + if (!source.isValid()) { + log.trace("[{}] Skip invalid message: {}", getTenantId(), source); + onFailure.accept(new IllegalArgumentException("Source message is no longer valid!")); + return; + } RuleChainId ruleChainId = nodeCtx.getSelf().getRuleChainId(); RuleNodeId ruleNodeId = nodeCtx.getSelf().getId(); TbMsg tbMsg = TbMsg.newMsg(source, queueName, ruleChainId, ruleNodeId); diff --git a/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActorMessageProcessor.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActorMessageProcessor.java index 88a4f6a202..6f6ea8fcec 100644 --- a/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActorMessageProcessor.java +++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActorMessageProcessor.java @@ -200,17 +200,20 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor relationTypes, String failureMessage) { try { - checkActive(msg); + checkComponentStateActive(msg); EntityId entityId = msg.getOriginator(); TopicPartitionInfo tpi = systemContext.resolve(ServiceType.TB_RULE_ENGINE, msg.getQueueName(), tenantId, entityId); diff --git a/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeActor.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeActor.java index 8fef00c49c..60f462f4d4 100644 --- a/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeActor.java +++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeActor.java @@ -27,6 +27,7 @@ import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.RuleNodeId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.msg.TbActorMsg; +import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg; import org.thingsboard.server.common.msg.queue.PartitionChangeMsg; @@ -86,12 +87,19 @@ public class RuleNodeActor extends ComponentActor extends Abstract schedulePeriodicMsgWithDelay(context, new StatsPersistTick(), statsPersistFrequency, statsPersistFrequency); } - protected void checkActive(TbMsg tbMsg) throws RuleNodeException { + protected boolean checkMsgValid(TbMsg tbMsg) { + var valid = tbMsg.isValid(); + if (!valid) { + if (log.isTraceEnabled()) { + log.trace("Skip processing of message: {} because it is no longer valid!", tbMsg); + } + } + return valid; + } + + protected void checkComponentStateActive(TbMsg tbMsg) throws RuleNodeException { if (state != ComponentLifecycleState.ACTIVE) { log.debug("Component is not active. Current state [{}] for processor [{}][{}] tenant [{}]", state, entityId.getEntityType(), entityId, tenantId); RuleNodeException ruleNodeException = getInactiveException(); diff --git a/application/src/main/java/org/thingsboard/server/service/queue/TbMsgPackCallback.java b/application/src/main/java/org/thingsboard/server/service/queue/TbMsgPackCallback.java index eff1ecaf86..e3ce0927ef 100644 --- a/application/src/main/java/org/thingsboard/server/service/queue/TbMsgPackCallback.java +++ b/application/src/main/java/org/thingsboard/server/service/queue/TbMsgPackCallback.java @@ -66,6 +66,11 @@ public class TbMsgPackCallback implements TbMsgCallback { ctx.onFailure(tenantId, id, e); } + @Override + public boolean isMsgValid() { + return !ctx.isComplete(); + } + @Override public void onProcessingStart(RuleNodeInfo ruleNodeInfo) { log.trace("[{}] ON PROCESSING START: {}", id, ruleNodeInfo); diff --git a/application/src/main/java/org/thingsboard/server/service/queue/TbMsgPackProcessingContext.java b/application/src/main/java/org/thingsboard/server/service/queue/TbMsgPackProcessingContext.java index d7a064c4ca..62285a411c 100644 --- a/application/src/main/java/org/thingsboard/server/service/queue/TbMsgPackProcessingContext.java +++ b/application/src/main/java/org/thingsboard/server/service/queue/TbMsgPackProcessingContext.java @@ -53,6 +53,8 @@ public class TbMsgPackProcessingContext { private final ConcurrentMap exceptionsMap = new ConcurrentHashMap<>(); private final ConcurrentMap lastRuleNodeMap = new ConcurrentHashMap<>(); + @Getter + private volatile boolean complete = false; public TbMsgPackProcessingContext(String queueName, TbRuleEngineSubmitStrategy submitStrategy) { this.queueName = queueName; @@ -149,6 +151,7 @@ public class TbMsgPackProcessingContext { } public void cleanup() { + complete = true; pendingMap.clear(); successMap.clear(); failedMap.clear(); diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/TbMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/TbMsg.java index 63c148b951..7c77d8a06b 100644 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/TbMsg.java +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/TbMsg.java @@ -269,7 +269,7 @@ public final class TbMsg implements Serializable { } public TbMsgCallback getCallback() { - //May be null in case of deserialization; + // May be null in case of deserialization; if (callback != null) { return callback; } else { @@ -288,4 +288,12 @@ public final class TbMsg implements Serializable { public TbMsgProcessingStackItem popFormStack() { return ctx.pop(); } + + /** + * Checks if the message is still valid for processing. May be invalid if the message pack is timed-out or canceled. + * @return 'true' if message is valid for processing, 'false' otherwise. + */ + public boolean isValid() { + return getCallback().isMsgValid(); + } } diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/queue/TbMsgCallback.java b/common/message/src/main/java/org/thingsboard/server/common/msg/queue/TbMsgCallback.java index fd62df83c9..2bab1e8340 100644 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/queue/TbMsgCallback.java +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/queue/TbMsgCallback.java @@ -17,6 +17,9 @@ package org.thingsboard.server.common.msg.queue; import org.thingsboard.server.common.data.id.RuleNodeId; +/** + * Should be renamed to TbMsgPackContext, but this can't be changed due to backward-compatibility. + */ public interface TbMsgCallback { TbMsgCallback EMPTY = new TbMsgCallback() { @@ -36,11 +39,20 @@ public interface TbMsgCallback { void onFailure(RuleEngineException e); + /** + * Returns 'true' if rule engine is expecting the message to be processed, 'false' otherwise. + * message may no longer be valid, if the message pack is already expired/canceled/failed. + * + * @return 'true' if rule engine is expecting the message to be processed, 'false' otherwise. + */ + default boolean isMsgValid() { + return true; + } + default void onProcessingStart(RuleNodeInfo ruleNodeInfo) { } default void onProcessingEnd(RuleNodeId ruleNodeId) { } - } From dffa84483e63fbc25e43c21ea78caa19ac43c50a Mon Sep 17 00:00:00 2001 From: Andrii Shvaika Date: Wed, 8 Dec 2021 10:50:45 +0200 Subject: [PATCH 019/798] Fixed tests --- .../rules/flow/AbstractRuleEngineFlowIntegrationTest.java | 2 ++ .../lifecycle/AbstractRuleEngineLifecycleIntegrationTest.java | 1 + 2 files changed, 3 insertions(+) diff --git a/application/src/test/java/org/thingsboard/server/rules/flow/AbstractRuleEngineFlowIntegrationTest.java b/application/src/test/java/org/thingsboard/server/rules/flow/AbstractRuleEngineFlowIntegrationTest.java index 90a0404751..eb79bc9d35 100644 --- a/application/src/test/java/org/thingsboard/server/rules/flow/AbstractRuleEngineFlowIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/rules/flow/AbstractRuleEngineFlowIntegrationTest.java @@ -144,6 +144,7 @@ public abstract class AbstractRuleEngineFlowIntegrationTest extends AbstractRule Thread.sleep(1000); TbMsgCallback tbMsgCallback = Mockito.mock(TbMsgCallback.class); + Mockito.when(tbMsgCallback.isMsgValid()).thenReturn(true); TbMsg tbMsg = TbMsg.newMsg("CUSTOM", device.getId(), new TbMsgMetaData(), "{}", tbMsgCallback); QueueToRuleEngineMsg qMsg = new QueueToRuleEngineMsg(savedTenant.getId(), tbMsg, null, null); // Pushing Message to the system @@ -256,6 +257,7 @@ public abstract class AbstractRuleEngineFlowIntegrationTest extends AbstractRule Thread.sleep(1000); TbMsgCallback tbMsgCallback = Mockito.mock(TbMsgCallback.class); + Mockito.when(tbMsgCallback.isMsgValid()).thenReturn(true); TbMsg tbMsg = TbMsg.newMsg("CUSTOM", device.getId(), new TbMsgMetaData(), "{}", tbMsgCallback); QueueToRuleEngineMsg qMsg = new QueueToRuleEngineMsg(savedTenant.getId(), tbMsg, null, null); // Pushing Message to the system diff --git a/application/src/test/java/org/thingsboard/server/rules/lifecycle/AbstractRuleEngineLifecycleIntegrationTest.java b/application/src/test/java/org/thingsboard/server/rules/lifecycle/AbstractRuleEngineLifecycleIntegrationTest.java index cfa546cc11..62b2034b79 100644 --- a/application/src/test/java/org/thingsboard/server/rules/lifecycle/AbstractRuleEngineLifecycleIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/rules/lifecycle/AbstractRuleEngineLifecycleIntegrationTest.java @@ -135,6 +135,7 @@ public abstract class AbstractRuleEngineLifecycleIntegrationTest extends Abstrac Thread.sleep(1000); TbMsgCallback tbMsgCallback = Mockito.mock(TbMsgCallback.class); + Mockito.when(tbMsgCallback.isMsgValid()).thenReturn(true); TbMsg tbMsg = TbMsg.newMsg("CUSTOM", device.getId(), new TbMsgMetaData(), "{}", tbMsgCallback); QueueToRuleEngineMsg qMsg = new QueueToRuleEngineMsg(savedTenant.getId(), tbMsg, null, null); // Pushing Message to the system From 09c8f2bdc7fabe3850bda97fa32ecdd5b91857bd Mon Sep 17 00:00:00 2001 From: ArtemDzhereleiko Date: Tue, 14 Dec 2021 12:40:18 +0200 Subject: [PATCH 020/798] Hide page size on tables refactoring --- .../attribute/attribute-table.component.html | 2 +- .../attribute/attribute-table.component.scss | 1 + .../attribute/attribute-table.component.ts | 18 ++++++++------- .../entity/entities-table.component.html | 2 +- .../entity/entities-table.component.scss | 1 + .../entity/entities-table.component.ts | 22 ++++++++----------- .../relation/relation-table.component.html | 2 +- .../relation/relation-table.component.scss | 1 + .../relation/relation-table.component.ts | 12 +++++----- .../manage-widget-actions.component.html | 5 +++-- .../manage-widget-actions.component.scss | 1 + .../action/manage-widget-actions.component.ts | 16 +++++++------- .../lib/alarms-table-widget.component.html | 2 +- .../lib/alarms-table-widget.component.scss | 1 + .../lib/alarms-table-widget.component.ts | 13 ++++++----- .../lib/entities-table-widget.component.html | 2 +- .../lib/entities-table-widget.component.scss | 1 + .../lib/entities-table-widget.component.ts | 13 ++++++----- .../timeseries-table-widget.component.html | 2 +- .../timeseries-table-widget.component.scss | 1 + .../lib/timeseries-table-widget.component.ts | 13 ++++++----- 21 files changed, 70 insertions(+), 61 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/attribute/attribute-table.component.html b/ui-ngx/src/app/modules/home/components/attribute/attribute-table.component.html index 493bfe3e2d..c0612646f3 100644 --- a/ui-ngx/src/app/modules/home/components/attribute/attribute-table.component.html +++ b/ui-ngx/src/app/modules/home/components/attribute/attribute-table.component.html @@ -15,7 +15,7 @@ limitations under the License. --> -
+
diff --git a/ui-ngx/src/app/modules/home/components/attribute/attribute-table.component.scss b/ui-ngx/src/app/modules/home/components/attribute/attribute-table.component.scss index df6b8e7184..715eb71fbc 100644 --- a/ui-ngx/src/app/modules/home/components/attribute/attribute-table.component.scss +++ b/ui-ngx/src/app/modules/home/components/attribute/attribute-table.component.scss @@ -18,6 +18,7 @@ :host { width: 100%; height: 100%; + display: block; .tb-entity-table { .tb-entity-table-content { width: 100%; diff --git a/ui-ngx/src/app/modules/home/components/attribute/attribute-table.component.ts b/ui-ngx/src/app/modules/home/components/attribute/attribute-table.component.ts index 1bd4dea53f..f197f995a6 100644 --- a/ui-ngx/src/app/modules/home/components/attribute/attribute-table.component.ts +++ b/ui-ngx/src/app/modules/home/components/attribute/attribute-table.component.ts @@ -111,6 +111,7 @@ export class AttributeTableComponent extends PageComponent implements AfterViewI pageLink: PageLink; textSearchMode = false; dataSource: AttributeDatasource; + hidePageSize = false; activeValue = false; dirtyValue = false; @@ -129,10 +130,14 @@ export class AttributeTableComponent extends PageComponent implements AfterViewI aliasController: IAliasController; private widgetDatasource: Datasource; + private widgetResize$: ResizeObserver; + private disableAttributeScopeSelectionValue: boolean; + get disableAttributeScopeSelection(): boolean { return this.disableAttributeScopeSelectionValue; } + @Input() set disableAttributeScopeSelection(value: boolean) { this.disableAttributeScopeSelectionValue = coerceBooleanProperty(value); @@ -168,15 +173,11 @@ export class AttributeTableComponent extends PageComponent implements AfterViewI @Input() entityName: string; - @ViewChild('attributeTableContainer', {static: true}) attributeTableContainerRef: ElementRef; @ViewChild('searchInput') searchInputField: ElementRef; @ViewChild(MatPaginator) paginator: MatPaginator; @ViewChild(MatSort) sort: MatSort; - public hidePageSize = false; - private widgetResize$: ResizeObserver; - constructor(protected store: Store, private attributeService: AttributeService, private telemetryWsService: TelemetryWebsocketService, @@ -190,7 +191,8 @@ export class AttributeTableComponent extends PageComponent implements AfterViewI private dashboardUtils: DashboardUtilsService, private widgetService: WidgetService, private zone: NgZone, - private cd: ChangeDetectorRef) { + private cd: ChangeDetectorRef, + private elementRef: ElementRef) { super(store); this.dirtyValue = !this.activeValue; const sortOrder: SortOrder = { property: 'key', direction: Direction.ASC }; @@ -200,13 +202,13 @@ export class AttributeTableComponent extends PageComponent implements AfterViewI ngOnInit() { this.widgetResize$ = new ResizeObserver(() => { - const showHidePageSize = this.attributeTableContainerRef.nativeElement.offsetWidth < hidePageSizePixelValue; + const showHidePageSize = this.elementRef.nativeElement.offsetWidth < hidePageSizePixelValue; if (showHidePageSize !== this.hidePageSize) { this.hidePageSize = showHidePageSize; - this.cd.detectChanges(); + this.cd.markForCheck(); } }); - this.widgetResize$.observe(this.attributeTableContainerRef.nativeElement); + this.widgetResize$.observe(this.elementRef.nativeElement); } ngOnDestroy() { diff --git a/ui-ngx/src/app/modules/home/components/entity/entities-table.component.html b/ui-ngx/src/app/modules/home/components/entity/entities-table.component.html index aa86660ebe..a7b15670bb 100644 --- a/ui-ngx/src/app/modules/home/components/entity/entities-table.component.html +++ b/ui-ngx/src/app/modules/home/components/entity/entities-table.component.html @@ -32,7 +32,7 @@ -
+
diff --git a/ui-ngx/src/app/modules/home/components/entity/entities-table.component.scss b/ui-ngx/src/app/modules/home/components/entity/entities-table.component.scss index ac7648d07a..dde91efc26 100644 --- a/ui-ngx/src/app/modules/home/components/entity/entities-table.component.scss +++ b/ui-ngx/src/app/modules/home/components/entity/entities-table.component.scss @@ -18,6 +18,7 @@ :host { width: 100%; height: 100%; + display: block; .tb-entity-table { .tb-entity-table-content { width: 100%; diff --git a/ui-ngx/src/app/modules/home/components/entity/entities-table.component.ts b/ui-ngx/src/app/modules/home/components/entity/entities-table.component.ts index 1851d5e085..79166bb116 100644 --- a/ui-ngx/src/app/modules/home/components/entity/entities-table.component.ts +++ b/ui-ngx/src/app/modules/home/components/entity/entities-table.component.ts @@ -43,7 +43,8 @@ import { TranslateService } from '@ngx-translate/core'; import { BaseData, HasId } from '@shared/models/base-data'; import { ActivatedRoute } from '@angular/router'; import { - CellActionDescriptor, CellActionDescriptorType, + CellActionDescriptor, + CellActionDescriptorType, EntityActionTableColumn, EntityColumn, EntityTableColumn, @@ -55,11 +56,7 @@ import { EntityTypeTranslation } from '@shared/models/entity-type.models'; import { DialogService } from '@core/services/dialog.service'; import { AddEntityDialogComponent } from './add-entity-dialog.component'; import { AddEntityDialogData, EntityAction } from '@home/models/entity/entity-component.models'; -import { - calculateIntervalStartEndTime, - HistoryWindowType, - Timewindow -} from '@shared/models/time/time.models'; +import { calculateIntervalStartEndTime, HistoryWindowType, Timewindow } from '@shared/models/time/time.models'; import { DomSanitizer, SafeHtml } from '@angular/platform-browser'; import { TbAnchorComponent } from '@shared/components/tb-anchor.component'; import { isDefined, isUndefined } from '@core/utils'; @@ -100,6 +97,7 @@ export class EntitiesTableComponent extends PageComponent implements AfterViewIn defaultPageSize = 10; displayPagination = true; + hidePageSize = false; pageSizeOptions; pageLink: PageLink; textSearchMode = false; @@ -111,8 +109,6 @@ export class EntitiesTableComponent extends PageComponent implements AfterViewIn isDetailsOpen = false; detailsPanelOpened = new EventEmitter(); - @ViewChild('entitiesTableContainer', {static: true}) entitiesTableContainerRef: ElementRef; - @ViewChild('entityTableHeader', {static: true}) entityTableHeaderAnchor: TbAnchorComponent; @ViewChild('searchInput') searchInputField: ElementRef; @@ -125,7 +121,6 @@ export class EntitiesTableComponent extends PageComponent implements AfterViewIn private viewInited = false; private widgetResize$: ResizeObserver; - public hidePageSize = false; constructor(protected store: Store, public route: ActivatedRoute, @@ -134,7 +129,8 @@ export class EntitiesTableComponent extends PageComponent implements AfterViewIn private dialogService: DialogService, private domSanitizer: DomSanitizer, private cd: ChangeDetectorRef, - private componentFactoryResolver: ComponentFactoryResolver) { + private componentFactoryResolver: ComponentFactoryResolver, + private elementRef: ElementRef) { super(store); } @@ -145,13 +141,13 @@ export class EntitiesTableComponent extends PageComponent implements AfterViewIn this.init(this.route.snapshot.data.entitiesTableConfig); } this.widgetResize$ = new ResizeObserver(() => { - const showHidePageSize = this.entitiesTableContainerRef.nativeElement.offsetWidth < hidePageSizePixelValue; + const showHidePageSize = this.elementRef.nativeElement.offsetWidth < hidePageSizePixelValue; if (showHidePageSize !== this.hidePageSize) { this.hidePageSize = showHidePageSize; - this.cd.detectChanges(); + this.cd.markForCheck(); } }); - this.widgetResize$.observe(this.entitiesTableContainerRef.nativeElement); + this.widgetResize$.observe(this.elementRef.nativeElement); } ngOnDestroy() { diff --git a/ui-ngx/src/app/modules/home/components/relation/relation-table.component.html b/ui-ngx/src/app/modules/home/components/relation/relation-table.component.html index edf5d1d04d..b5ae8c2d52 100644 --- a/ui-ngx/src/app/modules/home/components/relation/relation-table.component.html +++ b/ui-ngx/src/app/modules/home/components/relation/relation-table.component.html @@ -15,7 +15,7 @@ limitations under the License. --> -
+
diff --git a/ui-ngx/src/app/modules/home/components/relation/relation-table.component.scss b/ui-ngx/src/app/modules/home/components/relation/relation-table.component.scss index dfb94a7383..b5c43647e3 100644 --- a/ui-ngx/src/app/modules/home/components/relation/relation-table.component.scss +++ b/ui-ngx/src/app/modules/home/components/relation/relation-table.component.scss @@ -18,6 +18,7 @@ :host { width: 100%; height: 100%; + display: block; .tb-entity-table { .tb-entity-table-content { width: 100%; diff --git a/ui-ngx/src/app/modules/home/components/relation/relation-table.component.ts b/ui-ngx/src/app/modules/home/components/relation/relation-table.component.ts index 8f0b503717..db1fad53b7 100644 --- a/ui-ngx/src/app/modules/home/components/relation/relation-table.component.ts +++ b/ui-ngx/src/app/modules/home/components/relation/relation-table.component.ts @@ -67,6 +67,7 @@ export class RelationTableComponent extends PageComponent implements AfterViewIn displayedColumns: string[]; direction: EntitySearchDirection; pageLink: PageLink; + hidePageSize = false; textSearchMode = false; dataSource: RelationsDatasource; @@ -77,7 +78,6 @@ export class RelationTableComponent extends PageComponent implements AfterViewIn viewsInited = false; private widgetResize$: ResizeObserver; - public hidePageSize = false; @Input() set active(active: boolean) { @@ -105,7 +105,6 @@ export class RelationTableComponent extends PageComponent implements AfterViewIn } } - @ViewChild('relationTableContainer', {static: true}) relationTableContainerRef: ElementRef; @ViewChild('searchInput') searchInputField: ElementRef; @ViewChild(MatPaginator) paginator: MatPaginator; @@ -116,7 +115,8 @@ export class RelationTableComponent extends PageComponent implements AfterViewIn public translate: TranslateService, public dialog: MatDialog, private dialogService: DialogService, - private cd: ChangeDetectorRef) { + private cd: ChangeDetectorRef, + private elementRef: ElementRef) { super(store); this.dirtyValue = !this.activeValue; const sortOrder: SortOrder = { property: 'type', direction: Direction.ASC }; @@ -128,13 +128,13 @@ export class RelationTableComponent extends PageComponent implements AfterViewIn ngOnInit() { this.widgetResize$ = new ResizeObserver(() => { - const showHidePageSize = this.relationTableContainerRef.nativeElement.offsetWidth < hidePageSizePixelValue; + const showHidePageSize = this.elementRef.nativeElement.offsetWidth < hidePageSizePixelValue; if (showHidePageSize !== this.hidePageSize) { this.hidePageSize = showHidePageSize; - this.cd.detectChanges(); + this.cd.markForCheck(); } }); - this.widgetResize$.observe(this.relationTableContainerRef.nativeElement); + this.widgetResize$.observe(this.elementRef.nativeElement); } ngOnDestroy() { diff --git a/ui-ngx/src/app/modules/home/components/widget/action/manage-widget-actions.component.html b/ui-ngx/src/app/modules/home/components/widget/action/manage-widget-actions.component.html index d0c1db566c..9512c58b4a 100644 --- a/ui-ngx/src/app/modules/home/components/widget/action/manage-widget-actions.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/action/manage-widget-actions.component.html @@ -15,7 +15,7 @@ limitations under the License. --> -
+
@@ -119,6 +119,7 @@ + [pageSizeOptions]="[10, 20, 30]" + [hidePageSize]="hidePageSize">
diff --git a/ui-ngx/src/app/modules/home/components/widget/action/manage-widget-actions.component.scss b/ui-ngx/src/app/modules/home/components/widget/action/manage-widget-actions.component.scss index d71ec2e0d1..3406215b33 100644 --- a/ui-ngx/src/app/modules/home/components/widget/action/manage-widget-actions.component.scss +++ b/ui-ngx/src/app/modules/home/components/widget/action/manage-widget-actions.component.scss @@ -16,6 +16,7 @@ :host { width: 100%; height: 100%; + display: block; .tb-entity-table { .tb-entity-table-content { width: 100%; diff --git a/ui-ngx/src/app/modules/home/components/widget/action/manage-widget-actions.component.ts b/ui-ngx/src/app/modules/home/components/widget/action/manage-widget-actions.component.ts index 5173e00954..f131b84783 100644 --- a/ui-ngx/src/app/modules/home/components/widget/action/manage-widget-actions.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/action/manage-widget-actions.component.ts @@ -46,13 +46,12 @@ import { WidgetActionsDatasource } from '@home/components/widget/action/manage-widget-actions.component.models'; import { UtilsService } from '@core/services/utils.service'; -import { WidgetActionDescriptor, WidgetActionSource } from '@shared/models/widget.models'; +import { WidgetActionDescriptor, WidgetActionSource, widgetType } from '@shared/models/widget.models'; import { WidgetActionDialogComponent, WidgetActionDialogData } from '@home/components/widget/action/widget-action-dialog.component'; import { deepClone } from '@core/utils'; -import { widgetType } from '@shared/models/widget.models'; import { ResizeObserver } from '@juggle/resize-observer'; import { hidePageSizePixelValue } from '@shared/models/constants'; @@ -81,14 +80,14 @@ export class ManageWidgetActionsComponent extends PageComponent implements OnIni displayedColumns: string[]; pageLink: PageLink; textSearchMode = false; + hidePageSize = false; dataSource: WidgetActionsDatasource; viewsInited = false; dirtyValue = false; - public hidePageSize = false; + private widgetResize$: ResizeObserver; - @ViewChild('manageActionWidgetContainer', {static: true}) manageActionWidgetContainerRef: ElementRef; @ViewChild('searchInput') searchInputField: ElementRef; @ViewChild(MatPaginator) paginator: MatPaginator; @@ -101,7 +100,8 @@ export class ManageWidgetActionsComponent extends PageComponent implements OnIni private utils: UtilsService, private dialog: MatDialog, private dialogs: DialogService, - private cd: ChangeDetectorRef) { + private cd: ChangeDetectorRef, + private elementRef: ElementRef) { super(store); const sortOrder: SortOrder = { property: 'actionSourceName', direction: Direction.ASC }; this.pageLink = new PageLink(10, 0, null, sortOrder); @@ -111,13 +111,13 @@ export class ManageWidgetActionsComponent extends PageComponent implements OnIni ngOnInit(): void { this.widgetResize$ = new ResizeObserver(() => { - const showHidePageSize = this.manageActionWidgetContainerRef.nativeElement.offsetWidth < hidePageSizePixelValue; + const showHidePageSize = this.elementRef.nativeElement.offsetWidth < hidePageSizePixelValue; if (showHidePageSize !== this.hidePageSize) { this.hidePageSize = showHidePageSize; - this.cd.detectChanges(); + this.cd.markForCheck(); } }); - this.widgetResize$.observe(this.manageActionWidgetContainerRef.nativeElement); + this.widgetResize$.observe(this.elementRef.nativeElement); } ngOnDestroy(): void { diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/alarms-table-widget.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/alarms-table-widget.component.html index 872e040bad..a2b0868370 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/alarms-table-widget.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/alarms-table-widget.component.html @@ -15,7 +15,7 @@ limitations under the License. --> -
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/alarms-table-widget.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/alarms-table-widget.component.scss index edc52a9c48..15664c4750 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/alarms-table-widget.component.scss +++ b/ui-ngx/src/app/modules/home/components/widget/lib/alarms-table-widget.component.scss @@ -16,6 +16,7 @@ :host { width: 100%; height: 100%; + display: block; .tb-table-widget { .table-container { position: relative; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/alarms-table-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/alarms-table-widget.component.ts index e12fcfcebd..5cd0f9643d 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/alarms-table-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/alarms-table-widget.component.ts @@ -16,6 +16,7 @@ import { AfterViewInit, + ChangeDetectorRef, Component, ElementRef, EventEmitter, @@ -154,7 +155,6 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit, @Input() ctx: WidgetContext; - @ViewChild('alarmWidgetContainer', {static: true}) alarmWidgetContainerRef: ElementRef; @ViewChild('searchInput') searchInputField: ElementRef; @ViewChild(MatPaginator) paginator: MatPaginator; @ViewChild(MatSort) sort: MatSort; @@ -167,11 +167,11 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit, public pageLink: AlarmDataPageLink; public sortOrderProperty: string; public textSearchMode = false; + public hidePageSize = false; public columns: Array = []; public displayedColumns: string[] = []; public alarmsDatasource: AlarmsDatasource; public noDataDisplayMessageText: string; - public hidePageSize = false; private setCellButtonAction: boolean; private cellContentCache: Array = []; @@ -240,7 +240,8 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit, private datePipe: DatePipe, private dialog: MatDialog, private dialogService: DialogService, - private alarmService: AlarmService) { + private alarmService: AlarmService, + private cd: ChangeDetectorRef) { super(store); this.pageLink = { page: 0, @@ -263,13 +264,13 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit, () => this.pageLink.page = 0 ); this.widgetResize$ = new ResizeObserver(() => { - const showHidePageSize = this.alarmWidgetContainerRef.nativeElement.offsetWidth < hidePageSizePixelValue; + const showHidePageSize = this.elementRef.nativeElement.offsetWidth < hidePageSizePixelValue; if (showHidePageSize !== this.hidePageSize) { this.hidePageSize = showHidePageSize; - this.ctx.detectChanges(); + this.cd.markForCheck(); } }); - this.widgetResize$.observe(this.alarmWidgetContainerRef.nativeElement); + this.widgetResize$.observe(this.elementRef.nativeElement); } } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/entities-table-widget.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/entities-table-widget.component.html index 7f8ae9b9b1..c2d1997945 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/entities-table-widget.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/entities-table-widget.component.html @@ -15,7 +15,7 @@ limitations under the License. --> -
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/entities-table-widget.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/entities-table-widget.component.scss index edc52a9c48..15664c4750 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/entities-table-widget.component.scss +++ b/ui-ngx/src/app/modules/home/components/widget/lib/entities-table-widget.component.scss @@ -16,6 +16,7 @@ :host { width: 100%; height: 100%; + display: block; .tb-table-widget { .table-container { position: relative; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/entities-table-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/entities-table-widget.component.ts index 32a07cecb8..f4429fb4cc 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/entities-table-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/entities-table-widget.component.ts @@ -16,6 +16,7 @@ import { AfterViewInit, + ChangeDetectorRef, Component, ElementRef, Injector, @@ -131,7 +132,6 @@ export class EntitiesTableWidgetComponent extends PageComponent implements OnIni @Input() ctx: WidgetContext; - @ViewChild('entitiesWidgetContainer', {static: true}) entitiesWidgetContainerRef: ElementRef; @ViewChild('searchInput') searchInputField: ElementRef; @ViewChild(MatPaginator) paginator: MatPaginator; @ViewChild(MatSort) sort: MatSort; @@ -143,11 +143,11 @@ export class EntitiesTableWidgetComponent extends PageComponent implements OnIni public pageLink: EntityDataPageLink; public sortOrderProperty: string; public textSearchMode = false; + public hidePageSize = false; public columns: Array = []; public displayedColumns: string[] = []; public entityDatasource: EntityDatasource; public noDataDisplayMessageText: string; - public hidePageSize = false; private setCellButtonAction: boolean; private cellContentCache: Array = []; @@ -198,7 +198,8 @@ export class EntitiesTableWidgetComponent extends PageComponent implements OnIni private utils: UtilsService, private datePipe: DatePipe, private translate: TranslateService, - private domSanitizer: DomSanitizer) { + private domSanitizer: DomSanitizer, + private cd: ChangeDetectorRef) { super(store); this.pageLink = { page: 0, @@ -218,13 +219,13 @@ export class EntitiesTableWidgetComponent extends PageComponent implements OnIni this.ctx.updateWidgetParams(); if (this.displayPagination) { this.widgetResize$ = new ResizeObserver(() => { - const showHidePageSize = this.entitiesWidgetContainerRef.nativeElement.offsetWidth < hidePageSizePixelValue; + const showHidePageSize = this.elementRef.nativeElement.offsetWidth < hidePageSizePixelValue; if (showHidePageSize !== this.hidePageSize) { this.hidePageSize = showHidePageSize; - this.ctx.detectChanges(); + this.cd.markForCheck(); } }); - this.widgetResize$.observe(this.entitiesWidgetContainerRef.nativeElement); + this.widgetResize$.observe(this.elementRef.nativeElement); } } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/timeseries-table-widget.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/timeseries-table-widget.component.html index 236d81036c..6e37d38762 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/timeseries-table-widget.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/timeseries-table-widget.component.html @@ -15,7 +15,7 @@ limitations under the License. --> -
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/timeseries-table-widget.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/timeseries-table-widget.component.scss index 57052d4bf8..eb309c2cf7 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/timeseries-table-widget.component.scss +++ b/ui-ngx/src/app/modules/home/components/widget/lib/timeseries-table-widget.component.scss @@ -16,6 +16,7 @@ :host { width: 100%; height: 100%; + display: block; .tb-table-widget { mat-footer-row, mat-row { height: 38px; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/timeseries-table-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/timeseries-table-widget.component.ts index a5a2cbc12e..07d35a1405 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/timeseries-table-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/timeseries-table-widget.component.ts @@ -16,6 +16,7 @@ import { AfterViewInit, + ChangeDetectorRef, Component, ElementRef, Input, @@ -117,7 +118,6 @@ export class TimeseriesTableWidgetComponent extends PageComponent implements OnI @Input() ctx: WidgetContext; - @ViewChild('timeseriesWidgetContainer', {static: true}) timeseriesWidgetContainerRef: ElementRef; @ViewChild('searchInput') searchInputField: ElementRef; @ViewChildren(MatPaginator) paginators: QueryList; @ViewChildren(MatSort) sorts: QueryList; @@ -127,11 +127,11 @@ export class TimeseriesTableWidgetComponent extends PageComponent implements OnI public enableStickyAction = true; public pageSizeOptions; public textSearchMode = false; + public hidePageSize = false; public textSearch: string = null; public sources: TimeseriesTableSource[]; public sourceIndex: number; public noDataDisplayMessageText: string; - public hidePageSize = false; private setCellButtonAction: boolean; private cellContentCache: Array = []; @@ -172,7 +172,8 @@ export class TimeseriesTableWidgetComponent extends PageComponent implements OnI private utils: UtilsService, private translate: TranslateService, private domSanitizer: DomSanitizer, - private datePipe: DatePipe) { + private datePipe: DatePipe, + private cd: ChangeDetectorRef) { super(store); } @@ -196,13 +197,13 @@ export class TimeseriesTableWidgetComponent extends PageComponent implements OnI } ); this.widgetResize$ = new ResizeObserver(() => { - const showHidePageSize = this.timeseriesWidgetContainerRef.nativeElement.offsetWidth < hidePageSizePixelValue; + const showHidePageSize = this.elementRef.nativeElement.offsetWidth < hidePageSizePixelValue; if (showHidePageSize !== this.hidePageSize) { this.hidePageSize = showHidePageSize; - this.ctx.detectChanges(); + this.cd.markForCheck(); } }); - this.widgetResize$.observe(this.timeseriesWidgetContainerRef.nativeElement); + this.widgetResize$.observe(this.elementRef.nativeElement); } } From 452ea5f0e42168b7e212cbae92fa98c48528cb3b Mon Sep 17 00:00:00 2001 From: zbeacon Date: Thu, 16 Dec 2021 16:37:32 +0200 Subject: [PATCH 021/798] Init commit for notifications to gateway --- .../server/controller/DeviceController.java | 4 + .../DefaultGatewayDeviceStateService.java | 137 ++++++++++++++++++ .../GatewayDeviceStateService.java | 10 ++ .../queue/DefaultTbClusterService.java | 3 + .../transport/DefaultTransportApiService.java | 13 ++ .../server/common/data/DataConstants.java | 1 + 6 files changed, 168 insertions(+) create mode 100644 application/src/main/java/org/thingsboard/server/service/gateway_device/DefaultGatewayDeviceStateService.java create mode 100644 application/src/main/java/org/thingsboard/server/service/gateway_device/GatewayDeviceStateService.java diff --git a/application/src/main/java/org/thingsboard/server/controller/DeviceController.java b/application/src/main/java/org/thingsboard/server/controller/DeviceController.java index 6ea6bdd880..9674e8eba9 100644 --- a/application/src/main/java/org/thingsboard/server/controller/DeviceController.java +++ b/application/src/main/java/org/thingsboard/server/controller/DeviceController.java @@ -74,6 +74,7 @@ import org.thingsboard.server.dao.exception.IncorrectParameterException; import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.device.DeviceBulkImportService; +import org.thingsboard.server.service.gateway_device.GatewayDeviceStateService; import org.thingsboard.server.service.importing.BulkImportRequest; import org.thingsboard.server.service.importing.BulkImportResult; import org.thingsboard.server.service.security.model.SecurityUser; @@ -128,6 +129,8 @@ public class DeviceController extends BaseController { private final DeviceBulkImportService deviceBulkImportService; + private final GatewayDeviceStateService gatewayDeviceStatusService; + @ApiOperation(value = "Get Device (getDeviceById)", notes = "Fetch the Device object based on the provided Device Id. " + "If the user has the authority of 'TENANT_ADMIN', the server checks that the device is owned by the same tenant. " + @@ -261,6 +264,7 @@ public class DeviceController extends BaseController { List relatedEdgeIds = findRelatedEdgeIds(getTenantId(), deviceId); + gatewayDeviceStatusService.delete(device); deviceService.deleteDevice(getCurrentUser().getTenantId(), deviceId); tbClusterService.onDeviceDeleted(device, null); diff --git a/application/src/main/java/org/thingsboard/server/service/gateway_device/DefaultGatewayDeviceStateService.java b/application/src/main/java/org/thingsboard/server/service/gateway_device/DefaultGatewayDeviceStateService.java new file mode 100644 index 0000000000..7d9168e358 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/gateway_device/DefaultGatewayDeviceStateService.java @@ -0,0 +1,137 @@ +package org.thingsboard.server.service.gateway_device; + +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.google.common.util.concurrent.FutureCallback; +import com.google.common.util.concurrent.ListenableFuture; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Service; +import org.thingsboard.common.util.DonAsynchron; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.DataConstants; +import org.thingsboard.server.common.data.Device; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.kv.AttributeKvEntry; +import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; +import org.thingsboard.server.common.data.kv.JsonDataEntry; +import org.thingsboard.server.common.data.kv.KvEntry; +import org.thingsboard.server.common.data.relation.EntityRelation; +import org.thingsboard.server.common.data.relation.RelationTypeGroup; +import org.thingsboard.server.dao.attributes.AttributesService; +import org.thingsboard.server.dao.relation.RelationService; +import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; + +@Slf4j +@Service +@RequiredArgsConstructor +public class DefaultGatewayDeviceStateService implements GatewayDeviceStateService { + + private static final String RENAMED_GATEWAY_DEVICES = "renamedGatewayDevices"; + + @Lazy + @Autowired + private TelemetrySubscriptionService tsSubService; + private final AttributesService attributesService; + private final RelationService relationService; + + @Override + public void update(Device device, Device oldDevice) { + List relationToGatewayList = relationService.findByFromAndType(TenantId.SYS_TENANT_ID, device.getId(), DataConstants.LAST_CONNECTED_GATEWAY, RelationTypeGroup.COMMON); + if (!relationToGatewayList.isEmpty()) { + EntityRelation relationToGateway = relationToGatewayList.get(0); + ListenableFuture> renamedGatewayDevicesFuture = attributesService.find(device.getTenantId(), relationToGateway.getTo(), DataConstants.SHARED_SCOPE, Collections.singletonList("renamedGatewayDevices")); + DonAsynchron.withCallback(renamedGatewayDevicesFuture, renamedGatewayDevicesList -> { + ObjectNode renamedGatewayDevicesNode; + KvEntry renamedGatewayDevicesKvEntry; + String newDeviceName = device.getName(); + String oldDeviceName = oldDevice.getName(); + + if (renamedGatewayDevicesList.isEmpty()) { + renamedGatewayDevicesNode = JacksonUtil.newObjectNode(); + renamedGatewayDevicesNode.put(oldDeviceName, newDeviceName); + } else { + AttributeKvEntry receivedRenamedGatewayDevicesAttribute = renamedGatewayDevicesList.get(0); + renamedGatewayDevicesNode = (ObjectNode) JacksonUtil.toJsonNode(receivedRenamedGatewayDevicesAttribute.getValueAsString()); + if (renamedGatewayDevicesNode.findValue(newDeviceName) != null) { + // If new device name is the same like the first name + renamedGatewayDevicesNode.remove(newDeviceName); + } else if (renamedGatewayDevicesNode.findValue(oldDeviceName) == null) { + + AtomicBoolean renamedFirstTime = new AtomicBoolean(true); + + renamedGatewayDevicesNode.fields().forEachRemaining(entry -> { + // If device was renamed earlier + if (oldDeviceName.equals(entry.getValue().asText())) { + renamedGatewayDevicesNode.put(entry.getKey(), newDeviceName); + renamedFirstTime.set(false); + } + }); + if (renamedFirstTime.get()) { + renamedGatewayDevicesNode.put(oldDeviceName, newDeviceName); + } + } + } + + renamedGatewayDevicesKvEntry = new JsonDataEntry(RENAMED_GATEWAY_DEVICES, JacksonUtil.toString(renamedGatewayDevicesNode)); + saveGatewayDevicesAttribute(device, relationToGateway, renamedGatewayDevicesKvEntry); + }, + e -> log.error("Cannot get gateway renamed devices attribute", e)); + } + } + + @Override + public void delete(Device device) { + List relationToGatewayList = relationService.findByFromAndType(TenantId.SYS_TENANT_ID, device.getId(), DataConstants.LAST_CONNECTED_GATEWAY, RelationTypeGroup.COMMON); + if (!relationToGatewayList.isEmpty()) { + EntityRelation relationToGateway = relationToGatewayList.get(0); + String deletedDeviceName = device.getName(); + ListenableFuture> renamedGatewayDevicesFuture = attributesService.find(device.getTenantId(), relationToGateway.getTo(), DataConstants.SHARED_SCOPE, Collections.singletonList("renamedGatewayDevices")); + DonAsynchron.withCallback(renamedGatewayDevicesFuture, renamedGatewayDevicesList -> { + if (!renamedGatewayDevicesList.isEmpty()) { + ObjectNode renamedGatewayDevicesNode = JacksonUtil.fromString(renamedGatewayDevicesList.get(0).getValueAsString(), ObjectNode.class); + if (renamedGatewayDevicesNode != null && renamedGatewayDevicesNode.findValue(deletedDeviceName) != null) { + renamedGatewayDevicesNode.remove(deletedDeviceName); + KvEntry renamedGatewayDevicesKvEntry = new JsonDataEntry(RENAMED_GATEWAY_DEVICES, JacksonUtil.toString(renamedGatewayDevicesNode)); + saveGatewayDevicesAttribute(device, relationToGateway, renamedGatewayDevicesKvEntry); + } + } + }, e -> log.error("Cannot get gateway renamed devices attribute", e)); + ListenableFuture> deletedGatewayDevicesFuture = attributesService.find(device.getTenantId(), relationToGateway.getTo(), DataConstants.SHARED_SCOPE, Collections.singletonList("deletedGatewayDevices")); + DonAsynchron.withCallback(deletedGatewayDevicesFuture, deletedGatewayDevicesList -> { + ArrayNode deletedGatewayDevicesNode; + if (!deletedGatewayDevicesList.isEmpty()) { + deletedGatewayDevicesNode = (ArrayNode) JacksonUtil.toJsonNode(deletedGatewayDevicesList.get(0).getValueAsString()); + } else { + deletedGatewayDevicesNode = JacksonUtil.OBJECT_MAPPER.createArrayNode(); + } + deletedGatewayDevicesNode.add(deletedDeviceName); + KvEntry renamedGatewayDevicesKvEntry = new JsonDataEntry(RENAMED_GATEWAY_DEVICES, JacksonUtil.toString(deletedGatewayDevicesNode)); + saveGatewayDevicesAttribute(device, relationToGateway, renamedGatewayDevicesKvEntry); + }, e -> log.error("Cannot get gateway deleted devices attribute", e)); + } + } + + private void saveGatewayDevicesAttribute(Device device, EntityRelation relationToGateway, KvEntry renamedGatewayDevicesKvEntry) { + AttributeKvEntry attrKvEntry = new BaseAttributeKvEntry(System.currentTimeMillis(), renamedGatewayDevicesKvEntry); + tsSubService.saveAndNotify(device.getTenantId(), relationToGateway.getTo(), DataConstants.SHARED_SCOPE, List.of(attrKvEntry), true, new FutureCallback() { + @Override + public void onSuccess(@Nullable Void unused) { + log.trace("Attribute saved for gateway with ID [{}] and data [{}]", relationToGateway.getTo(), renamedGatewayDevicesKvEntry.getJsonValue()); + } + + @Override + public void onFailure(Throwable throwable) { + log.error("Cannot save gateway device attribute", throwable); + } + }); + } +} \ No newline at end of file diff --git a/application/src/main/java/org/thingsboard/server/service/gateway_device/GatewayDeviceStateService.java b/application/src/main/java/org/thingsboard/server/service/gateway_device/GatewayDeviceStateService.java new file mode 100644 index 0000000000..f086b25d5a --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/gateway_device/GatewayDeviceStateService.java @@ -0,0 +1,10 @@ +package org.thingsboard.server.service.gateway_device; + +import org.thingsboard.server.common.data.Device; + +public interface GatewayDeviceStateService { + + void update(Device device, Device oldDevice); + + void delete(Device device); +} diff --git a/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbClusterService.java b/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbClusterService.java index 0a82994a42..be44c9ee40 100644 --- a/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbClusterService.java +++ b/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbClusterService.java @@ -61,6 +61,7 @@ import org.thingsboard.server.queue.common.MultipleTbQueueCallbackWrapper; import org.thingsboard.server.queue.common.TbProtoQueueMsg; import org.thingsboard.server.queue.discovery.PartitionService; import org.thingsboard.server.queue.provider.TbQueueProducerProvider; +import org.thingsboard.server.service.gateway_device.GatewayDeviceStateService; import org.thingsboard.server.service.ota.OtaPackageStateService; import org.thingsboard.server.service.profile.TbDeviceProfileCache; import org.thingsboard.server.common.msg.rpc.FromDeviceRpcResponse; @@ -91,6 +92,7 @@ public class DefaultTbClusterService implements TbClusterService { private final DataDecodingEncodingService encodingService; private final TbDeviceProfileCache deviceProfileCache; private final OtaPackageStateService otaPackageStateService; + private final GatewayDeviceStateService gatewayDeviceStateService; @Override public void pushMsgToCore(TenantId tenantId, EntityId entityId, ToCoreMsg msg, TbQueueCallback callback) { @@ -405,6 +407,7 @@ public class DefaultTbClusterService implements TbClusterService { broadcastEntityStateChangeEvent(device.getTenantId(), device.getId(), created ? ComponentLifecycleEvent.CREATED : ComponentLifecycleEvent.UPDATED); sendDeviceStateServiceEvent(device.getTenantId(), device.getId(), created, !created, false); otaPackageStateService.update(device, old); + gatewayDeviceStateService.update(device, old); if (!created && notifyEdge) { sendNotificationMsgToEdgeService(device.getTenantId(), null, device.getId(), null, null, EdgeEventActionType.UPDATED); } diff --git a/application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java b/application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java index 1cefefd5d1..cb4e01032f 100644 --- a/application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java +++ b/application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java @@ -56,6 +56,7 @@ import org.thingsboard.server.common.data.ota.OtaPackageUtil; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.relation.EntityRelation; +import org.thingsboard.server.common.data.relation.RelationTypeGroup; import org.thingsboard.server.common.data.security.DeviceCredentials; import org.thingsboard.server.common.data.security.DeviceCredentialsType; import org.thingsboard.server.common.msg.EncryptionUtil; @@ -99,6 +100,7 @@ import org.thingsboard.server.cluster.TbClusterService; import org.thingsboard.server.service.resource.TbResourceService; import org.thingsboard.server.service.state.DeviceStateService; +import java.util.List; import java.util.Optional; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; @@ -304,6 +306,17 @@ public class DefaultTransportApiService implements TransportApiService { TbMsg tbMsg = TbMsg.newMsg(DataConstants.ENTITY_CREATED, deviceId, customerId, metaData, TbMsgDataType.JSON, mapper.writeValueAsString(entityNode)); tbClusterService.pushMsgToRuleEngine(tenantId, deviceId, tbMsg, null); } + + List currentLastConnectedGatewayRelationList = relationService.findByFromAndType(TenantId.SYS_TENANT_ID, device.getId(), DataConstants.LAST_CONNECTED_GATEWAY, RelationTypeGroup.COMMON); + EntityRelation lastConnectedGatewayRelation; + if (!currentLastConnectedGatewayRelationList.isEmpty()) { + lastConnectedGatewayRelation = currentLastConnectedGatewayRelationList.get(0); + lastConnectedGatewayRelation.setTo(gateway.getId()); + } else { + lastConnectedGatewayRelation = new EntityRelation(device.getId(), gateway.getId(), DataConstants.LAST_CONNECTED_GATEWAY); + } + relationService.saveRelationAsync(TenantId.SYS_TENANT_ID, lastConnectedGatewayRelation); + GetOrCreateDeviceFromGatewayResponseMsg.Builder builder = GetOrCreateDeviceFromGatewayResponseMsg.newBuilder() .setDeviceInfo(getDeviceInfoProto(device)); DeviceProfile deviceProfile = deviceProfileCache.get(device.getTenantId(), device.getDeviceProfileId()); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/DataConstants.java b/common/data/src/main/java/org/thingsboard/server/common/data/DataConstants.java index 707a50dfb3..40f6c13794 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/DataConstants.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/DataConstants.java @@ -116,4 +116,5 @@ public class DataConstants { public static final String EDGE_MSG_SOURCE = "edge"; public static final String MSG_SOURCE_KEY = "source"; + public static final String LAST_CONNECTED_GATEWAY = "lastConnectedGateway"; } From 1ebf7f703906081ea7dd0ce41f2df92ed671a0bc Mon Sep 17 00:00:00 2001 From: zbeacon Date: Fri, 17 Dec 2021 10:57:46 +0200 Subject: [PATCH 022/798] Refactoring of removing devices --- .../DefaultGatewayDeviceStateService.java | 56 ++++++++++++------- 1 file changed, 37 insertions(+), 19 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/gateway_device/DefaultGatewayDeviceStateService.java b/application/src/main/java/org/thingsboard/server/service/gateway_device/DefaultGatewayDeviceStateService.java index 7d9168e358..f3aee5a109 100644 --- a/application/src/main/java/org/thingsboard/server/service/gateway_device/DefaultGatewayDeviceStateService.java +++ b/application/src/main/java/org/thingsboard/server/service/gateway_device/DefaultGatewayDeviceStateService.java @@ -1,5 +1,6 @@ package org.thingsboard.server.service.gateway_device; +import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.ObjectNode; import com.google.common.util.concurrent.FutureCallback; @@ -25,9 +26,10 @@ import org.thingsboard.server.dao.attributes.AttributesService; import org.thingsboard.server.dao.relation.RelationService; import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService; -import java.util.ArrayList; import java.util.Collections; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.concurrent.atomic.AtomicBoolean; @Slf4j @@ -36,12 +38,12 @@ import java.util.concurrent.atomic.AtomicBoolean; public class DefaultGatewayDeviceStateService implements GatewayDeviceStateService { private static final String RENAMED_GATEWAY_DEVICES = "renamedGatewayDevices"; - + private static final String DELETED_GATEWAY_DEVICES = "deletedGatewayDevices"; + private final AttributesService attributesService; + private final RelationService relationService; @Lazy @Autowired private TelemetrySubscriptionService tsSubService; - private final AttributesService attributesService; - private final RelationService relationService; @Override public void update(Device device, Device oldDevice) { @@ -61,10 +63,10 @@ public class DefaultGatewayDeviceStateService implements GatewayDeviceStateServi } else { AttributeKvEntry receivedRenamedGatewayDevicesAttribute = renamedGatewayDevicesList.get(0); renamedGatewayDevicesNode = (ObjectNode) JacksonUtil.toJsonNode(receivedRenamedGatewayDevicesAttribute.getValueAsString()); - if (renamedGatewayDevicesNode.findValue(newDeviceName) != null) { - // If new device name is the same like the first name + if (renamedGatewayDevicesNode.findValue(newDeviceName) != null && oldDeviceName.equals(renamedGatewayDevicesNode.get(newDeviceName).asText())) { + // If a new device name is the same like the first name or another device was renamed like some existing device renamedGatewayDevicesNode.remove(newDeviceName); - } else if (renamedGatewayDevicesNode.findValue(oldDeviceName) == null) { + } else { AtomicBoolean renamedFirstTime = new AtomicBoolean(true); @@ -93,15 +95,31 @@ public class DefaultGatewayDeviceStateService implements GatewayDeviceStateServi List relationToGatewayList = relationService.findByFromAndType(TenantId.SYS_TENANT_ID, device.getId(), DataConstants.LAST_CONNECTED_GATEWAY, RelationTypeGroup.COMMON); if (!relationToGatewayList.isEmpty()) { EntityRelation relationToGateway = relationToGatewayList.get(0); - String deletedDeviceName = device.getName(); + final String[] deletedDeviceName = {device.getName()}; ListenableFuture> renamedGatewayDevicesFuture = attributesService.find(device.getTenantId(), relationToGateway.getTo(), DataConstants.SHARED_SCOPE, Collections.singletonList("renamedGatewayDevices")); DonAsynchron.withCallback(renamedGatewayDevicesFuture, renamedGatewayDevicesList -> { if (!renamedGatewayDevicesList.isEmpty()) { - ObjectNode renamedGatewayDevicesNode = JacksonUtil.fromString(renamedGatewayDevicesList.get(0).getValueAsString(), ObjectNode.class); - if (renamedGatewayDevicesNode != null && renamedGatewayDevicesNode.findValue(deletedDeviceName) != null) { - renamedGatewayDevicesNode.remove(deletedDeviceName); - KvEntry renamedGatewayDevicesKvEntry = new JsonDataEntry(RENAMED_GATEWAY_DEVICES, JacksonUtil.toString(renamedGatewayDevicesNode)); - saveGatewayDevicesAttribute(device, relationToGateway, renamedGatewayDevicesKvEntry); + ObjectNode renamedGatewayDevicesNode = (ObjectNode) JacksonUtil.toJsonNode(renamedGatewayDevicesList.get(0).getValueAsString()); + if (renamedGatewayDevicesNode != null) { + AtomicBoolean renamedListChanged = new AtomicBoolean(false); + if (renamedGatewayDevicesNode.findValue(deletedDeviceName[0]) != null) { + renamedGatewayDevicesNode.remove(deletedDeviceName[0]); + renamedListChanged.set(true); + } else { + Map renamedGatewayDevicesMap = JacksonUtil.OBJECT_MAPPER.convertValue(renamedGatewayDevicesNode, new TypeReference<>() {}); + renamedGatewayDevicesMap.forEach((key, value) -> { + // If device was renamed earlier + if (deletedDeviceName[0].equals(value)) { + renamedGatewayDevicesNode.remove(key); + deletedDeviceName[0] = key; + renamedListChanged.set(true); + } + }); + } + if (renamedListChanged.get()) { + KvEntry renamedGatewayDevicesKvEntry = new JsonDataEntry(RENAMED_GATEWAY_DEVICES, JacksonUtil.toString(renamedGatewayDevicesNode)); + saveGatewayDevicesAttribute(device, relationToGateway, renamedGatewayDevicesKvEntry); + } } } }, e -> log.error("Cannot get gateway renamed devices attribute", e)); @@ -113,19 +131,19 @@ public class DefaultGatewayDeviceStateService implements GatewayDeviceStateServi } else { deletedGatewayDevicesNode = JacksonUtil.OBJECT_MAPPER.createArrayNode(); } - deletedGatewayDevicesNode.add(deletedDeviceName); - KvEntry renamedGatewayDevicesKvEntry = new JsonDataEntry(RENAMED_GATEWAY_DEVICES, JacksonUtil.toString(deletedGatewayDevicesNode)); - saveGatewayDevicesAttribute(device, relationToGateway, renamedGatewayDevicesKvEntry); + deletedGatewayDevicesNode.add(deletedDeviceName[0]); + KvEntry deletedGatewayDevicesKvEntry = new JsonDataEntry(DELETED_GATEWAY_DEVICES, JacksonUtil.toString(deletedGatewayDevicesNode)); + saveGatewayDevicesAttribute(device, relationToGateway, deletedGatewayDevicesKvEntry); }, e -> log.error("Cannot get gateway deleted devices attribute", e)); } } - private void saveGatewayDevicesAttribute(Device device, EntityRelation relationToGateway, KvEntry renamedGatewayDevicesKvEntry) { - AttributeKvEntry attrKvEntry = new BaseAttributeKvEntry(System.currentTimeMillis(), renamedGatewayDevicesKvEntry); + private void saveGatewayDevicesAttribute(Device device, EntityRelation relationToGateway, KvEntry gatewayDevicesKvEntry) { + AttributeKvEntry attrKvEntry = new BaseAttributeKvEntry(System.currentTimeMillis(), gatewayDevicesKvEntry); tsSubService.saveAndNotify(device.getTenantId(), relationToGateway.getTo(), DataConstants.SHARED_SCOPE, List.of(attrKvEntry), true, new FutureCallback() { @Override public void onSuccess(@Nullable Void unused) { - log.trace("Attribute saved for gateway with ID [{}] and data [{}]", relationToGateway.getTo(), renamedGatewayDevicesKvEntry.getJsonValue()); + log.trace("Attribute saved for gateway with ID [{}] and data [{}]", relationToGateway.getTo(), gatewayDevicesKvEntry.getJsonValue()); } @Override From b76088892b1ca3d5a047c14172d0cf5320afd529 Mon Sep 17 00:00:00 2001 From: zbeacon Date: Fri, 17 Dec 2021 13:49:05 +0200 Subject: [PATCH 023/798] Added "handle device renaming parameter to the gateway" --- .../DefaultGatewayDeviceStateService.java | 151 ++++++++++++------ .../GatewayDeviceStateService.java | 17 ++ .../transport/DefaultTransportApiService.java | 4 + .../device-wizard-dialog.component.html | 6 + .../wizard/device-wizard-dialog.component.ts | 2 + .../home/pages/device/device.component.html | 6 + .../home/pages/device/device.component.ts | 2 + .../assets/locale/locale.constant-en_US.json | 1 + 8 files changed, 143 insertions(+), 46 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/gateway_device/DefaultGatewayDeviceStateService.java b/application/src/main/java/org/thingsboard/server/service/gateway_device/DefaultGatewayDeviceStateService.java index f3aee5a109..57a0b36508 100644 --- a/application/src/main/java/org/thingsboard/server/service/gateway_device/DefaultGatewayDeviceStateService.java +++ b/application/src/main/java/org/thingsboard/server/service/gateway_device/DefaultGatewayDeviceStateService.java @@ -1,6 +1,22 @@ +/** + * Copyright © 2016-2021 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.service.gateway_device; import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.ObjectNode; import com.google.common.util.concurrent.FutureCallback; @@ -15,6 +31,7 @@ import org.thingsboard.common.util.DonAsynchron; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.Device; +import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.kv.AttributeKvEntry; import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; @@ -23,11 +40,11 @@ import org.thingsboard.server.common.data.kv.KvEntry; import org.thingsboard.server.common.data.relation.EntityRelation; import org.thingsboard.server.common.data.relation.RelationTypeGroup; import org.thingsboard.server.dao.attributes.AttributesService; +import org.thingsboard.server.dao.device.DeviceService; import org.thingsboard.server.dao.relation.RelationService; import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService; import java.util.Collections; -import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicBoolean; @@ -39,8 +56,11 @@ public class DefaultGatewayDeviceStateService implements GatewayDeviceStateServi private static final String RENAMED_GATEWAY_DEVICES = "renamedGatewayDevices"; private static final String DELETED_GATEWAY_DEVICES = "deletedGatewayDevices"; + private static final String HANDLE_DEVICE_RENAMING_PARAMETER = "handleDeviceRenaming"; + private final AttributesService attributesService; private final RelationService relationService; + private final DeviceService deviceService; @Lazy @Autowired private TelemetrySubscriptionService tsSubService; @@ -49,44 +69,50 @@ public class DefaultGatewayDeviceStateService implements GatewayDeviceStateServi public void update(Device device, Device oldDevice) { List relationToGatewayList = relationService.findByFromAndType(TenantId.SYS_TENANT_ID, device.getId(), DataConstants.LAST_CONNECTED_GATEWAY, RelationTypeGroup.COMMON); if (!relationToGatewayList.isEmpty()) { - EntityRelation relationToGateway = relationToGatewayList.get(0); - ListenableFuture> renamedGatewayDevicesFuture = attributesService.find(device.getTenantId(), relationToGateway.getTo(), DataConstants.SHARED_SCOPE, Collections.singletonList("renamedGatewayDevices")); - DonAsynchron.withCallback(renamedGatewayDevicesFuture, renamedGatewayDevicesList -> { - ObjectNode renamedGatewayDevicesNode; - KvEntry renamedGatewayDevicesKvEntry; - String newDeviceName = device.getName(); - String oldDeviceName = oldDevice.getName(); - - if (renamedGatewayDevicesList.isEmpty()) { - renamedGatewayDevicesNode = JacksonUtil.newObjectNode(); - renamedGatewayDevicesNode.put(oldDeviceName, newDeviceName); - } else { - AttributeKvEntry receivedRenamedGatewayDevicesAttribute = renamedGatewayDevicesList.get(0); - renamedGatewayDevicesNode = (ObjectNode) JacksonUtil.toJsonNode(receivedRenamedGatewayDevicesAttribute.getValueAsString()); - if (renamedGatewayDevicesNode.findValue(newDeviceName) != null && oldDeviceName.equals(renamedGatewayDevicesNode.get(newDeviceName).asText())) { - // If a new device name is the same like the first name or another device was renamed like some existing device - renamedGatewayDevicesNode.remove(newDeviceName); - } else { - - AtomicBoolean renamedFirstTime = new AtomicBoolean(true); - - renamedGatewayDevicesNode.fields().forEachRemaining(entry -> { - // If device was renamed earlier - if (oldDeviceName.equals(entry.getValue().asText())) { - renamedGatewayDevicesNode.put(entry.getKey(), newDeviceName); - renamedFirstTime.set(false); - } - }); - if (renamedFirstTime.get()) { + if (oldDevice != null) { + EntityRelation relationToGateway = relationToGatewayList.get(0); + + Device gatewayDevice = deviceService.findDeviceById(device.getTenantId(), (DeviceId) relationToGateway.getTo()); + if (isHandleDeviceRenamingEnabled(gatewayDevice.getAdditionalInfo())) { + ListenableFuture> renamedGatewayDevicesFuture = attributesService.find(device.getTenantId(), relationToGateway.getTo(), DataConstants.SHARED_SCOPE, Collections.singletonList("renamedGatewayDevices")); + DonAsynchron.withCallback(renamedGatewayDevicesFuture, renamedGatewayDevicesList -> { + ObjectNode renamedGatewayDevicesNode; + KvEntry renamedGatewayDevicesKvEntry; + String newDeviceName = device.getName(); + String oldDeviceName = oldDevice.getName(); + + if (renamedGatewayDevicesList.isEmpty()) { + renamedGatewayDevicesNode = JacksonUtil.newObjectNode(); renamedGatewayDevicesNode.put(oldDeviceName, newDeviceName); + } else { + AttributeKvEntry receivedRenamedGatewayDevicesAttribute = renamedGatewayDevicesList.get(0); + renamedGatewayDevicesNode = (ObjectNode) JacksonUtil.toJsonNode(receivedRenamedGatewayDevicesAttribute.getValueAsString()); + if (renamedGatewayDevicesNode.findValue(newDeviceName) != null && oldDeviceName.equals(renamedGatewayDevicesNode.get(newDeviceName).asText())) { + // If a new device name is the same like the first name or another device was renamed like some existing device + renamedGatewayDevicesNode.remove(newDeviceName); + } else { + + AtomicBoolean renamedFirstTime = new AtomicBoolean(true); + + renamedGatewayDevicesNode.fields().forEachRemaining(entry -> { + // If device was renamed earlier + if (oldDeviceName.equals(entry.getValue().asText())) { + renamedGatewayDevicesNode.put(entry.getKey(), newDeviceName); + renamedFirstTime.set(false); + } + }); + if (renamedFirstTime.get()) { + renamedGatewayDevicesNode.put(oldDeviceName, newDeviceName); + } + } } - } - } - renamedGatewayDevicesKvEntry = new JsonDataEntry(RENAMED_GATEWAY_DEVICES, JacksonUtil.toString(renamedGatewayDevicesNode)); - saveGatewayDevicesAttribute(device, relationToGateway, renamedGatewayDevicesKvEntry); - }, - e -> log.error("Cannot get gateway renamed devices attribute", e)); + renamedGatewayDevicesKvEntry = new JsonDataEntry(RENAMED_GATEWAY_DEVICES, JacksonUtil.toString(renamedGatewayDevicesNode)); + saveGatewayDevicesAttribute(device, relationToGateway, renamedGatewayDevicesKvEntry); + }, + e -> log.error("Cannot get gateway renamed devices attribute", e)); + } + } } } @@ -105,17 +131,17 @@ public class DefaultGatewayDeviceStateService implements GatewayDeviceStateServi if (renamedGatewayDevicesNode.findValue(deletedDeviceName[0]) != null) { renamedGatewayDevicesNode.remove(deletedDeviceName[0]); renamedListChanged.set(true); - } else { - Map renamedGatewayDevicesMap = JacksonUtil.OBJECT_MAPPER.convertValue(renamedGatewayDevicesNode, new TypeReference<>() {}); - renamedGatewayDevicesMap.forEach((key, value) -> { - // If device was renamed earlier - if (deletedDeviceName[0].equals(value)) { - renamedGatewayDevicesNode.remove(key); - deletedDeviceName[0] = key; - renamedListChanged.set(true); - } - }); } + Map renamedGatewayDevicesMap = JacksonUtil.OBJECT_MAPPER.convertValue(renamedGatewayDevicesNode, new TypeReference<>() { + }); + renamedGatewayDevicesMap.forEach((key, value) -> { + // If device was renamed earlier + if (deletedDeviceName[0].equals(value)) { + renamedGatewayDevicesNode.remove(key); + deletedDeviceName[0] = key; + renamedListChanged.set(true); + } + }); if (renamedListChanged.get()) { KvEntry renamedGatewayDevicesKvEntry = new JsonDataEntry(RENAMED_GATEWAY_DEVICES, JacksonUtil.toString(renamedGatewayDevicesNode)); saveGatewayDevicesAttribute(device, relationToGateway, renamedGatewayDevicesKvEntry); @@ -138,6 +164,32 @@ public class DefaultGatewayDeviceStateService implements GatewayDeviceStateServi } } + @Override + public void checkAndUpdateDeletedGatewayDevicesAttribute(Device device) { + List relationToGatewayList = relationService.findByFromAndType(TenantId.SYS_TENANT_ID, device.getId(), DataConstants.LAST_CONNECTED_GATEWAY, RelationTypeGroup.COMMON); + if (!relationToGatewayList.isEmpty()) { + EntityRelation relationToGateway = relationToGatewayList.get(0); + ListenableFuture> deletedGatewayDevicesFuture = attributesService.find(device.getTenantId(), relationToGateway.getTo(), DataConstants.SHARED_SCOPE, Collections.singletonList("deletedGatewayDevices")); + DonAsynchron.withCallback(deletedGatewayDevicesFuture, deletedGatewayDevicesList -> { + ArrayNode deletedGatewayDevicesNode; + if (!deletedGatewayDevicesList.isEmpty()) { + int deletedDeviceIndex = -1; + deletedGatewayDevicesNode = (ArrayNode) JacksonUtil.toJsonNode(deletedGatewayDevicesList.get(0).getValueAsString()); + for (int i = 0; i < deletedGatewayDevicesNode.size(); i++) { + if (deletedGatewayDevicesNode.get(i).asText().equals(device.getName())) { + deletedDeviceIndex = i; + } + } + if (deletedDeviceIndex != -1) { + deletedGatewayDevicesNode.remove(deletedDeviceIndex); + KvEntry deletedGatewayDevicesKvEntry = new JsonDataEntry(DELETED_GATEWAY_DEVICES, JacksonUtil.toString(deletedGatewayDevicesNode)); + saveGatewayDevicesAttribute(device, relationToGateway, deletedGatewayDevicesKvEntry); + } + } + }, e -> log.error("Cannot get gateway deleted devices attribute", e)); + } + } + private void saveGatewayDevicesAttribute(Device device, EntityRelation relationToGateway, KvEntry gatewayDevicesKvEntry) { AttributeKvEntry attrKvEntry = new BaseAttributeKvEntry(System.currentTimeMillis(), gatewayDevicesKvEntry); tsSubService.saveAndNotify(device.getTenantId(), relationToGateway.getTo(), DataConstants.SHARED_SCOPE, List.of(attrKvEntry), true, new FutureCallback() { @@ -152,4 +204,11 @@ public class DefaultGatewayDeviceStateService implements GatewayDeviceStateServi } }); } + + private boolean isHandleDeviceRenamingEnabled(JsonNode additionalInfo) { + if (additionalInfo.get(HANDLE_DEVICE_RENAMING_PARAMETER) != null) { + return additionalInfo.get(HANDLE_DEVICE_RENAMING_PARAMETER).asBoolean(); + } + return false; + } } \ No newline at end of file diff --git a/application/src/main/java/org/thingsboard/server/service/gateway_device/GatewayDeviceStateService.java b/application/src/main/java/org/thingsboard/server/service/gateway_device/GatewayDeviceStateService.java index f086b25d5a..d4079487a5 100644 --- a/application/src/main/java/org/thingsboard/server/service/gateway_device/GatewayDeviceStateService.java +++ b/application/src/main/java/org/thingsboard/server/service/gateway_device/GatewayDeviceStateService.java @@ -1,3 +1,18 @@ +/** + * Copyright © 2016-2021 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.service.gateway_device; import org.thingsboard.server.common.data.Device; @@ -7,4 +22,6 @@ public interface GatewayDeviceStateService { void update(Device device, Device oldDevice); void delete(Device device); + + void checkAndUpdateDeletedGatewayDevicesAttribute(Device device); } diff --git a/application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java b/application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java index cb4e01032f..929d87e5f4 100644 --- a/application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java +++ b/application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java @@ -95,6 +95,7 @@ import org.thingsboard.server.queue.common.TbProtoQueueMsg; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.apiusage.TbApiUsageStateService; import org.thingsboard.server.service.executors.DbCallbackExecutorService; +import org.thingsboard.server.service.gateway_device.GatewayDeviceStateService; import org.thingsboard.server.service.profile.TbDeviceProfileCache; import org.thingsboard.server.cluster.TbClusterService; import org.thingsboard.server.service.resource.TbResourceService; @@ -137,6 +138,7 @@ public class DefaultTransportApiService implements TransportApiService { private final TbResourceService resourceService; private final OtaPackageService otaPackageService; private final OtaPackageDataCache otaPackageDataCache; + private final GatewayDeviceStateService gatewayDeviceStateService; private final ConcurrentMap deviceCreationLocks = new ConcurrentHashMap<>(); @@ -317,6 +319,8 @@ public class DefaultTransportApiService implements TransportApiService { } relationService.saveRelationAsync(TenantId.SYS_TENANT_ID, lastConnectedGatewayRelation); + gatewayDeviceStateService.checkAndUpdateDeletedGatewayDevicesAttribute(device); + GetOrCreateDeviceFromGatewayResponseMsg.Builder builder = GetOrCreateDeviceFromGatewayResponseMsg.newBuilder() .setDeviceInfo(getDeviceInfoProto(device)); DeviceProfile deviceProfile = deviceProfileCache.get(device.getTenantId(), device.getDeviceProfileId()); diff --git a/ui-ngx/src/app/modules/home/components/wizard/device-wizard-dialog.component.html b/ui-ngx/src/app/modules/home/components/wizard/device-wizard-dialog.component.html index ae0be158d5..cac8ae4c1e 100644 --- a/ui-ngx/src/app/modules/home/components/wizard/device-wizard-dialog.component.html +++ b/ui-ngx/src/app/modules/home/components/wizard/device-wizard-dialog.component.html @@ -103,10 +103,16 @@ {{ 'device.is-gateway' | translate }} +
+
{{ 'device.overwrite-activity-time' | translate }} + + {{ 'device.handle-device-renaming' | translate }} +
device.description diff --git a/ui-ngx/src/app/modules/home/components/wizard/device-wizard-dialog.component.ts b/ui-ngx/src/app/modules/home/components/wizard/device-wizard-dialog.component.ts index fdbc25107f..552e1d5eb8 100644 --- a/ui-ngx/src/app/modules/home/components/wizard/device-wizard-dialog.component.ts +++ b/ui-ngx/src/app/modules/home/components/wizard/device-wizard-dialog.component.ts @@ -109,6 +109,7 @@ export class DeviceWizardDialogComponent extends label: ['', Validators.maxLength(255)], gateway: [false], overwriteActivityTime: [false], + handleDeviceRenaming: [false], addProfileType: [0], deviceProfileId: [null, Validators.required], newDeviceProfileTitle: [{value: null, disabled: true}], @@ -326,6 +327,7 @@ export class DeviceWizardDialogComponent extends additionalInfo: { gateway: this.deviceWizardFormGroup.get('gateway').value, overwriteActivityTime: this.deviceWizardFormGroup.get('overwriteActivityTime').value, + handleDeviceRenaming: this.deviceWizardFormGroup.get('handleDeviceRenaming').value, description: this.deviceWizardFormGroup.get('description').value }, customerId: null diff --git a/ui-ngx/src/app/modules/home/pages/device/device.component.html b/ui-ngx/src/app/modules/home/pages/device/device.component.html index 1187a34f5d..1f89481fd0 100644 --- a/ui-ngx/src/app/modules/home/pages/device/device.component.html +++ b/ui-ngx/src/app/modules/home/pages/device/device.component.html @@ -128,10 +128,16 @@ {{ 'device.is-gateway' | translate }} +
+
{{ 'device.overwrite-activity-time' | translate }} + + {{ 'device.handle-device-renaming' | translate }} +
device.description diff --git a/ui-ngx/src/app/modules/home/pages/device/device.component.ts b/ui-ngx/src/app/modules/home/pages/device/device.component.ts index 7cd60a89f4..cd1c78b3dd 100644 --- a/ui-ngx/src/app/modules/home/pages/device/device.component.ts +++ b/ui-ngx/src/app/modules/home/pages/device/device.component.ts @@ -92,6 +92,7 @@ export class DeviceComponent extends EntityComponent { { gateway: [entity && entity.additionalInfo ? entity.additionalInfo.gateway : false], overwriteActivityTime: [entity && entity.additionalInfo ? entity.additionalInfo.overwriteActivityTime : false], + handleDeviceRenaming: [entity && entity.additionalInfo ? entity.additionalInfo.handleDeviceRenaming : false], description: [entity && entity.additionalInfo ? entity.additionalInfo.description : ''], } ) @@ -121,6 +122,7 @@ export class DeviceComponent extends EntityComponent { additionalInfo: { gateway: entity.additionalInfo ? entity.additionalInfo.gateway : false, overwriteActivityTime: entity.additionalInfo ? entity.additionalInfo.overwriteActivityTime : false, + handleDeviceRenaming: entity.additionalInfo ? entity.additionalInfo.handleDeviceRenaming : false, description: entity.additionalInfo ? entity.additionalInfo.description : '' } }); diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index 3982bfedda..bfa105092f 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -1042,6 +1042,7 @@ "unable-delete-device-alias-text": "Device alias '{{deviceAlias}}' can't be deleted as it used by the following widget(s):
{{widgetsList}}", "is-gateway": "Is gateway", "overwrite-activity-time": "Overwrite activity time for connected device", + "handle-device-renaming": "Handle device renaming", "public": "Public", "device-public": "Device is public", "select-device": "Select device", From 9c44530ab92d8490debf5cc856745b8539f2d656 Mon Sep 17 00:00:00 2001 From: dlandiak Date: Tue, 21 Dec 2021 15:19:30 +0200 Subject: [PATCH 024/798] added boolean param to the mqtt node to allow adding a suffix for client id param --- .../main/java/org/thingsboard/rule/engine/mqtt/TbMqttNode.java | 3 ++- .../thingsboard/rule/engine/mqtt/TbMqttNodeConfiguration.java | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mqtt/TbMqttNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mqtt/TbMqttNode.java index 4f4dc7ab60..ac5fa9e7ea 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mqtt/TbMqttNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mqtt/TbMqttNode.java @@ -104,7 +104,8 @@ public class TbMqttNode implements TbNode { protected MqttClient initClient(TbContext ctx) throws Exception { MqttClientConfig config = new MqttClientConfig(getSslContext()); if (!StringUtils.isEmpty(this.mqttNodeConfiguration.getClientId())) { - config.setClientId(this.mqttNodeConfiguration.getClientId()); + config.setClientId(this.mqttNodeConfiguration.isAppendClientIdSuffix() ? + this.mqttNodeConfiguration.getClientId() + "_" + ctx.getServiceId() : this.mqttNodeConfiguration.getClientId()); } config.setCleanSession(this.mqttNodeConfiguration.isCleanSession()); diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mqtt/TbMqttNodeConfiguration.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mqtt/TbMqttNodeConfiguration.java index 9ac797a98a..46c81f3ddd 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mqtt/TbMqttNodeConfiguration.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mqtt/TbMqttNodeConfiguration.java @@ -28,6 +28,7 @@ public class TbMqttNodeConfiguration implements NodeConfiguration Date: Wed, 22 Dec 2021 18:12:17 +0200 Subject: [PATCH 025/798] UI: Update and refactoring --- .../server/controller/RpcV2Controller.java | 2 +- ui-ngx/src/app/core/api/widget-api.models.ts | 5 +- .../src/app/core/api/widget-subscription.ts | 41 ------ ui-ngx/src/app/core/http/device.service.ts | 10 +- ui-ngx/src/app/modules/common/modules-map.ts | 2 + .../rpc/persistent-add-dialog.component.html | 2 +- .../rpc/persistent-add-dialog.component.scss | 5 +- .../rpc/persistent-add-dialog.component.ts | 32 ++--- .../persistent-details-dialog.component.html | 2 +- .../persistent-details-dialog.component.scss | 5 +- .../persistent-details-dialog.component.ts | 61 +++------ .../persistent-filter-panel.component.html | 2 +- .../persistent-filter-panel.component.scss | 1 + .../rpc/persistent-filter-panel.component.ts | 16 +-- .../lib/rpc/persistent-table.component.html | 2 +- .../lib/rpc/persistent-table.component.scss | 1 + .../lib/rpc/persistent-table.component.ts | 129 ++++++++++++++---- .../json-object-view.component.html | 2 +- .../components/json-object-view.component.ts | 2 +- ui-ngx/src/app/shared/models/rpc.models.ts | 1 - ui-ngx/src/app/shared/shared.module.ts | 2 +- .../assets/locale/locale.constant-uk_UA.json | 2 +- 22 files changed, 171 insertions(+), 156 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/controller/RpcV2Controller.java b/application/src/main/java/org/thingsboard/server/controller/RpcV2Controller.java index 3f85841b3c..81e9a9e394 100644 --- a/application/src/main/java/org/thingsboard/server/controller/RpcV2Controller.java +++ b/application/src/main/java/org/thingsboard/server/controller/RpcV2Controller.java @@ -188,7 +188,7 @@ public class RpcV2Controller extends AbstractRpcController { @RequestParam(required = false) String sortOrder) throws ThingsboardException { checkParameter("DeviceId", strDeviceId); try { - if (rpcStatus.equals(RpcStatus.DELETED)) { + if (rpcStatus != null && rpcStatus.equals(RpcStatus.DELETED)) { throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "RpcStatus: DELETED"); } diff --git a/ui-ngx/src/app/core/api/widget-api.models.ts b/ui-ngx/src/app/core/api/widget-api.models.ts index c0bab56e4e..7559692561 100644 --- a/ui-ngx/src/app/core/api/widget-api.models.ts +++ b/ui-ngx/src/app/core/api/widget-api.models.ts @@ -55,8 +55,7 @@ import { TranslateService } from '@ngx-translate/core'; import { AlarmDataService } from '@core/api/alarm-data.service'; import { IDashboardController } from '@home/components/dashboard-page/dashboard-page.models'; import { PopoverPlacement } from '@shared/components/popover.models'; -import { PageLink } from '@shared/models/page/page-link'; -import { PersistentRpc, RpcStatus } from '@shared/models/rpc.models'; +import { PersistentRpc } from '@shared/models/rpc.models'; export interface TimewindowFunctions { onUpdateTimewindow: (startTimeMs: number, endTimeMs: number, interval?: number) => void; @@ -322,8 +321,6 @@ export interface IWidgetSubscription { persistentPollingInterval?: number, retries?: number, additionalInfo?: any, requestUUID?: string): Observable; clearRpcError(): void; - subscribeForPersistentRequests(pageLink: PageLink, keyFileter: RpcStatus): Observable; - subscribe(): void; subscribeAllForPaginatedData(pageLink: EntityDataPageLink, diff --git a/ui-ngx/src/app/core/api/widget-subscription.ts b/ui-ngx/src/app/core/api/widget-subscription.ts index 1634bb7b87..0f864335aa 100644 --- a/ui-ngx/src/app/core/api/widget-subscription.ts +++ b/ui-ngx/src/app/core/api/widget-subscription.ts @@ -70,7 +70,6 @@ import { import { distinct, filter, map, switchMap, takeUntil } from 'rxjs/operators'; import { AlarmDataListener } from '@core/api/alarm-data.service'; import { RpcStatus } from '@shared/models/rpc.models'; -import { PageLink } from '@shared/models/page/page-link'; const moment = moment_; @@ -781,46 +780,6 @@ export class WidgetSubscription implements IWidgetSubscription { } } - subscribeForPersistentRequests(pageLink: PageLink, keyFilter: RpcStatus): Observable { - if (!this.rpcEnabled) { - return throwError(new Error('Rpc disabled!')); - } else if (!this.targetDeviceId) { - return throwError(new Error('Target device is not set!')); - } - const rpcSubject: Subject = new Subject(); - - this.ctx.deviceService.getPersistedRpcRequests(this.targetDeviceId, pageLink, keyFilter).subscribe( - (responseBody) => { - rpcSubject.next(responseBody); - rpcSubject.complete(); - }, - (rejection: HttpErrorResponse) => { - const index = this.executingSubjects.indexOf(rpcSubject); - if (index >= 0) { - this.executingSubjects.splice( index, 1 ); - } - this.executingRpcRequest = this.executingSubjects.length > 0; - this.callbacks.rpcStateChanged(this); - if (!this.executingRpcRequest || rejection.status === 504) { - this.rpcRejection = rejection; - if (rejection.status === 504) { - this.rpcErrorText = 'Request Timeout.'; - } else { - this.rpcErrorText = 'Error : ' + rejection.status + ' - ' + rejection.statusText; - const error = this.extractRejectionErrorText(rejection); - if (error) { - this.rpcErrorText += '
'; - this.rpcErrorText += error; - } - } - this.callbacks.onRpcFailed(this); - } - rpcSubject.error(rejection); - } - ); - return rpcSubject.asObservable(); - } - private extractRejectionErrorText(rejection: HttpErrorResponse) { let error = null; if (rejection.error) { diff --git a/ui-ngx/src/app/core/http/device.service.ts b/ui-ngx/src/app/core/http/device.service.ts index 9b5119d317..88f0cacb46 100644 --- a/ui-ngx/src/app/core/http/device.service.ts +++ b/ui-ngx/src/app/core/http/device.service.ts @@ -148,10 +148,12 @@ export class DeviceService { } public getPersistedRpcRequests(deviceId: string, pageLink: PageLink, - keyFilter: RpcStatus, config?: RequestConfig): Observable> { - const rpcStatus = keyFilter ? '&rpcStatus=' + keyFilter : ''; - return this.http.get>(`/api/rpc/persistent/device/${deviceId}${pageLink.toQuery()}${rpcStatus}`, - defaultHttpOptionsFromConfig(config)); + rpcStatus?: RpcStatus, config?: RequestConfig): Observable> { + let url = `/api/rpc/persistent/device/${deviceId}${pageLink.toQuery()}`; + if (rpcStatus && rpcStatus.length) { + url += `&rpcStatus=${rpcStatus}`; + } + return this.http.get>(url, defaultHttpOptionsFromConfig(config)); } public findByQuery(query: DeviceSearchQuery, diff --git a/ui-ngx/src/app/modules/common/modules-map.ts b/ui-ngx/src/app/modules/common/modules-map.ts index 3cd4139ec4..147492ec29 100644 --- a/ui-ngx/src/app/modules/common/modules-map.ts +++ b/ui-ngx/src/app/modules/common/modules-map.ts @@ -139,6 +139,7 @@ import * as QueueTypeListComponent from '@shared/components/queue/queue-type-lis import * as RelationTypeAutocompleteComponent from '@shared/components/relation/relation-type-autocomplete.component'; import * as SocialSharePanelComponent from '@shared/components/socialshare-panel.component'; import * as JsonObjectEditComponent from '@shared/components/json-object-edit.component'; +import * as JsonObjectViewComponent from '@shared/components/json-object-view.component'; import * as JsonContentComponent from '@shared/components/json-content.component'; import * as JsFuncComponent from '@shared/components/js-func.component'; import * as FabToolbarComponent from '@shared/components/fab-toolbar.component'; @@ -420,6 +421,7 @@ class ModulesMap implements IModulesMap { '@shared/components/relation/relation-type-autocomplete.component': RelationTypeAutocompleteComponent, '@shared/components/socialshare-panel.component': SocialSharePanelComponent, '@shared/components/json-object-edit.component': JsonObjectEditComponent, + '@shared/components/json-object-view.component': JsonObjectViewComponent, '@shared/components/json-content.component': JsonContentComponent, '@shared/components/js-func.component': JsFuncComponent, '@shared/components/fab-toolbar.component': FabToolbarComponent, diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-add-dialog.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-add-dialog.component.html index 7e0ae3056f..1e74657946 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-add-dialog.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-add-dialog.component.html @@ -32,7 +32,7 @@
- {{ 'widgets.persistent-table.message-types.' + persistentFormGroup.get('oneWayElseTwoWay').value | translate }} + {{ rpcMessageTypeText }}
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-add-dialog.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-add-dialog.component.scss index e450fa4a09..3c9d0b43a2 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-add-dialog.component.scss +++ b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-add-dialog.component.scss @@ -13,8 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -:host { - .add-dialog ::ng-deep { + +:host ::ng-deep { + .add-dialog { .params-json-editor, .additional-json-editor { diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-add-dialog.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-add-dialog.component.ts index c4e87c5e6b..8c4f47a904 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-add-dialog.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-add-dialog.component.ts @@ -22,6 +22,7 @@ import { Router } from '@angular/router'; import { MatDialogRef } from '@angular/material/dialog'; import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { RequestData } from '@shared/models/rpc.models'; +import { TranslateService } from '@ngx-translate/core'; @Component({ selector: 'tb-persistent-add-dialog', @@ -32,15 +33,15 @@ import { RequestData } from '@shared/models/rpc.models'; export class PersistentAddDialogComponent extends DialogComponent implements OnInit { public persistentFormGroup: FormGroup; + public rpcMessageTypeText: string; - private requestData: RequestData = { - persistentUpdated: false - }; + private requestData: RequestData = null; constructor(protected store: Store, protected router: Router, public dialogRef: MatDialogRef, - private fb: FormBuilder) { + private fb: FormBuilder, + private translate: TranslateService) { super(store, router, dialogRef); this.persistentFormGroup = this.fb.group( @@ -48,27 +49,24 @@ export class PersistentAddDialogComponent extends DialogComponent { + this.rpcMessageTypeText = this.translate.instant(`widgets.persistent-table.message-types.${this.persistentFormGroup.get('oneWayElseTwoWay').value}`); + } + ); } close(): void { diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-details-dialog.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-details-dialog.component.html index 3a9451db67..440b142db2 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-details-dialog.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-details-dialog.component.html @@ -48,7 +48,7 @@ widgets.persistent-table.status + [ngStyle]="{ fontWeight: 'bold', color: rpcStatusColorsMap.get(data.persistentRequest.status) }"> widgets.persistent-table.method diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-details-dialog.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-details-dialog.component.scss index ec456d5d9e..2a6a06c2a8 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-details-dialog.component.scss +++ b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-details-dialog.component.scss @@ -13,8 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -:host { - .rpc-dialog ::ng-deep { +:host ::ng-deep { + .rpc-dialog { .mat-expansion-panel-body { padding-bottom: 0 !important; } @@ -23,6 +23,7 @@ margin: 0 0 16px 0; } } + .tb-audit-log-response-data { width: 100%; min-width: 400px; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-details-dialog.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-details-dialog.component.ts index 552175b905..8e7e755e86 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-details-dialog.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-details-dialog.component.ts @@ -24,13 +24,7 @@ import { TranslateService } from '@ngx-translate/core'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { FormBuilder, FormGroup } from '@angular/forms'; import { DeviceService } from '@core/http/device.service'; -import { - PersistentRpc, - rpcStatusColors, - RpcStatus, - rpcStatusTranslation -} from '@shared/models/rpc.models'; -import { isDefinedAndNotNull } from '@core/utils'; +import { PersistentRpc, RpcStatus, rpcStatusColors, rpcStatusTranslation } from '@shared/models/rpc.models'; import { NULL_UUID } from '@shared/models/id/has-uuid'; import { DialogService } from '@core/services/dialog.service'; @@ -82,7 +76,7 @@ export class PersistentDetailsDialogComponent extends DialogComponent { if (res) { - if (res) { - this.deviceService.deletePersistedRpc(persistentRpc.id.id).subscribe(() => { - this.persistentUpdated = true; - this.close(); - }); - } + this.deviceService.deletePersistedRpc(persistentRpc.id.id).subscribe(() => { + this.persistentUpdated = true; + this.close(); + }); } }); } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-filter-panel.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-filter-panel.component.html index ffbcab5749..d5eca15a5f 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-filter-panel.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-filter-panel.component.html @@ -19,7 +19,7 @@ widgets.persistent-table.rpc-status-list + placeholder="{{ rpcSearchPlaceholder }}"> {{ 'widgets.persistent-table.rpc-search-status-all' | translate }} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-filter-panel.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-filter-panel.component.scss index 63f832d78c..2ad57a3f7f 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-filter-panel.component.scss +++ b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-filter-panel.component.scss @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + :host { width: 100%; height: 100%; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-filter-panel.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-filter-panel.component.ts index d60c80ac43..c8a72ffc3b 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-filter-panel.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-filter-panel.component.ts @@ -18,6 +18,7 @@ import { Component, Inject, InjectionToken } from '@angular/core'; import { FormBuilder, FormGroup } from '@angular/forms'; import { OverlayRef } from '@angular/cdk/overlay'; import { RpcStatus, rpcStatusTranslation } from '@shared/models/rpc.models'; +import { TranslateService } from '@ngx-translate/core'; export const PERSISTENT_FILTER_PANEL_DATA = new InjectionToken('AlarmFilterPanelData'); @@ -35,26 +36,21 @@ export class PersistentFilterPanelComponent { public persistentFilterFormGroup: FormGroup; public result: PersistentFilterPanelData; public rpcSearchStatusTranslationMap = rpcStatusTranslation; + public rpcSearchPlaceholder: string; - public persistentSearchStatuses = [ - RpcStatus.QUEUED, - RpcStatus.SENT, - RpcStatus.DELIVERED, - RpcStatus.SUCCESSFUL, - RpcStatus.TIMEOUT, - RpcStatus.EXPIRED, - RpcStatus.FAILED - ]; + public persistentSearchStatuses = Object.keys(RpcStatus); constructor(@Inject(PERSISTENT_FILTER_PANEL_DATA) public data: PersistentFilterPanelData, public overlayRef: OverlayRef, - private fb: FormBuilder) { + private fb: FormBuilder, + private translate: TranslateService) { this.persistentFilterFormGroup = this.fb.group( { rpcStatus: this.data.rpcStatus } ); + this.rpcSearchPlaceholder = this.translate.instant('widgets.persistent-table.any-status'); } update() { diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-table.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-table.component.html index fd86fd92cc..e786efec7b 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-table.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-table.component.html @@ -15,7 +15,7 @@ limitations under the License. --> -
+
{ - const showHidePageSize = this.persistentWidgetContainerRef.nativeElement.offsetWidth < hidePageSizePixelValue; + const showHidePageSize = this.elementRef.nativeElement.offsetWidth < hidePageSizePixelValue; if (showHidePageSize !== this.hidePageSize) { this.hidePageSize = showHidePageSize; - this.ctx.detectChanges(); + this.cd.markForCheck(); } }); - this.widgetResize$.observe(this.persistentWidgetContainerRef.nativeElement); + this.widgetResize$.observe(this.elementRef.nativeElement); } } @@ -250,7 +257,7 @@ export class PersistentTableComponent extends PageComponent implements OnInit { this.displayedColumns.push('actions'); } - this.persistentDatasource = new PersistentDatasource(this.translate, this.subscription); + this.persistentDatasource = new PersistentDatasource(this.translate, this.subscription, this.ctx); const cssString = constructTableCssString(this.widgetConfig); const cssParser = new cssjs(); @@ -300,11 +307,9 @@ export class PersistentTableComponent extends PageComponent implements OnInit { this.translate.instant('action.yes') ).subscribe((res) => { if (res) { - if (res) { - this.deviceService.deletePersistedRpc(persistentRpc.id.id).subscribe(() => { - this.reloadPersistentRequests(); - }); - } + this.deviceService.deletePersistedRpc(persistentRpc.id.id).subscribe(() => { + this.reloadPersistentRequests(); + }); } }); } @@ -345,7 +350,7 @@ export class PersistentTableComponent extends PageComponent implements OnInit { panelClass: ['tb-dialog', 'tb-fullscreen-dialog'] }).afterClosed().subscribe( (requestData) => { - if (requestData.persistentUpdated) { + if (requestData) { this.sendRequests(requestData); } } @@ -440,11 +445,16 @@ class PersistentDatasource implements DataSource { private persistentSubject = new BehaviorSubject([]); private pageDataSubject = new BehaviorSubject>(emptyPageData()); + private rpcErrorText: string; + private executingSubjects: Array>; + private executingRpcRequest = false; + public dataLoading = true; public pageData$ = this.pageDataSubject.asObservable(); constructor(private translate: TranslateService, - private subscription: IWidgetSubscription) { + private subscription: IWidgetSubscription, + private ctx: WidgetContext) { } connect(collectionViewer: CollectionViewer): Observable> { @@ -462,11 +472,11 @@ class PersistentDatasource implements DataSource { this.pageDataSubject.next(pageData); } - loadPersistent(pageLink: PageLink, keyFilter: RpcStatus) { + loadPersistent(pageLink: PageLink, rpcStatusFilter: RpcStatus) { this.dataLoading = true; const result = new ReplaySubject>(); - this.fetchEntities(pageLink, keyFilter).pipe( + this.fetchEntities(pageLink, rpcStatusFilter).pipe( catchError(() => of(emptyPageData())), ).subscribe( (pageData) => { @@ -479,8 +489,81 @@ class PersistentDatasource implements DataSource { return result; } - fetchEntities(pageLink: PageLink, keyFilter: RpcStatus): Observable> { - return this.subscription.subscribeForPersistentRequests(pageLink, keyFilter); + fetchEntities(pageLink: PageLink, rpcStatusFilter: RpcStatus): Observable> { + if (!this.subscription.rpcEnabled) { + return throwError(new Error('Rpc disabled!')); + } else if (!this.subscription.targetDeviceId) { + return throwError(new Error('Target device is not set!')); + } + const rpcSubject: Subject = new Subject(); + + this.ctx.deviceService.getPersistedRpcRequests(this.subscription.targetDeviceId, pageLink, rpcStatusFilter).subscribe( + (responseBody) => { + rpcSubject.next(responseBody); + rpcSubject.complete(); + }, + (rejection: HttpErrorResponse) => { + this.rpcErrorText = null; + this.executingSubjects = []; + + const index = this.executingSubjects.indexOf(rpcSubject); + if (index >= 0) { + this.executingSubjects.splice(index, 1); + } + this.executingRpcRequest = this.executingSubjects.length > 0; + this.subscription.options.callbacks.rpcStateChanged(this.subscription); + if (!this.executingRpcRequest || rejection.status === 504) { + this.subscription.rpcRejection = rejection; + if (rejection.status === 504) { + this.subscription.rpcErrorText = 'Request Timeout.'; + } else { + this.subscription.rpcErrorText = 'Error : ' + rejection.status + ' - ' + rejection.statusText; + const error = this.extractRejectionErrorText(rejection); + if (error) { + this.subscription.rpcErrorText += '
'; + this.subscription.rpcErrorText += error.message || ''; + } + } + this.subscription.callbacks.onRpcFailed(this.subscription); + } + rpcSubject.error(rejection); + } + ); + return rpcSubject.asObservable(); + } + + extractRejectionErrorText(rejection: HttpErrorResponse) { + let error = null; + if (rejection.error) { + error = rejection.error; + try { + error = rejection.error ? JSON.parse(rejection.error) : null; + } catch (e) {} + } + if (error && !error.message) { + error = this.prepareMessageFromData(error); + } else if (error && error.message) { + error = error.message; + } + return error; + } + + prepareMessageFromData(data) { + if (typeof data === 'object' && data.constructor === ArrayBuffer) { + const msg = String.fromCharCode.apply(null, new Uint8Array(data)); + try { + const msgObj = JSON.parse(msg); + if (msgObj.message) { + return msgObj.message; + } else { + return msg; + } + } catch (e) { + return msg; + } + } else { + return data; + } } isEmpty(): Observable { diff --git a/ui-ngx/src/app/shared/components/json-object-view.component.html b/ui-ngx/src/app/shared/components/json-object-view.component.html index 379c557b44..b3fbcef2de 100644 --- a/ui-ngx/src/app/shared/components/json-object-view.component.html +++ b/ui-ngx/src/app/shared/components/json-object-view.component.html @@ -18,5 +18,5 @@
-
+
diff --git a/ui-ngx/src/app/shared/components/json-object-view.component.ts b/ui-ngx/src/app/shared/components/json-object-view.component.ts index 5ce00b0189..b85988c3d3 100644 --- a/ui-ngx/src/app/shared/components/json-object-view.component.ts +++ b/ui-ngx/src/app/shared/components/json-object-view.component.ts @@ -153,7 +153,7 @@ export class JsonObjectViewComponent implements OnInit { }, 2); } } catch (e) { - // + console.error(e); } if (this.jsonViewer) { this.jsonViewer.setValue(this.contentValue ? this.contentValue : '', -1); diff --git a/ui-ngx/src/app/shared/models/rpc.models.ts b/ui-ngx/src/app/shared/models/rpc.models.ts index 61a94a401e..4512b590a0 100644 --- a/ui-ngx/src/app/shared/models/rpc.models.ts +++ b/ui-ngx/src/app/shared/models/rpc.models.ts @@ -79,7 +79,6 @@ export interface PersistentRpcData extends PersistentRpc { } export interface RequestData { - persistentUpdated: boolean; method?: string; oneWayElseTwoWay?: boolean; persistentPollingInterval?: number; diff --git a/ui-ngx/src/app/shared/shared.module.ts b/ui-ngx/src/app/shared/shared.module.ts index 71a4d344bf..bb16964806 100644 --- a/ui-ngx/src/app/shared/shared.module.ts +++ b/ui-ngx/src/app/shared/shared.module.ts @@ -150,7 +150,7 @@ import { TogglePasswordComponent } from '@shared/components/button/toggle-passwo import { HelpPopupComponent } from '@shared/components/help-popup.component'; import { TbPopoverComponent, TbPopoverDirective } from '@shared/components/popover.component'; import { TbStringTemplateOutletDirective } from '@shared/components/directives/sring-template-outlet.directive'; -import { TbComponentOutletDirective} from '@shared/components/directives/component-outlet.directive'; +import { TbComponentOutletDirective } from '@shared/components/directives/component-outlet.directive'; import { HelpMarkdownComponent } from '@shared/components/help-markdown.component'; import { MarkedOptionsService } from '@shared/components/marked-options.service'; import { TbPopoverService } from '@shared/components/popover.service'; diff --git a/ui-ngx/src/assets/locale/locale.constant-uk_UA.json b/ui-ngx/src/assets/locale/locale.constant-uk_UA.json index 3942a5193c..943598baef 100644 --- a/ui-ngx/src/assets/locale/locale.constant-uk_UA.json +++ b/ui-ngx/src/assets/locale/locale.constant-uk_UA.json @@ -2395,7 +2395,7 @@ }, "rpc-search-status-all": "ВСІ", "message-types": { - "false": "Двусторонній", + "false": "Двосторонній", "true": "Односторонній" } } From c20f6e4021a29bc0f3f6a38ac19392cbbdf21258 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Thu, 23 Dec 2021 11:20:22 +0200 Subject: [PATCH 026/798] UI: Add in map widgets new group setting - Editor setting --- ui-ngx/package.json | 2 +- .../components/widget/lib/maps/leaflet-map.ts | 52 +++++---- .../components/widget/lib/maps/map-models.ts | 19 +++- .../components/widget/lib/maps/map-widget2.ts | 3 + .../components/widget/lib/maps/markers.ts | 29 ++--- .../components/widget/lib/maps/polygon.ts | 7 +- .../components/widget/lib/maps/schemes.ts | 58 ++++++++++ ui-ngx/yarn.lock | 107 ++++++++---------- 8 files changed, 178 insertions(+), 99 deletions(-) diff --git a/ui-ngx/package.json b/ui-ngx/package.json index adec5e815a..1153524a26 100644 --- a/ui-ngx/package.json +++ b/ui-ngx/package.json @@ -28,7 +28,7 @@ "@date-io/date-fns": "^2.11.0", "@flowjs/flow.js": "^2.14.1", "@flowjs/ngx-flow": "~0.4.6", - "@geoman-io/leaflet-geoman-free": "^2.11.3", + "@geoman-io/leaflet-geoman-free": "^2.11.4", "@juggle/resize-observer": "^3.3.1", "@mat-datetimepicker/core": "~7.0.1", "@material-ui/core": "^4.12.3", diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/leaflet-map.ts b/ui-ngx/src/app/modules/home/components/widget/lib/maps/leaflet-map.ts index 78f459990e..9d661ae8d2 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/maps/leaflet-map.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/maps/leaflet-map.ts @@ -24,7 +24,9 @@ import { defaultSettings, FormattedData, MapSettings, - MarkerSettings, MarkerIconInfo, MarkerImageInfo, + MarkerIconInfo, + MarkerImageInfo, + MarkerSettings, PolygonSettings, PolylineSettings, ReplaceInfo, @@ -182,7 +184,7 @@ export default abstract class LeafletMap { addEditControl() { // Customize edit marker - if (this.options.draggableMarker) { + if (this.options.draggableMarker && !this.options.hideDrawControlButton) { const actions = [{ text: L.PM.Utils.getTranslation('actions.cancel'), onClick: () => this.toggleDrawMode('tbMarker') @@ -197,7 +199,7 @@ export default abstract class LeafletMap { } // Customize edit polygon - if (this.editPolygons) { + if (this.editPolygons && !this.options.hideDrawControlButton) { const rectangleActions = [ { text: L.PM.Utils.getTranslation('actions.cancel'), @@ -231,18 +233,27 @@ export default abstract class LeafletMap { const translateService = this.ctx.$injector.get(TranslateService); this.map.pm.setLang('en', translateService.instant('widgets.maps'), 'en'); - this.map.pm.addControls({ - position: 'topleft', - drawMarker: false, - drawCircle: false, - drawCircleMarker: false, - drawRectangle: false, - drawPolyline: false, - drawPolygon: false, - editMode: this.editPolygons, - cutPolygon: this.editPolygons, - rotateMode: this.editPolygons - }); + if (!this.options.hideAllControlButton) { + this.map.pm.addControls({ + position: 'topleft', + drawControls: !this.options.hideDrawControlButton, + drawMarker: false, + drawCircle: false, + drawCircleMarker: false, + drawRectangle: false, + drawPolyline: false, + drawPolygon: false, + dragMode: !this.options.hideEditControlButton, + editMode: this.editPolygons && !this.options.hideEditControlButton, + cutPolygon: this.editPolygons && !this.options.hideEditControlButton, + removalMode: !this.options.hideRemoveControlButton, + rotateMode: this.editPolygons && !this.options.hideEditControlButton + }); + } + + if (this.options.initDragMode) { + this.map.pm.enableGlobalDragMode(); + } this.map.on('pm:create', (e) => { if (e.shape === 'tbMarker') { @@ -344,6 +355,7 @@ export default abstract class LeafletMap { } if (this.options.draggableMarker || this.editPolygons) { map.pm.setGlobalOptions({ snappable: false } as L.PM.GlobalOptions); + map.pm.applyGlobalOptions(); this.addEditControl(); } else { this.map.pm.disableDraw(); @@ -555,7 +567,7 @@ export default abstract class LeafletMap { updatedMarkers.push(m); } } else { - m = this.createMarker(data.entityName, data, markersData, this.options as MarkerSettings, updateBounds, callback); + m = this.createMarker(data.entityName, data, markersData, this.options, updateBounds, callback); if (m) { createdMarkers.push(m); } @@ -569,7 +581,7 @@ export default abstract class LeafletMap { } }); this.markersData = markersData; - if ((this.options as MarkerSettings).useClusterMarkers) { + if (this.options.useClusterMarkers) { if (createdMarkers.length) { this.markersCluster.addLayers(createdMarkers.map(marker => marker.leafletMarker)); } @@ -589,7 +601,7 @@ export default abstract class LeafletMap { this.saveLocation(data, this.convertToCustomFormat(e.target._latlng)).subscribe(); } - private createMarker(key: string, data: FormattedData, dataSources: FormattedData[], settings: MarkerSettings, + private createMarker(key: string, data: FormattedData, dataSources: FormattedData[], settings: UnitedMapSettings, updateBounds = true, callback?): Marker { const newMarker = new Marker(this, this.convertPosition(data), settings, data, dataSources, this.dragMarker); if (callback) { @@ -597,7 +609,7 @@ export default abstract class LeafletMap { callback(data, true); }); } - if (this.bounds && updateBounds && !(this.options as MarkerSettings).useClusterMarkers) { + if (this.bounds && updateBounds && !this.options.useClusterMarkers) { this.fitBounds(this.bounds.extend(newMarker.leafletMarker.getLatLng())); } this.markers.set(key, newMarker); @@ -786,7 +798,7 @@ export default abstract class LeafletMap { this.saveLocation(data, this.convertPolygonToCustomFormat(coordinates)).subscribe(() => {}); } - createPolygon(polyData: FormattedData, dataSources: FormattedData[], settings: PolygonSettings, updateBounds = true) { + createPolygon(polyData: FormattedData, dataSources: FormattedData[], settings: UnitedMapSettings, updateBounds = true) { const polygon = new Polygon(this.map, polyData, dataSources, settings, this.dragPolygonVertex); if (updateBounds) { const bounds = polygon.leafletPoly.getBounds(); diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/map-models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/maps/map-models.ts index e65479f588..de94bdc156 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/maps/map-models.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/maps/map-models.ts @@ -192,6 +192,15 @@ export type PolylineSettings = { strokeWeightFunction: GenericFunction; }; +export interface EditorSettings { + snappable: boolean; + initDragMode: boolean; + hideAllControlButton: boolean; + hideDrawControlButton: boolean; + hideEditControlButton: boolean; + hideRemoveControlButton: boolean; +} + export interface HistorySelectSettings { buttonColor: string; } @@ -231,7 +240,7 @@ export interface TripAnimationSettings extends PolygonSettings { export type actionsHandler = ($event: Event, datasource: Datasource) => void; -export type UnitedMapSettings = MapSettings & PolygonSettings & MarkerSettings & PolylineSettings & TripAnimationSettings; +export type UnitedMapSettings = MapSettings & PolygonSettings & MarkerSettings & PolylineSettings & TripAnimationSettings & EditorSettings; export const defaultSettings: any = { xPosKeyName: 'xPos', @@ -271,7 +280,13 @@ export const defaultSettings: any = { draggableMarker: false, editablePolygon: false, fitMapBounds: true, - mapPageSize: DEFAULT_MAP_PAGE_SIZE + mapPageSize: DEFAULT_MAP_PAGE_SIZE, + snappable: false, + initDragMode: false, + hideAllControlButton: false, + hideDrawControlButton: false, + hideEditControlButton: false, + hideRemoveControlButton: false }; export const hereProviders = [ diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/map-widget2.ts b/ui-ngx/src/app/modules/home/components/widget/lib/maps/map-widget2.ts index 565887b160..bc042e4fc0 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/maps/map-widget2.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/maps/map-widget2.ts @@ -18,6 +18,7 @@ import { defaultSettings, FormattedData, hereProviders, MapProviders, UnitedMapS import LeafletMap from './leaflet-map'; import { commonMapSettingsSchema, + editorSettingSchema, mapPolygonSchema, markerClusteringSettingsSchema, markerClusteringSettingsSchemaLeaflet, @@ -115,6 +116,8 @@ export class MapWidgetController implements MapWidgetInterface { `model.useClusterMarkers === true && model.provider !== "image-map"`)]); addToSchema(schema, clusteringSchema); addGroupInfo(schema, 'Markers Clustering Settings'); + addToSchema(schema, addCondition(editorSettingSchema, '(model.editablePolygon === true || model.draggableMarker === true)')); + addGroupInfo(schema, 'Editor settings'); } return schema; } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/markers.ts b/ui-ngx/src/app/modules/home/components/widget/lib/maps/markers.ts index 85020b7f33..741cf22719 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/maps/markers.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/maps/markers.ts @@ -14,19 +14,17 @@ /// limitations under the License. /// -import L, { Icon, LeafletMouseEvent } from 'leaflet'; -import { FormattedData, MarkerIconInfo, MarkerIconReadyFunction, MarkerImageInfo, MarkerSettings } from './map-models'; +import L, { LeafletMouseEvent } from 'leaflet'; import { - bindPopupActions, - createTooltip, -} from './maps-utils'; -import { - aspectCache, - fillPattern, - parseWithTranslation, - processPattern, - safeExecute -} from './common-maps-utils'; + FormattedData, + MarkerIconInfo, + MarkerIconReadyFunction, + MarkerImageInfo, + MarkerSettings, + UnitedMapSettings +} from './map-models'; +import { bindPopupActions, createTooltip, } from './maps-utils'; +import { aspectCache, fillPattern, parseWithTranslation, processPattern, safeExecute } from './common-maps-utils'; import tinycolor from 'tinycolor2'; import { isDefined, isDefinedAndNotNull } from '@core/utils'; import LeafletMap from './leaflet-map'; @@ -40,10 +38,13 @@ export class Marker { data: FormattedData; dataSources: FormattedData[]; - constructor(private map: LeafletMap, private location: L.LatLng, public settings: MarkerSettings, + constructor(private map: LeafletMap, private location: L.LatLng, public settings: UnitedMapSettings, data?: FormattedData, dataSources?, onDragendListener?) { this.setDataSources(data, dataSources); - this.leafletMarker = L.marker(location, {pmIgnore: !settings.draggableMarker}); + this.leafletMarker = L.marker(location, { + pmIgnore: !settings.draggableMarker, + snapIgnore: !settings.snappable + }); this.markerOffset = [ isDefined(settings.markerOffsetX) ? settings.markerOffsetX : 0.5, diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/polygon.ts b/ui-ngx/src/app/modules/home/components/widget/lib/maps/polygon.ts index 908952e7d2..de55d9b1b0 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/maps/polygon.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/maps/polygon.ts @@ -23,7 +23,7 @@ import { processPattern, safeExecute } from './common-maps-utils'; -import { FormattedData, MarkerSettings, PolygonSettings } from './map-models'; +import { FormattedData, PolygonSettings, UnitedMapSettings } from './map-models'; export class Polygon { @@ -32,7 +32,7 @@ export class Polygon { data: FormattedData; dataSources: FormattedData[]; - constructor(public map, data: FormattedData, dataSources: FormattedData[], private settings: PolygonSettings, + constructor(public map, data: FormattedData, dataSources: FormattedData[], private settings: UnitedMapSettings, private onDragendListener?) { this.dataSources = dataSources; this.data = data; @@ -47,7 +47,8 @@ export class Polygon { weight: settings.polygonStrokeWeight, fillOpacity: settings.polygonOpacity, opacity: settings.polygonStrokeOpacity, - pmIgnore: !settings.editablePolygon + pmIgnore: !settings.editablePolygon, + snapIgnore: !settings.snappable }).addTo(this.map); this.updateLabel(settings); diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/schemes.ts b/ui-ngx/src/app/modules/home/components/widget/lib/maps/schemes.ts index 2924ff688e..1c383b75e0 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/maps/schemes.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/maps/schemes.ts @@ -1311,3 +1311,61 @@ export const providerSets: { [key: string]: IProvider } = { name: 'image-map' } }; + +export const editorSettingSchema = + { + schema: { + title: 'Editor settings', + type: 'object', + properties: { + snappable: { + title: 'Enable snapping to other vertices for precision drawing', + type: 'boolean', + default: false + }, + initDragMode: { + title: 'Initialize map in draggable mode', + type: 'boolean', + default: false + }, + hideAllControlButton: { + title: 'Hide all button', + type: 'boolean', + default: false + }, + hideDrawControlButton: { + title: 'Hide draw buttons', + type: 'boolean', + default: false + }, + hideEditControlButton: { + title: 'Hide edit buttons', + type: 'boolean', + default: false + }, + hideRemoveControlButton: { + title: 'Hide remove button', + type: 'boolean', + default: false + }, + }, + required: [] + }, + form: [ + 'snappable', + 'initDragMode', + 'hideAllControlButton', + { + key: 'hideDrawControlButton', + condition: 'model.hideAllControlButton == false' + }, + { + key: 'hideEditControlButton', + condition: 'model.hideAllControlButton == false' + }, + { + key: 'hideRemoveControlButton', + condition: 'model.hideAllControlButton == false' + } + ] + }; diff --git a/ui-ngx/yarn.lock b/ui-ngx/yarn.lock index f1e5a9abc9..25ca2b4b64 100644 --- a/ui-ngx/yarn.lock +++ b/ui-ngx/yarn.lock @@ -1308,15 +1308,15 @@ resolved "https://registry.yarnpkg.com/@gar/promisify/-/promisify-1.1.2.tgz#30aa825f11d438671d585bd44e7fd564535fc210" integrity sha512-82cpyJyKRoQoRi+14ibCeGPu0CwypgtBAdBhq1WfvagpCZNKqwXbKwXllYSMG91DhmG4jt9gN8eP6lGOtozuaw== -"@geoman-io/leaflet-geoman-free@^2.11.3": - version "2.11.3" - resolved "https://registry.yarnpkg.com/@geoman-io/leaflet-geoman-free/-/leaflet-geoman-free-2.11.3.tgz#480164ab76c2b2a885003e0c111284f3c3160a36" - integrity sha512-LsiurEgKEHBcTnAVl8h7EfS5V/doCuxePzPE9SnfrhtJBN7IzP6UwkEo35Agwko+BnIuw/o2bE4F7irvKwQzjw== - dependencies: - "@turf/boolean-contains" "6.3.0" - "@turf/kinks" "6.3.0" - "@turf/line-intersect" "6.3.0" - "@turf/line-split" "6.3.0" +"@geoman-io/leaflet-geoman-free@^2.11.4": + version "2.11.4" + resolved "https://registry.yarnpkg.com/@geoman-io/leaflet-geoman-free/-/leaflet-geoman-free-2.11.4.tgz#4a43fa8d3d5d2bca751135b775c19c6cc0063699" + integrity sha512-uWfgaGDhrtoCMHdHi2oNVKb8WXFMQvyNnan1sS/+Yn5jMPuhijWFyAjy0G5kTCamXhGXg4vUvlEpiRSrBwewKg== + dependencies: + "@turf/boolean-contains" "^6.5.0" + "@turf/kinks" "^6.5.0" + "@turf/line-intersect" "^6.5.0" + "@turf/line-split" "^6.5.0" lodash "4.17.21" polygon-clipping "0.15.3" @@ -1601,7 +1601,7 @@ resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.2.tgz#423c77877d0569db20e1fc80885ac4118314010e" integrity sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA== -"@turf/bbox@*", "@turf/bbox@^6.3.0": +"@turf/bbox@*", "@turf/bbox@^6.5.0": version "6.5.0" resolved "https://registry.yarnpkg.com/@turf/bbox/-/bbox-6.5.0.tgz#bec30a744019eae420dac9ea46fb75caa44d8dc5" integrity sha512-RBbLaao5hXTYyyg577iuMtDB8ehxMlUqHEJiMs8jT1GHkFhr6sYre3lmLsPeYEi/ZKj5TP5tt7fkzNdJ4GIVyw== @@ -1617,18 +1617,18 @@ "@turf/helpers" "^6.5.0" "@turf/invariant" "^6.5.0" -"@turf/boolean-contains@6.3.0": - version "6.3.0" - resolved "https://registry.yarnpkg.com/@turf/boolean-contains/-/boolean-contains-6.3.0.tgz#fe4fc359e408c8c3c89e7fb159c9d31fde48779a" - integrity sha512-1MW7B5G5tIu1lnAv3pXyFzl75wfBYnbA2GhwHDb4okIXMhloy/r5uIqAZHo0fOXykKVJS/gIfA/MioKIftoTug== +"@turf/boolean-contains@^6.5.0": + version "6.5.0" + resolved "https://registry.yarnpkg.com/@turf/boolean-contains/-/boolean-contains-6.5.0.tgz#f802e7432fb53109242d5bf57393ef2f53849bbf" + integrity sha512-4m8cJpbw+YQcKVGi8y0cHhBUnYT+QRfx6wzM4GI1IdtYH3p4oh/DOBJKrepQyiDzFDaNIjxuWXBh0ai1zVwOQQ== dependencies: - "@turf/bbox" "^6.3.0" - "@turf/boolean-point-in-polygon" "^6.3.0" - "@turf/boolean-point-on-line" "^6.3.0" - "@turf/helpers" "^6.3.0" - "@turf/invariant" "^6.3.0" + "@turf/bbox" "^6.5.0" + "@turf/boolean-point-in-polygon" "^6.5.0" + "@turf/boolean-point-on-line" "^6.5.0" + "@turf/helpers" "^6.5.0" + "@turf/invariant" "^6.5.0" -"@turf/boolean-point-in-polygon@^6.3.0": +"@turf/boolean-point-in-polygon@^6.5.0": version "6.5.0" resolved "https://registry.yarnpkg.com/@turf/boolean-point-in-polygon/-/boolean-point-in-polygon-6.5.0.tgz#6d2e9c89de4cd2e4365004c1e51490b7795a63cf" integrity sha512-DtSuVFB26SI+hj0SjrvXowGTUCHlgevPAIsukssW6BG5MlNSBQAo70wpICBNJL6RjukXg8d2eXaAWuD/CqL00A== @@ -1636,7 +1636,7 @@ "@turf/helpers" "^6.5.0" "@turf/invariant" "^6.5.0" -"@turf/boolean-point-on-line@^6.3.0": +"@turf/boolean-point-on-line@^6.5.0": version "6.5.0" resolved "https://registry.yarnpkg.com/@turf/boolean-point-on-line/-/boolean-point-on-line-6.5.0.tgz#a8efa7bad88760676f395afb9980746bc5b376e9" integrity sha512-A1BbuQ0LceLHvq7F/P7w3QvfpmZqbmViIUPHdNLvZimFNLo4e6IQunmzbe+8aSStH9QRZm3VOflyvNeXvvpZEQ== @@ -1660,37 +1660,26 @@ "@turf/helpers" "^6.5.0" "@turf/invariant" "^6.5.0" -"@turf/helpers@6.x", "@turf/helpers@^6.3.0", "@turf/helpers@^6.5.0": +"@turf/helpers@6.x", "@turf/helpers@^6.5.0": version "6.5.0" resolved "https://registry.yarnpkg.com/@turf/helpers/-/helpers-6.5.0.tgz#f79af094bd6b8ce7ed2bd3e089a8493ee6cae82e" integrity sha512-VbI1dV5bLFzohYYdgqwikdMVpe7pJ9X3E+dlr425wa2/sMJqYDhTO++ec38/pcPvPE6oD9WEEeU3Xu3gza+VPw== -"@turf/invariant@^6.3.0", "@turf/invariant@^6.5.0": +"@turf/invariant@^6.5.0": version "6.5.0" resolved "https://registry.yarnpkg.com/@turf/invariant/-/invariant-6.5.0.tgz#970afc988023e39c7ccab2341bd06979ddc7463f" integrity sha512-Wv8PRNCtPD31UVbdJE/KVAWKe7l6US+lJItRR/HOEW3eh+U/JwRCSUl/KZ7bmjM/C+zLNoreM2TU6OoLACs4eg== dependencies: "@turf/helpers" "^6.5.0" -"@turf/kinks@6.3.0": - version "6.3.0" - resolved "https://registry.yarnpkg.com/@turf/kinks/-/kinks-6.3.0.tgz#a16b4ccc5a5aae139d43e36271e0a0494fdb4bf7" - integrity sha512-BLWvbl2/fa4SeJzVMbleT6Vo1cmzwmzRfxL2xxMei2jmf6JSvqDoMJFwIHGXrLZXvhOCb1b2C+MhBfhtc7kYkQ== - dependencies: - "@turf/helpers" "^6.3.0" - -"@turf/line-intersect@6.3.0": - version "6.3.0" - resolved "https://registry.yarnpkg.com/@turf/line-intersect/-/line-intersect-6.3.0.tgz#726a50edc66bb7b5e798b052b103fb0da4d1c4f4" - integrity sha512-3naxR7XpkPd2vst3Mw6DFry4C9m3o0/f2n/xu5UAyxb88Ie4m2k+1eqkhzMMx/0L+E6iThWpLx7DASM6q6o9ow== +"@turf/kinks@^6.5.0": + version "6.5.0" + resolved "https://registry.yarnpkg.com/@turf/kinks/-/kinks-6.5.0.tgz#80e7456367535365012f658cf1a988b39a2c920b" + integrity sha512-ViCngdPt1eEL7hYUHR2eHR662GvCgTc35ZJFaNR6kRtr6D8plLaDju0FILeFFWSc+o8e3fwxZEJKmFj9IzPiIQ== dependencies: - "@turf/helpers" "^6.3.0" - "@turf/invariant" "^6.3.0" - "@turf/line-segment" "^6.3.0" - "@turf/meta" "^6.3.0" - geojson-rbush "3.x" + "@turf/helpers" "^6.5.0" -"@turf/line-intersect@^6.3.0", "@turf/line-intersect@^6.5.0": +"@turf/line-intersect@^6.5.0": version "6.5.0" resolved "https://registry.yarnpkg.com/@turf/line-intersect/-/line-intersect-6.5.0.tgz#dea48348b30c093715d2195d2dd7524aee4cf020" integrity sha512-CS6R1tZvVQD390G9Ea4pmpM6mJGPWoL82jD46y0q1KSor9s6HupMIo1kY4Ny+AEYQl9jd21V3Scz20eldpbTVA== @@ -1701,7 +1690,7 @@ "@turf/meta" "^6.5.0" geojson-rbush "3.x" -"@turf/line-segment@^6.3.0", "@turf/line-segment@^6.5.0": +"@turf/line-segment@^6.5.0": version "6.5.0" resolved "https://registry.yarnpkg.com/@turf/line-segment/-/line-segment-6.5.0.tgz#ee73f3ffcb7c956203b64ed966d96af380a4dd65" integrity sha512-jI625Ho4jSuJESNq66Mmi290ZJ5pPZiQZruPVpmHkUw257Pew0alMmb6YrqYNnLUuiVVONxAAKXUVeeUGtycfw== @@ -1710,30 +1699,30 @@ "@turf/invariant" "^6.5.0" "@turf/meta" "^6.5.0" -"@turf/line-split@6.3.0": - version "6.3.0" - resolved "https://registry.yarnpkg.com/@turf/line-split/-/line-split-6.3.0.tgz#ee218f66cd65ce84eafc4956c24083663f6082ea" - integrity sha512-Q0nUJ0vczy11piyEz0FaKScFwSQtb1HJ2RPEMCw1coUJhTCB02KBWQLImhYqwsD3uLg+H/fxaJ1Gva6EPWoDNQ== - dependencies: - "@turf/bbox" "^6.3.0" - "@turf/helpers" "^6.3.0" - "@turf/invariant" "^6.3.0" - "@turf/line-intersect" "^6.3.0" - "@turf/line-segment" "^6.3.0" - "@turf/meta" "^6.3.0" - "@turf/nearest-point-on-line" "^6.3.0" - "@turf/square" "^6.3.0" - "@turf/truncate" "^6.3.0" +"@turf/line-split@^6.5.0": + version "6.5.0" + resolved "https://registry.yarnpkg.com/@turf/line-split/-/line-split-6.5.0.tgz#116d7fbf714457878225187f5820ef98db7b02c2" + integrity sha512-/rwUMVr9OI2ccJjw7/6eTN53URtGThNSD5I0GgxyFXMtxWiloRJ9MTff8jBbtPWrRka/Sh2GkwucVRAEakx9Sw== + dependencies: + "@turf/bbox" "^6.5.0" + "@turf/helpers" "^6.5.0" + "@turf/invariant" "^6.5.0" + "@turf/line-intersect" "^6.5.0" + "@turf/line-segment" "^6.5.0" + "@turf/meta" "^6.5.0" + "@turf/nearest-point-on-line" "^6.5.0" + "@turf/square" "^6.5.0" + "@turf/truncate" "^6.5.0" geojson-rbush "3.x" -"@turf/meta@6.x", "@turf/meta@^6.3.0", "@turf/meta@^6.5.0": +"@turf/meta@6.x", "@turf/meta@^6.5.0": version "6.5.0" resolved "https://registry.yarnpkg.com/@turf/meta/-/meta-6.5.0.tgz#b725c3653c9f432133eaa04d3421f7e51e0418ca" integrity sha512-RrArvtsV0vdsCBegoBtOalgdSOfkBrTJ07VkpiCnq/491W67hnMWmDu7e6Ztw0C3WldRYTXkg3SumfdzZxLBHA== dependencies: "@turf/helpers" "^6.5.0" -"@turf/nearest-point-on-line@^6.3.0": +"@turf/nearest-point-on-line@^6.5.0": version "6.5.0" resolved "https://registry.yarnpkg.com/@turf/nearest-point-on-line/-/nearest-point-on-line-6.5.0.tgz#8e1cd2cdc0b5acaf4c8d8b3b33bb008d3cb99e7b" integrity sha512-WthrvddddvmymnC+Vf7BrkHGbDOUu6Z3/6bFYUGv1kxw8tiZ6n83/VG6kHz4poHOfS0RaNflzXSkmCi64fLBlg== @@ -1746,7 +1735,7 @@ "@turf/line-intersect" "^6.5.0" "@turf/meta" "^6.5.0" -"@turf/square@^6.3.0": +"@turf/square@^6.5.0": version "6.5.0" resolved "https://registry.yarnpkg.com/@turf/square/-/square-6.5.0.tgz#ab43eef99d39c36157ab5b80416bbeba1f6b2122" integrity sha512-BM2UyWDmiuHCadVhHXKIx5CQQbNCpOxB6S/aCNOCLbhCeypKX5Q0Aosc5YcmCJgkwO5BERCC6Ee7NMbNB2vHmQ== @@ -1754,7 +1743,7 @@ "@turf/distance" "^6.5.0" "@turf/helpers" "^6.5.0" -"@turf/truncate@^6.3.0": +"@turf/truncate@^6.5.0": version "6.5.0" resolved "https://registry.yarnpkg.com/@turf/truncate/-/truncate-6.5.0.tgz#c3a16cad959f1be1c5156157d5555c64b19185d8" integrity sha512-pFxg71pLk+eJj134Z9yUoRhIi8vqnnKvCYwdT4x/DQl/19RVdq1tV3yqOT3gcTQNfniteylL5qV1uTBDV5sgrg== From d7f4bd5436c72b1701fd806d9661bbd1ce203371 Mon Sep 17 00:00:00 2001 From: ArtemDzhereleiko Date: Thu, 23 Dec 2021 14:16:22 +0200 Subject: [PATCH 027/798] UI: Show legend fieldset only in timeseries and latest widget --- .../home/components/widget/legend.component.ts | 12 +++++++----- .../components/widget/widget-config.component.html | 2 +- .../components/widget/widget-config.component.ts | 4 ++++ 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/widget/legend.component.ts b/ui-ngx/src/app/modules/home/components/widget/legend.component.ts index 004fd5087b..3f66fc8aba 100644 --- a/ui-ngx/src/app/modules/home/components/widget/legend.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/legend.component.ts @@ -60,11 +60,13 @@ export class LegendComponent implements OnInit { } legendKeys(): LegendKey[] { - let keys = this.legendData.keys; - if (this.legendConfig.sortDataKeys) { - keys = this.legendData.keys.sort((key1, key2) => key1.dataKey.label.localeCompare(key2.dataKey.label)); - } - return keys.filter(legendKey => this.legendData.keys[legendKey.dataIndex].dataKey.inLegend); + try { + let keys = this.legendData.keys; + if (this.legendConfig.sortDataKeys) { + keys = this.legendData.keys.sort((key1, key2) => key1.dataKey.label.localeCompare(key2.dataKey.label)); + } + return keys.filter(legendKey => this.legendData.keys[legendKey.dataIndex].dataKey.inLegend); + } catch (e) {} } } diff --git a/ui-ngx/src/app/modules/home/components/widget/widget-config.component.html b/ui-ngx/src/app/modules/home/components/widget/widget-config.component.html index 97542fd3cb..6878a19d60 100644 --- a/ui-ngx/src/app/modules/home/components/widget/widget-config.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/widget-config.component.html @@ -421,7 +421,7 @@ -
+
widget-config.legend diff --git a/ui-ngx/src/app/modules/home/components/widget/widget-config.component.ts b/ui-ngx/src/app/modules/home/components/widget/widget-config.component.ts index 7d779a3a6c..e64cc4de42 100644 --- a/ui-ngx/src/app/modules/home/components/widget/widget-config.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/widget-config.component.ts @@ -159,6 +159,8 @@ export class WidgetConfigComponent extends PageComponent implements OnInit, Cont modelValue: WidgetConfigComponentData; + showLegendFieldset = true; + private propagateChange = null; public dataSettings: FormGroup; @@ -322,6 +324,8 @@ export class WidgetConfigComponent extends PageComponent implements OnInit, Cont this.datasourceTypes.push(DatasourceType.entityCount); } } + this.showLegendFieldset = (this.widgetType === widgetType.timeseries || this.widgetType === widgetType.latest); + this.dataSettings = this.fb.group({}); this.targetDeviceSettings = this.fb.group({}); this.alarmSourceSettings = this.fb.group({}); From 94d502e6eedde28c9360789c43cfda454efd2b4b Mon Sep 17 00:00:00 2001 From: ArtemDzhereleiko Date: Fri, 24 Dec 2021 11:13:28 +0200 Subject: [PATCH 028/798] UI: Refactoring --- .../modules/home/components/widget/widget-config.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui-ngx/src/app/modules/home/components/widget/widget-config.component.ts b/ui-ngx/src/app/modules/home/components/widget/widget-config.component.ts index e64cc4de42..6b485dd66b 100644 --- a/ui-ngx/src/app/modules/home/components/widget/widget-config.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/widget-config.component.ts @@ -324,7 +324,6 @@ export class WidgetConfigComponent extends PageComponent implements OnInit, Cont this.datasourceTypes.push(DatasourceType.entityCount); } } - this.showLegendFieldset = (this.widgetType === widgetType.timeseries || this.widgetType === widgetType.latest); this.dataSettings = this.fb.group({}); this.targetDeviceSettings = this.fb.group({}); @@ -389,6 +388,7 @@ export class WidgetConfigComponent extends PageComponent implements OnInit, Cont if (this.modelValue) { if (this.widgetType !== this.modelValue.widgetType) { this.widgetType = this.modelValue.widgetType; + this.showLegendFieldset = (this.widgetType === widgetType.timeseries || this.widgetType === widgetType.latest); this.buildForms(); } const config = this.modelValue.config; From cfead9941ed1a66e29b89adaf28cad40ca28b982 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Fri, 24 Dec 2021 11:28:20 +0200 Subject: [PATCH 029/798] UI: Fixed code style --- ui-ngx/src/app/shared/components/json-object-view.component.ts | 1 - ui-ngx/src/assets/locale/locale.constant-en_US.json | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/ui-ngx/src/app/shared/components/json-object-view.component.ts b/ui-ngx/src/app/shared/components/json-object-view.component.ts index b85988c3d3..483d1a014a 100644 --- a/ui-ngx/src/app/shared/components/json-object-view.component.ts +++ b/ui-ngx/src/app/shared/components/json-object-view.component.ts @@ -127,7 +127,6 @@ export class JsonObjectViewComponent implements OnInit { newWidth = 8 * maxLineLength + 16; } if (this.heigthValue) { - // this.renderer.setStyle(editorElement, 'minHeight', newHeight.toString() + 'px'); this.renderer.setStyle(editorElement, 'height', newHeight.toString() + 'px'); } if (this.widthValue) { diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index bd33c10376..e4fa66b4e5 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -3279,7 +3279,7 @@ "EXPIRED": "EXPIRED", "FAILED": "FAILED" }, - "rpc-search-status-all": "ALL", + "rpc-search-status-all": "ALL", "message-types": { "false": "Two-way", "true": "One-way" From 8a421e3aa0cf399573940437f98116f0b9ebd271 Mon Sep 17 00:00:00 2001 From: ArtemDzhereleiko Date: Fri, 24 Dec 2021 16:05:24 +0200 Subject: [PATCH 030/798] UI: Fixed constant boolean filters creating form --- .../filter/boolean-filter-predicate.component.ts | 7 +++++-- .../home/components/filter/key-filter-dialog.component.ts | 6 +++++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/filter/boolean-filter-predicate.component.ts b/ui-ngx/src/app/modules/home/components/filter/boolean-filter-predicate.component.ts index d5299a52ea..e0a4868898 100644 --- a/ui-ngx/src/app/modules/home/components/filter/boolean-filter-predicate.component.ts +++ b/ui-ngx/src/app/modules/home/components/filter/boolean-filter-predicate.component.ts @@ -109,8 +109,11 @@ export class BooleanFilterPredicateComponent implements ControlValueAccessor, Va } private updateModel() { - const predicate: BooleanFilterPredicate = this.booleanFilterPredicateFormGroup.getRawValue(); - predicate.type = FilterPredicateType.BOOLEAN; + let predicate: BooleanFilterPredicate = null; + if (this.booleanFilterPredicateFormGroup.valid) { + predicate = this.booleanFilterPredicateFormGroup.getRawValue(); + predicate.type = FilterPredicateType.BOOLEAN; + } this.propagateChange(predicate); } diff --git a/ui-ngx/src/app/modules/home/components/filter/key-filter-dialog.component.ts b/ui-ngx/src/app/modules/home/components/filter/key-filter-dialog.component.ts index 6c9c85330a..41520eede7 100644 --- a/ui-ngx/src/app/modules/home/components/filter/key-filter-dialog.component.ts +++ b/ui-ngx/src/app/modules/home/components/filter/key-filter-dialog.component.ts @@ -134,6 +134,10 @@ export class KeyFilterDialogComponent extends } ); } + if (valueType === EntityKeyValueType.BOOLEAN) { + this.keyFilterFormGroup.get('value').clearValidators(); + this.keyFilterFormGroup.get('value').updateValueAndValidity(); + } }); this.keyFilterFormGroup.get('key.type').valueChanges.pipe( @@ -148,7 +152,7 @@ export class KeyFilterDialogComponent extends this.showAutocomplete = false; } if (this.data.telemetryKeysOnly) { - if (type === EntityKeyType.CONSTANT) { + if (type === EntityKeyType.CONSTANT && (this.keyFilterFormGroup.get('valueType').value !== EntityKeyValueType.BOOLEAN)) { this.keyFilterFormGroup.get('value').setValidators(Validators.required); this.keyFilterFormGroup.get('value').updateValueAndValidity(); } else { From fef7b2605066cb39322de5e9b941c8b39140067b Mon Sep 17 00:00:00 2001 From: ArtemDzhereleiko Date: Fri, 24 Dec 2021 17:03:51 +0200 Subject: [PATCH 031/798] UI: Added a condition for ckeck boolean key type only for constant type --- .../home/components/filter/key-filter-dialog.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui-ngx/src/app/modules/home/components/filter/key-filter-dialog.component.ts b/ui-ngx/src/app/modules/home/components/filter/key-filter-dialog.component.ts index 41520eede7..c96bc66419 100644 --- a/ui-ngx/src/app/modules/home/components/filter/key-filter-dialog.component.ts +++ b/ui-ngx/src/app/modules/home/components/filter/key-filter-dialog.component.ts @@ -134,7 +134,7 @@ export class KeyFilterDialogComponent extends } ); } - if (valueType === EntityKeyValueType.BOOLEAN) { + if (valueType === EntityKeyValueType.BOOLEAN && this.isConstantKeyType) { this.keyFilterFormGroup.get('value').clearValidators(); this.keyFilterFormGroup.get('value').updateValueAndValidity(); } From edcde714c7fbb1255c9e628317e30cf598b57744 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Tue, 28 Dec 2021 12:01:26 +0200 Subject: [PATCH 032/798] UI: Added entity details page --- ui-ngx/src/app/core/utils.ts | 14 ++ .../entity/entity-details-page.component.html | 57 ++++++ .../entity/entity-details-page.component.scss | 119 +++++++++++++ .../entity/entity-details-page.component.ts | 154 ++++++++++++++++ .../entity/entity-details-panel.component.ts | 16 +- .../components/entity/entity.component.ts | 2 + .../home/components/home-components.module.ts | 3 + ...device-profile-autocomplete.component.html | 7 + ...device-profile-autocomplete.component.scss | 21 +++ .../device-profile-autocomplete.component.ts | 9 +- ...ofile-provision-configuration.component.ts | 2 +- .../profile/device-profile.component.html | 8 + ...tenant-profile-autocomplete.component.html | 7 + ...tenant-profile-autocomplete.component.scss | 21 +++ .../tenant-profile-autocomplete.component.ts | 16 +- .../profile/tenant-profile.component.html | 6 + .../relation/relation-table.component.html | 8 +- .../relation/relation-table.component.scss | 3 + .../models/datasource/relation-datasource.ts | 5 +- .../home/pages/admin/admin-routing.module.ts | 38 +++- .../resources-library-table-config.resolve.ts | 13 +- .../resource/resources-library.component.html | 6 + .../resource/resources-library.component.ts | 5 +- .../home/pages/asset/asset-routing.module.ts | 42 ++++- .../home/pages/asset/asset.component.html | 6 + .../asset/assets-table-config.resolver.ts | 10 ++ .../pages/customer/customer-routing.module.ts | 167 +++++++++++++++--- .../pages/customer/customer.component.html | 6 + .../customers-table-config.resolver.ts | 10 ++ .../device-profile-routing.module.ts | 20 +++ .../device-profiles-table-config.resolver.ts | 13 +- .../pages/device/device-routing.module.ts | 42 ++++- .../home/pages/device/device.component.html | 7 + .../home/pages/device/device.component.ts | 1 + .../device/devices-table-config.resolver.ts | 10 ++ .../home/pages/edge/edge-routing.module.ts | 131 ++++++++++++-- .../home/pages/edge/edge.component.html | 6 + .../pages/edge/edges-table-config.resolver.ts | 10 ++ .../entity-view/entity-view-routing.module.ts | 42 ++++- .../entity-view/entity-view.component.html | 6 + .../entity-views-table-config.resolver.ts | 10 ++ .../ota-update/ota-update-routing.module.ts | 39 +++- .../ota-update-table-config.resolve.ts | 13 +- .../ota-update/ota-update.component.html | 9 +- .../tenant-profile-routing.module.ts | 20 +++ .../tenant-profiles-table-config.resolver.ts | 15 +- .../pages/tenant/tenant-routing.module.ts | 55 +++++- .../home/pages/tenant/tenant.component.html | 7 + .../tenant/tenants-table-config.resolver.ts | 10 ++ .../home/pages/user/user-routing.module.ts | 43 ++++- .../home/pages/user/user.component.html | 6 + .../pages/user/users-table-config.resolver.ts | 27 ++- .../shared/components/breadcrumb.component.ts | 8 +- .../ota-package-autocomplete.component.html | 7 + .../ota-package-autocomplete.component.scss | 21 +++ .../ota-package-autocomplete.component.ts | 10 +- ui-ngx/src/app/shared/models/constants.ts | 15 ++ .../src/app/shared/models/relation.models.ts | 1 + ui-ngx/src/app/shared/shared.module.ts | 1 + .../assets/locale/locale.constant-en_US.json | 3 +- 60 files changed, 1271 insertions(+), 118 deletions(-) create mode 100644 ui-ngx/src/app/modules/home/components/entity/entity-details-page.component.html create mode 100644 ui-ngx/src/app/modules/home/components/entity/entity-details-page.component.scss create mode 100644 ui-ngx/src/app/modules/home/components/entity/entity-details-page.component.ts create mode 100644 ui-ngx/src/app/modules/home/components/profile/device-profile-autocomplete.component.scss create mode 100644 ui-ngx/src/app/modules/home/components/profile/tenant-profile-autocomplete.component.scss create mode 100644 ui-ngx/src/app/shared/components/ota-package/ota-package-autocomplete.component.scss diff --git a/ui-ngx/src/app/core/utils.ts b/ui-ngx/src/app/core/utils.ts index b986583171..c09488a593 100644 --- a/ui-ngx/src/app/core/utils.ts +++ b/ui-ngx/src/app/core/utils.ts @@ -20,6 +20,10 @@ import { finalize, share } from 'rxjs/operators'; import { Datasource } from '@app/shared/models/widget.models'; import { EntityId } from '@shared/models/id/entity-id'; import { NULL_UUID } from '@shared/models/id/has-uuid'; +import { BreadCrumbLabelFunction } from '@shared/components/breadcrumb'; +import { EntityDetailsPageComponent } from '@home/components/entity/entity-details-page.component'; +import { baseDetailsPageByEntityType } from '@shared/models/constants'; +import { EntityType } from '@shared/models/entity-type.models'; const varsRegex = /\${([^}]*)}/g; @@ -460,3 +464,13 @@ export function randomAlphanumeric(length: number): string { } return result; } + +export const entityDetailsPageBreadcrumbLabelFunction: BreadCrumbLabelFunction + = ((route, translate, component) => { + return component.entity?.name || component.headerSubtitle; +}); + + +export function getEntityDetailsPageURL(id: string, entityType: EntityType): string { + return `${baseDetailsPageByEntityType.get(entityType)}/${id}`; +} diff --git a/ui-ngx/src/app/modules/home/components/entity/entity-details-page.component.html b/ui-ngx/src/app/modules/home/components/entity/entity-details-page.component.html new file mode 100644 index 0000000000..e025534f2b --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/entity/entity-details-page.component.html @@ -0,0 +1,57 @@ + + + +
+
+
{{ headerTitle }}
+
{{ headerSubtitle }}
+
+
+ +
+ + +
+
+
+ + + + + + + + +
+ diff --git a/ui-ngx/src/app/modules/home/components/entity/entity-details-page.component.scss b/ui-ngx/src/app/modules/home/components/entity/entity-details-page.component.scss new file mode 100644 index 0000000000..a74e9e46c1 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/entity/entity-details-page.component.scss @@ -0,0 +1,119 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +@import "../../../../../scss/constants"; + +:host { + width: 100%; + height: 100%; + display: flex; + flex-direction: column; + overflow: hidden; + + .settings-card { + margin: 8px; + padding: 0; + width: 100%; + height: 100%; + display: flex; + flex-direction: column; + + .details-toolbar { + height: 84px; + min-height: 84px; + border-radius: 4px 4px 0 0; + background: #fff; + border-bottom: 1px solid rgba(0, 0, 0, 0.12); + + .mat-toolbar-tools { + padding: 0 8px; + } + + .tb-details-title-header { + min-width: 0; + width: auto; + } + + .tb-details-title { + font-size: 1rem; + font-weight: 500; + + @media #{$mat-gt-sm} { + font-size: 1.2rem; + } + } + + .tb-details-subtitle { + font-size: 0.9rem; + opacity: .8; + } + + .tb-ellipsis { + width: 100%; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + } + + @media #{$mat-md} { + width: 80%; + } + + @media #{$mat-gt-md} { + width: 60%; + } + + .tb-header-button { + .tb-btn-header { + position: relative !important; + display: inline-block !important; + animation: tbMoveFromTopFade .3s ease both; + + &.tb-hide { + animation: tbMoveToTopFade .3s ease both; + } + } + } + } +} + +:host ::ng-deep { + .tb-help { + .mat-icon-button.mat-primary { + color: rgba(0, 0, 0, 0.52); + } + } + + .mat-card-content { + position: relative; + overflow: hidden; + + > .mat-tab-group { + > .mat-tab-body-wrapper { + position: absolute; + top: 49px; + left: 0; + right: 0; + bottom: 0; + } + > .mat-tab-header { + .mat-tab-label { + min-width: 40px; + } + } + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/entity/entity-details-page.component.ts b/ui-ngx/src/app/modules/home/components/entity/entity-details-page.component.ts new file mode 100644 index 0000000000..27ed899c79 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/entity/entity-details-page.component.ts @@ -0,0 +1,154 @@ +/// +/// Copyright © 2016-2021 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { + ChangeDetectionStrategy, + ChangeDetectorRef, + Component, + ComponentFactoryResolver, + HostBinding, + Injector, + OnDestroy, + OnInit +} from '@angular/core'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { EntityTableConfig } from '@home/models/entity/entities-table-config.models'; +import { BaseData, HasId } from '@shared/models/base-data'; +import { ActivatedRoute, Router } from '@angular/router'; +import { FormGroup } from '@angular/forms'; +import { AssetId } from '@shared/models/id/asset-id'; +import { TranslateService } from '@ngx-translate/core'; +import { deepClone, mergeDeep } from '@core/utils'; +import { BroadcastService } from '@core/services/broadcast.service'; +import { EntityDetailsPanelComponent } from '@home/components/entity/entity-details-panel.component'; + +@Component({ + selector: 'tb-entity-details-page', + templateUrl: './entity-details-page.component.html', + styleUrls: ['./entity-details-page.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class EntityDetailsPageComponent extends EntityDetailsPanelComponent implements OnInit, OnDestroy { + + headerTitle: string; + headerSubtitle: string; + + isReadOnly = false; + + set entitiesTableConfig(entitiesTableConfig: EntityTableConfig>) { + if (this.entitiesTableConfigValue !== entitiesTableConfig) { + this.entitiesTableConfigValue = entitiesTableConfig; + if (this.entitiesTableConfigValue) { + this.isEdit = false; + this.entity = null; + } + } + } + + get entitiesTableConfig(): EntityTableConfig> { + return this.entitiesTableConfigValue; + } + + @HostBinding('class') 'tb-absolute-fill'; + + constructor(private route: ActivatedRoute, + private router: Router, + protected injector: Injector, + protected cd: ChangeDetectorRef, + protected componentFactoryResolver: ComponentFactoryResolver, + private broadcast: BroadcastService, + private translate: TranslateService, + protected store: Store) { + super(store, injector, cd, componentFactoryResolver); + this.entitiesTableConfig = this.route.snapshot.data.entitiesTableConfig; + } + + ngOnInit() { + this.headerSubtitle = ''; + this.route.paramMap.subscribe( paramMap => { + this.entityId = new AssetId(paramMap.get('entityId')); + }); + this.headerSubtitle = this.translate.instant(this.entitiesTableConfig.entityTranslations.details); + super.init(); + this.entityComponent.isDetailsPage = true; + } + + ngOnDestroy() { + super.ngOnDestroy(); + } + + reload(): void { + this.isEdit = false; + this.entitiesTableConfig.loadEntity(this.currentEntityId).subscribe( + (entity) => { + this.entity = entity; + this.broadcast.broadcast('updateBreadcrumb'); + this.isReadOnly = this.entitiesTableConfig.detailsReadonly(entity); + this.headerTitle = this.entitiesTableConfig.entityTitle(entity); + this.entityComponent.entity = entity; + this.entityComponent.isEdit = false; + if (this.entityTabsComponent) { + this.entityTabsComponent.entity = entity; + } + } + ); + } + + onToggleDetailsEditMode() { + if (this.isEdit) { + this.entityComponent.entity = this.entity; + if (this.entityTabsComponent) { + this.entityTabsComponent.entity = this.entity; + } + this.isEdit = !this.isEdit; + } else { + this.isEdit = !this.isEdit; + this.editingEntity = deepClone(this.entity); + this.entityComponent.entity = this.editingEntity; + if (this.entityTabsComponent) { + this.entityTabsComponent.entity = this.editingEntity; + } + if (this.entitiesTableConfig.hideDetailsTabsOnEdit) { + this.selectedTab = 0; + } + } + } + + onApplyDetails() { + if (this.detailsForm && this.detailsForm.valid) { + const editingEntity = {...this.editingEntity, ...this.detailsForm.getRawValue()}; + if (this.detailsForm.hasOwnProperty('additionalInfo')) { + editingEntity.additionalInfo = + mergeDeep((this.editingEntity as any).additionalInfo, this.detailsForm.getRawValue()?.additionalInfo); + } + this.entitiesTableConfig.saveEntity(editingEntity, this.editingEntity).subscribe( + (entity) => { + this.entity = entity; + this.entityComponent.entity = entity; + if (this.entityTabsComponent) { + this.entityTabsComponent.entity = entity; + } + this.isEdit = false; + } + ); + } + } + + confirmForm(): FormGroup { + return this.detailsForm; + } +} diff --git a/ui-ngx/src/app/modules/home/components/entity/entity-details-panel.component.ts b/ui-ngx/src/app/modules/home/components/entity/entity-details-panel.component.ts index d6378620f9..c5e7968935 100644 --- a/ui-ngx/src/app/modules/home/components/entity/entity-details-panel.component.ts +++ b/ui-ngx/src/app/modules/home/components/entity/entity-details-panel.component.ts @@ -88,15 +88,15 @@ export class EntityDetailsPanelComponent extends PageComponent implements AfterV entity: BaseData; editingEntity: BaseData; - private currentEntityId: HasId; - private subscriptions: Subscription[] = []; - private viewInited = false; - private pendingTabs: MatTab[]; + protected currentEntityId: HasId; + protected subscriptions: Subscription[] = []; + protected viewInited = false; + protected pendingTabs: MatTab[]; constructor(protected store: Store, - private injector: Injector, - private cd: ChangeDetectorRef, - private componentFactoryResolver: ComponentFactoryResolver) { + protected injector: Injector, + protected cd: ChangeDetectorRef, + protected componentFactoryResolver: ComponentFactoryResolver) { super(store); } @@ -139,7 +139,7 @@ export class EntityDetailsPanelComponent extends PageComponent implements AfterV return this.isEditValue; } - private init() { + protected init() { this.translations = this.entitiesTableConfig.entityTranslations; this.resources = this.entitiesTableConfig.entityResources; this.buildEntityComponent(); diff --git a/ui-ngx/src/app/modules/home/components/entity/entity.component.ts b/ui-ngx/src/app/modules/home/components/entity/entity.component.ts index 8d868eff98..5da90b49ff 100644 --- a/ui-ngx/src/app/modules/home/components/entity/entity.component.ts +++ b/ui-ngx/src/app/modules/home/components/entity/entity.component.ts @@ -38,6 +38,8 @@ export abstract class EntityComponent, isEditValue: boolean; + isDetailsPage = false; + @Input() set entitiesTableConfig(entitiesTableConfig: C) { this.setEntitiesTableConfig(entitiesTableConfig); diff --git a/ui-ngx/src/app/modules/home/components/home-components.module.ts b/ui-ngx/src/app/modules/home/components/home-components.module.ts index 73361eddce..0960d98620 100644 --- a/ui-ngx/src/app/modules/home/components/home-components.module.ts +++ b/ui-ngx/src/app/modules/home/components/home-components.module.ts @@ -147,6 +147,7 @@ import { HOME_COMPONENTS_MODULE_TOKEN } from '@home/components/tokens'; import { DashboardStateComponent } from '@home/components/dashboard-page/dashboard-state.component'; +import { EntityDetailsPageComponent } from '@home/components/entity/entity-details-page.component'; @NgModule({ declarations: @@ -155,6 +156,7 @@ import { DashboardStateComponent } from '@home/components/dashboard-page/dashboa AddEntityDialogComponent, DetailsPanelComponent, EntityDetailsPanelComponent, + EntityDetailsPageComponent, AuditLogTableComponent, AuditLogDetailsDialogComponent, EventContentDialogComponent, @@ -282,6 +284,7 @@ import { DashboardStateComponent } from '@home/components/dashboard-page/dashboa AddEntityDialogComponent, DetailsPanelComponent, EntityDetailsPanelComponent, + EntityDetailsPageComponent, AuditLogTableComponent, EventTableComponent, EdgeDownlinkTableHeaderComponent, diff --git a/ui-ngx/src/app/modules/home/components/profile/device-profile-autocomplete.component.html b/ui-ngx/src/app/modules/home/components/profile/device-profile-autocomplete.component.html index 20f271d660..2b73fc32c4 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device-profile-autocomplete.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/device-profile-autocomplete.component.html @@ -38,6 +38,13 @@ (click)="editDeviceProfile($event)"> edit + DeviceProfileAutocompleteComponent), @@ -79,6 +80,9 @@ export class DeviceProfileAutocompleteComponent implements ControlValueAccessor, @Input() addNewProfile = true; + @Input() + showDetailsPageLink = false; + @Input() transportType: DeviceTransportType = null; @@ -110,6 +114,7 @@ export class DeviceProfileAutocompleteComponent implements ControlValueAccessor, filteredDeviceProfiles: Observable>; searchText = ''; + deviceProfileURL: string; private dirty = false; @@ -240,6 +245,7 @@ export class DeviceProfileAutocompleteComponent implements ControlValueAccessor, this.deviceProfileService.getDeviceProfileInfo(value.id).subscribe( (profile) => { this.modelValue = new DeviceProfileId(profile.id.id); + this.deviceProfileURL = getEntityDetailsPageURL(this.modelValue.id, this.modelValue.entityType); this.selectDeviceProfileFormGroup.get('deviceProfile').patchValue(profile, {emitEvent: false}); this.deviceProfileChanged.emit(profile); } @@ -278,6 +284,7 @@ export class DeviceProfileAutocompleteComponent implements ControlValueAccessor, const idValue = deviceProfile && deviceProfile.id ? new DeviceProfileId(deviceProfile.id.id) : null; if (!entityIdEquals(this.modelValue, idValue)) { this.modelValue = idValue; + this.deviceProfileURL = getEntityDetailsPageURL(this.modelValue.id, this.modelValue.entityType); this.propagateChange(this.modelValue); this.deviceProfileChanged.emit(deviceProfile); } diff --git a/ui-ngx/src/app/modules/home/components/profile/device-profile-provision-configuration.component.ts b/ui-ngx/src/app/modules/home/components/profile/device-profile-provision-configuration.component.ts index 28559dc4f7..3693c82f8f 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device-profile-provision-configuration.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device-profile-provision-configuration.component.ts @@ -130,7 +130,7 @@ export class DeviceProfileProvisionConfigurationComponent implements ControlValu setDisabledState(isDisabled: boolean){ this.disabled = isDisabled; if (this.disabled){ - this.provisionConfigurationFormGroup.disable(); + this.provisionConfigurationFormGroup.disable({emitEvent: false}); } else { if (this.provisionConfigurationFormGroup.get('type').value !== DeviceProvisionType.DISABLED) { this.provisionConfigurationFormGroup.enable({emitEvent: false}); diff --git a/ui-ngx/src/app/modules/home/components/profile/device-profile.component.html b/ui-ngx/src/app/modules/home/components/profile/device-profile.component.html index 917ad25dd2..df00bdf8fa 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device-profile.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/device-profile.component.html @@ -16,6 +16,12 @@ -->
+ + TenantProfileAutocompleteComponent), @@ -66,6 +67,9 @@ export class TenantProfileAutocompleteComponent implements ControlValueAccessor, @Input() disabled: boolean; + @Input() + showDetailsPageLink = false; + @Output() tenantProfileUpdated = new EventEmitter(); @@ -74,6 +78,7 @@ export class TenantProfileAutocompleteComponent implements ControlValueAccessor, filteredTenantProfiles: Observable>; searchText = ''; + tenantProfileURL: string; private dirty = false; @@ -123,6 +128,7 @@ export class TenantProfileAutocompleteComponent implements ControlValueAccessor, (profile) => { if (profile) { this.modelValue = new TenantProfileId(profile.id.id); + this.tenantProfileURL = getEntityDetailsPageURL(this.modelValue.id, this.modelValue.entityType); this.selectTenantProfileFormGroup.get('tenantProfile').patchValue(profile, {emitEvent: false}); this.propagateChange(this.modelValue); } @@ -133,6 +139,11 @@ export class TenantProfileAutocompleteComponent implements ControlValueAccessor, setDisabledState(isDisabled: boolean): void { this.disabled = isDisabled; + if (this.disabled) { + this.selectTenantProfileFormGroup.disable(); + } else { + this.selectTenantProfileFormGroup.enable(); + } } writeValue(value: TenantProfileId | null): void { @@ -141,6 +152,7 @@ export class TenantProfileAutocompleteComponent implements ControlValueAccessor, this.tenantProfileService.getTenantProfileInfo(value.id).subscribe( (profile) => { this.modelValue = new TenantProfileId(profile.id.id); + this.tenantProfileURL = getEntityDetailsPageURL(this.modelValue.id, this.modelValue.entityType); this.selectTenantProfileFormGroup.get('tenantProfile').patchValue(profile, {emitEvent: false}); } ); diff --git a/ui-ngx/src/app/modules/home/components/profile/tenant-profile.component.html b/ui-ngx/src/app/modules/home/components/profile/tenant-profile.component.html index e5a3a54293..f98d0b6a80 100644 --- a/ui-ngx/src/app/modules/home/components/profile/tenant-profile.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/tenant-profile.component.html @@ -16,6 +16,12 @@ -->
+ + diff --git a/ui-ngx/src/app/shared/components/ota-package/ota-package-autocomplete.component.scss b/ui-ngx/src/app/shared/components/ota-package/ota-package-autocomplete.component.scss new file mode 100644 index 0000000000..38af76b016 --- /dev/null +++ b/ui-ngx/src/app/shared/components/ota-package/ota-package-autocomplete.component.scss @@ -0,0 +1,21 @@ +/** + * Copyright © 2016-2021 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. + */ +:host{ + .mat-icon-button a { + border-bottom: none; + color: inherit; + } +} diff --git a/ui-ngx/src/app/shared/components/ota-package/ota-package-autocomplete.component.ts b/ui-ngx/src/app/shared/components/ota-package/ota-package-autocomplete.component.ts index 6609d4ceb7..4d22d1f298 100644 --- a/ui-ngx/src/app/shared/components/ota-package/ota-package-autocomplete.component.ts +++ b/ui-ngx/src/app/shared/components/ota-package/ota-package-autocomplete.component.ts @@ -31,12 +31,13 @@ import { OtaPackageInfo, OtaUpdateTranslation, OtaUpdateType } from '@shared/mod import { OtaPackageService } from '@core/http/ota-package.service'; import { PageLink } from '@shared/models/page/page-link'; import { Direction } from '@shared/models/page/sort-order'; -import { emptyPageData } from "@shared/models/page/page-data"; +import { emptyPageData } from '@shared/models/page/page-data'; +import { getEntityDetailsPageURL } from '@core/utils'; @Component({ selector: 'tb-ota-package-autocomplete', templateUrl: './ota-package-autocomplete.component.html', - styleUrls: [], + styleUrls: ['./ota-package-autocomplete.component.scss'], providers: [{ provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => OtaPackageAutocompleteComponent), @@ -64,6 +65,9 @@ export class OtaPackageAutocompleteComponent implements ControlValueAccessor, On @Input() useFullEntityId = false; + @Input() + showDetailsPageLink = false; + private requiredValue: boolean; get required(): boolean { @@ -83,6 +87,7 @@ export class OtaPackageAutocompleteComponent implements ControlValueAccessor, On filteredPackages: Observable>; searchText = ''; + packageURL: string; private dirty = false; @@ -166,6 +171,7 @@ export class OtaPackageAutocompleteComponent implements ControlValueAccessor, On if (packageId !== '') { this.entityService.getEntity(EntityType.OTA_PACKAGE, packageId, {ignoreLoading: true, ignoreErrors: true}).subscribe( (entity) => { + this.packageURL = getEntityDetailsPageURL(entity.id.id, EntityType.OTA_PACKAGE); this.modelValue = this.useFullEntityId ? entity.id : entity.id.id; this.otaPackageFormGroup.get('packageId').patchValue(entity, {emitEvent: false}); }, diff --git a/ui-ngx/src/app/shared/models/constants.ts b/ui-ngx/src/app/shared/models/constants.ts index 9bf120f8b7..fa73e0d40e 100644 --- a/ui-ngx/src/app/shared/models/constants.ts +++ b/ui-ngx/src/app/shared/models/constants.ts @@ -16,6 +16,7 @@ import { InjectionToken } from '@angular/core'; import { IModulesMap } from '@modules/common/modules-map.models'; +import { AliasEntityType, EntityType } from '@shared/models/entity-type.models'; export const Constants = { serverErrorCode: { @@ -137,6 +138,20 @@ export const HelpLinks = { } }; +export const baseDetailsPageByEntityType = new Map([ + [EntityType.TENANT, '/tenants'], + [EntityType.TENANT_PROFILE, '/tenantProfiles'], + [EntityType.CUSTOMER, '/customers'], + [EntityType.USER, '/users'], + [EntityType.ASSET, '/assets'], + [EntityType.DEVICE, '/devices'], + [EntityType.DEVICE_PROFILE, '/deviceProfiles'], + [EntityType.EDGE, '/edgeInstances'], + [EntityType.ENTITY_VIEW, '/entityViews'], + [EntityType.TB_RESOURCE, '/settings/resources-library'], + [EntityType.OTA_PACKAGE, '/otaUpdates'] +]); + export interface ValueTypeData { name: string; icon: string; diff --git a/ui-ngx/src/app/shared/models/relation.models.ts b/ui-ngx/src/app/shared/models/relation.models.ts index c9037a5dab..b1601753d8 100644 --- a/ui-ngx/src/app/shared/models/relation.models.ts +++ b/ui-ngx/src/app/shared/models/relation.models.ts @@ -89,4 +89,5 @@ export interface EntityRelationInfo extends EntityRelation { toEntityTypeName?: string; toName: string; fromEntityTypeName?: string; + entityURL?: string; } diff --git a/ui-ngx/src/app/shared/shared.module.ts b/ui-ngx/src/app/shared/shared.module.ts index 086768c854..50975b9f11 100644 --- a/ui-ngx/src/app/shared/shared.module.ts +++ b/ui-ngx/src/app/shared/shared.module.ts @@ -455,6 +455,7 @@ export function MarkedOptionsFactory(markedOptionsService: MarkedOptionsService) KeyboardShortcutPipe, FileSizePipe, SelectableColumnsPipe, + RouterModule, TranslateModule, JsonObjectEditDialogComponent, HistorySelectorComponent, diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index 807f3781a9..4d3a51d087 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -622,7 +622,8 @@ "enter-search": "Enter search", "created-time": "Created time", "loading": "Loading...", - "proceed": "Proceed" + "proceed": "Proceed", + "open-details-page": "Open details page" }, "content-type": { "json": "Json", From b58b3a303b4861f71635faa626eaa9d81021bec2 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Tue, 28 Dec 2021 18:37:37 +0200 Subject: [PATCH 033/798] UI: Added support delete entity in details page --- .../entity/entity-details-page.component.ts | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/ui-ngx/src/app/modules/home/components/entity/entity-details-page.component.ts b/ui-ngx/src/app/modules/home/components/entity/entity-details-page.component.ts index 27ed899c79..c2878680e5 100644 --- a/ui-ngx/src/app/modules/home/components/entity/entity-details-page.component.ts +++ b/ui-ngx/src/app/modules/home/components/entity/entity-details-page.component.ts @@ -35,6 +35,7 @@ import { TranslateService } from '@ngx-translate/core'; import { deepClone, mergeDeep } from '@core/utils'; import { BroadcastService } from '@core/services/broadcast.service'; import { EntityDetailsPanelComponent } from '@home/components/entity/entity-details-panel.component'; +import { DialogService } from '@core/services/dialog.service'; @Component({ selector: 'tb-entity-details-page', @@ -72,6 +73,7 @@ export class EntityDetailsPageComponent extends EntityDetailsPanelComponent impl protected componentFactoryResolver: ComponentFactoryResolver, private broadcast: BroadcastService, private translate: TranslateService, + private dialogService: DialogService, protected store: Store) { super(store, injector, cd, componentFactoryResolver); this.entitiesTableConfig = this.route.snapshot.data.entitiesTableConfig; @@ -85,6 +87,11 @@ export class EntityDetailsPageComponent extends EntityDetailsPanelComponent impl this.headerSubtitle = this.translate.instant(this.entitiesTableConfig.entityTranslations.details); super.init(); this.entityComponent.isDetailsPage = true; + this.subscriptions.push(this.entityAction.subscribe((action) => { + if (action.action === 'delete') { + this.deleteEntity(action.event, action.entity); + } + })); } ngOnDestroy() { @@ -151,4 +158,25 @@ export class EntityDetailsPageComponent extends EntityDetailsPanelComponent impl confirmForm(): FormGroup { return this.detailsForm; } + + private deleteEntity($event: Event, entity: BaseData) { + if ($event) { + $event.stopPropagation(); + } + this.dialogService.confirm( + this.entitiesTableConfig.deleteEntityTitle(entity), + this.entitiesTableConfig.deleteEntityContent(entity), + this.translate.instant('action.no'), + this.translate.instant('action.yes'), + true + ).subscribe((result) => { + if (result) { + this.entitiesTableConfig.deleteEntity(entity.id).subscribe( + () => { + this.router.navigate(['../'], {relativeTo: this.route}); + } + ); + } + }); + } } From d7f9b1d430fbb9009346da5458c522a862cd1b5e Mon Sep 17 00:00:00 2001 From: ArtemDzhereleiko Date: Wed, 29 Dec 2021 12:59:39 +0200 Subject: [PATCH 034/798] UI: Disable column sort if enable post processing func --- .../widget/lib/entities-table-widget.component.html | 2 +- .../components/widget/lib/entities-table-widget.component.ts | 4 ++++ .../home/components/widget/lib/table-widget.models.ts | 1 + .../widget/lib/timeseries-table-widget.component.html | 2 +- .../widget/lib/timeseries-table-widget.component.ts | 5 ++++- 5 files changed, 11 insertions(+), 3 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/entities-table-widget.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/entities-table-widget.component.html index 74ccd53ec2..bfb775306b 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/entities-table-widget.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/entities-table-widget.component.html @@ -41,7 +41,7 @@
- {{ column.title }} + {{ column.title }} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/entities-table-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/entities-table-widget.component.ts index bd52f2ab9b..a2e4f7b922 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/entities-table-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/entities-table-widget.component.ts @@ -324,6 +324,7 @@ export class EntitiesTableWidgetComponent extends PageComponent implements OnIni label: 'entityName', def: 'entityName', title: entityNameColumnTitle, + sortable: true, entityKey: { key: 'name', type: EntityKeyType.ENTITY_FIELD @@ -347,6 +348,7 @@ export class EntitiesTableWidgetComponent extends PageComponent implements OnIni label: 'entityLabel', def: 'entityLabel', title: entityLabelColumnTitle, + sortable: true, entityKey: { key: 'label', type: EntityKeyType.ENTITY_FIELD @@ -370,6 +372,7 @@ export class EntitiesTableWidgetComponent extends PageComponent implements OnIni label: 'entityType', def: 'entityType', title: this.translate.instant('entity.entity-type'), + sortable: true, entityKey: { key: 'entityType', type: EntityKeyType.ENTITY_FIELD @@ -403,6 +406,7 @@ export class EntitiesTableWidgetComponent extends PageComponent implements OnIni dataKey.label = this.utils.customTranslation(dataKey.label, dataKey.label); dataKey.title = dataKey.label; dataKey.def = 'def' + this.columns.length; + dataKey.sortable = !dataKey.usePostProcessing; const keySettings: TableWidgetDataKeySettings = dataKey.settings; if (dataKey.type === DataKeyType.entityField && !isDefined(keySettings.columnWidth) || keySettings.columnWidth === '0px') { diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/table-widget.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/table-widget.models.ts index f90c287111..1d798a88ca 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/table-widget.models.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/table-widget.models.ts @@ -73,6 +73,7 @@ export interface EntityData { export interface EntityColumn extends DataKey { def: string; title: string; + sortable: boolean; entityKey?: EntityKey; } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/timeseries-table-widget.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/timeseries-table-widget.component.html index 8c3b9084b0..d209a649b6 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/timeseries-table-widget.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/timeseries-table-widget.component.html @@ -52,7 +52,7 @@ - {{ h.dataKey.label }} + {{ h.dataKey.label }} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/timeseries-table-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/timeseries-table-widget.component.ts index de8cec8dc7..198e36c919 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/timeseries-table-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/timeseries-table-widget.component.ts @@ -88,6 +88,7 @@ interface TimeseriesRow { interface TimeseriesHeader { index: number; dataKey: DataKey; + sortable: boolean; } interface TimeseriesTableSource { @@ -308,10 +309,12 @@ export class TimeseriesTableWidgetComponent extends PageComponent implements OnI for (let a = 0; a < datasource.dataKeys.length; a++ ) { const dataKey = datasource.dataKeys[a]; const keySettings: TableWidgetDataKeySettings = dataKey.settings; + const sortable = !dataKey.usePostProcessing; const index = a + 1; source.header.push({ index, - dataKey + dataKey, + sortable }); source.displayedColumns.push(index + ''); source.rowDataTemplate[dataKey.label] = null; From b53746bda6bf246f5ad9c28f31b746de739f068a Mon Sep 17 00:00:00 2001 From: desoliture Date: Tue, 28 Dec 2021 16:38:05 +0200 Subject: [PATCH 035/798] MQTT Gateway API attributes request fix --- .../transport/mqtt/adaptors/JsonMqttAdaptor.java | 12 +++++++++--- .../mqtt/session/GatewayDeviceSessionCtx.java | 6 ++++++ .../mqtt/session/GatewaySessionHandler.java | 6 ++++++ .../common/transport/adaptor/JsonConverter.java | 15 +++++++++------ 4 files changed, 30 insertions(+), 9 deletions(-) diff --git a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/JsonMqttAdaptor.java b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/JsonMqttAdaptor.java index dc2b74a8ea..0548fa08fb 100644 --- a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/JsonMqttAdaptor.java +++ b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/JsonMqttAdaptor.java @@ -34,6 +34,7 @@ import org.thingsboard.server.common.data.ota.OtaPackageType; import org.thingsboard.server.common.transport.adaptor.AdaptorException; import org.thingsboard.server.common.transport.adaptor.JsonConverter; import org.thingsboard.server.gen.transport.TransportProtos; +import org.thingsboard.server.transport.mqtt.session.GatewayDeviceSessionCtx; import org.thingsboard.server.transport.mqtt.session.MqttDeviceAwareSessionContext; import java.nio.charset.Charset; @@ -123,7 +124,12 @@ public class JsonMqttAdaptor implements MqttTransportAdaptor { @Override public Optional convertToGatewayPublish(MqttDeviceAwareSessionContext ctx, String deviceName, TransportProtos.GetAttributeResponseMsg responseMsg) throws AdaptorException { - return processConvertFromGatewayAttributeResponseMsg(ctx, deviceName, responseMsg); + JsonObject request = ((GatewayDeviceSessionCtx) ctx) + .getPendingAttributesRequests() + .getOrDefault(responseMsg.getRequestId(), new JsonObject()); + boolean multipleAttrKeysRequested = + request.has("keys") && request.get("keys").getAsJsonArray().size() > 1; + return processConvertFromGatewayAttributeResponseMsg(ctx, deviceName, responseMsg, multipleAttrKeysRequested); } @Override @@ -232,11 +238,11 @@ public class JsonMqttAdaptor implements MqttTransportAdaptor { } } - private Optional processConvertFromGatewayAttributeResponseMsg(MqttDeviceAwareSessionContext ctx, String deviceName, TransportProtos.GetAttributeResponseMsg responseMsg) throws AdaptorException { + private Optional processConvertFromGatewayAttributeResponseMsg(MqttDeviceAwareSessionContext ctx, String deviceName, TransportProtos.GetAttributeResponseMsg responseMsg, boolean multipleAttrKeysRequested) throws AdaptorException { if (!StringUtils.isEmpty(responseMsg.getError())) { throw new AdaptorException(responseMsg.getError()); } else { - JsonObject result = JsonConverter.getJsonObjectForGateway(deviceName, responseMsg); + JsonObject result = JsonConverter.getJsonObjectForGateway(deviceName, responseMsg, multipleAttrKeysRequested); return Optional.of(createMqttPublishMsg(ctx, MqttTopics.GATEWAY_ATTRIBUTES_RESPONSE_TOPIC, result)); } } diff --git a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewayDeviceSessionCtx.java b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewayDeviceSessionCtx.java index f41c5668cd..593205b745 100644 --- a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewayDeviceSessionCtx.java +++ b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewayDeviceSessionCtx.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.transport.mqtt.session; +import com.google.gson.JsonObject; import io.netty.channel.ChannelFuture; import io.netty.handler.codec.mqtt.MqttMessage; import lombok.extern.slf4j.Slf4j; @@ -27,6 +28,7 @@ import org.thingsboard.server.common.transport.auth.TransportDeviceInfo; import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto; +import java.util.Map; import java.util.UUID; import java.util.concurrent.ConcurrentMap; @@ -136,6 +138,10 @@ public class GatewayDeviceSessionCtx extends MqttDeviceAwareSessionContext imple // This feature is not supported in the TB IoT Gateway yet. } + public Map getPendingAttributesRequests() { + return parent.getPendingAttributesRequests(); + } + private boolean isAckExpected(MqttMessage message) { return message.fixedHeader().qosLevel().value() > 0; } diff --git a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionHandler.java b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionHandler.java index 7b45b5f9d9..4b4e986d5f 100644 --- a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionHandler.java +++ b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionHandler.java @@ -89,6 +89,7 @@ public class GatewaySessionHandler { private final ConcurrentMap mqttQoSMap; private final ChannelHandlerContext channel; private final DeviceSessionCtx deviceSessionCtx; + private final Map pendingAttributesRequests = new ConcurrentHashMap<>(); public GatewaySessionHandler(DeviceSessionCtx deviceSessionCtx, UUID sessionId) { this.context = deviceSessionCtx.getContext(); @@ -107,6 +108,10 @@ public class GatewaySessionHandler { return new ConcurrentReferenceHashMap<>(16, ReferenceType.WEAK); } + public Map getPendingAttributesRequests () { + return pendingAttributesRequests; + } + public void onDeviceConnect(MqttPublishMessage mqttMsg) throws AdaptorException { if (isJsonPayloadType()) { onDeviceConnectJson(mqttMsg); @@ -558,6 +563,7 @@ public class GatewaySessionHandler { if (json.isJsonObject()) { JsonObject jsonObj = json.getAsJsonObject(); int requestId = jsonObj.get("id").getAsInt(); + pendingAttributesRequests.put(requestId, jsonObj); String deviceName = jsonObj.get(DEVICE_PROPERTY).getAsString(); boolean clientScope = jsonObj.get("client").getAsBoolean(); Set keys; diff --git a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/adaptor/JsonConverter.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/adaptor/JsonConverter.java index be4143e388..86f90d786f 100644 --- a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/adaptor/JsonConverter.java +++ b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/adaptor/JsonConverter.java @@ -313,16 +313,19 @@ public class JsonConverter { return result; } - public static JsonObject getJsonObjectForGateway(String deviceName, TransportProtos.GetAttributeResponseMsg - responseMsg) { + public static JsonObject getJsonObjectForGateway( + String deviceName, + TransportProtos.GetAttributeResponseMsg responseMsg, + boolean multipleAttrKeysRequested + ) { JsonObject result = new JsonObject(); result.addProperty("id", responseMsg.getRequestId()); result.addProperty(DEVICE_PROPERTY, deviceName); if (responseMsg.getClientAttributeListCount() > 0) { - addValues(result, responseMsg.getClientAttributeListList()); + addValues(result, responseMsg.getClientAttributeListList(), multipleAttrKeysRequested); } if (responseMsg.getSharedAttributeListCount() > 0) { - addValues(result, responseMsg.getSharedAttributeListList()); + addValues(result, responseMsg.getSharedAttributeListList(), multipleAttrKeysRequested); } return result; } @@ -335,8 +338,8 @@ public class JsonConverter { return result; } - private static void addValues(JsonObject result, List kvList) { - if (kvList.size() == 1) { + private static void addValues(JsonObject result, List kvList, boolean multipleAttrKeysRequested) { + if (kvList.size() == 1 && !multipleAttrKeysRequested) { addValueToJson(result, "value", kvList.get(0).getKv()); } else { JsonObject values; From 2f5648c400122b02838ea11d650dc55056dc860c Mon Sep 17 00:00:00 2001 From: desoliture Date: Wed, 29 Dec 2021 17:22:32 +0200 Subject: [PATCH 036/798] refactoring --- .../server/transport/mqtt/adaptors/JsonMqttAdaptor.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/JsonMqttAdaptor.java b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/JsonMqttAdaptor.java index 0548fa08fb..7aea8f3f91 100644 --- a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/JsonMqttAdaptor.java +++ b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/JsonMqttAdaptor.java @@ -43,6 +43,7 @@ import java.util.Arrays; import java.util.HashSet; import java.util.Optional; import java.util.Set; +import java.util.Map; import java.util.UUID; import static org.thingsboard.server.common.data.device.profile.MqttTopics.DEVICE_SOFTWARE_FIRMWARE_RESPONSES_TOPIC_FORMAT; @@ -124,11 +125,12 @@ public class JsonMqttAdaptor implements MqttTransportAdaptor { @Override public Optional convertToGatewayPublish(MqttDeviceAwareSessionContext ctx, String deviceName, TransportProtos.GetAttributeResponseMsg responseMsg) throws AdaptorException { - JsonObject request = ((GatewayDeviceSessionCtx) ctx) - .getPendingAttributesRequests() - .getOrDefault(responseMsg.getRequestId(), new JsonObject()); + Map pendingAttributesRequests = ((GatewayDeviceSessionCtx) ctx).getPendingAttributesRequests(); + int requestId = responseMsg.getRequestId(); + JsonObject request = pendingAttributesRequests.getOrDefault(requestId, new JsonObject()); boolean multipleAttrKeysRequested = request.has("keys") && request.get("keys").getAsJsonArray().size() > 1; + pendingAttributesRequests.remove(requestId); return processConvertFromGatewayAttributeResponseMsg(ctx, deviceName, responseMsg, multipleAttrKeysRequested); } From b82c27f4f725d29e27a9bbc24d0d5f6b2d783c7b Mon Sep 17 00:00:00 2001 From: zbeacon Date: Wed, 29 Dec 2021 17:39:11 +0200 Subject: [PATCH 037/798] Refactored to use persistent RPC --- .../DefaultGatewayDeviceStateService.java | 200 +++++------------- .../GatewayDeviceStateService.java | 2 - .../queue/DefaultTbClusterService.java | 11 +- .../rpc/DefaultTbCoreDeviceRpcService.java | 7 +- .../transport/DefaultTransportApiService.java | 6 - .../device-wizard-dialog.component.html | 6 - .../wizard/device-wizard-dialog.component.ts | 2 - .../home/pages/device/device.component.html | 6 - .../home/pages/device/device.component.ts | 2 - .../assets/locale/locale.constant-en_US.json | 1 - 10 files changed, 61 insertions(+), 182 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/gateway_device/DefaultGatewayDeviceStateService.java b/application/src/main/java/org/thingsboard/server/service/gateway_device/DefaultGatewayDeviceStateService.java index 57a0b36508..84b9f977ba 100644 --- a/application/src/main/java/org/thingsboard/server/service/gateway_device/DefaultGatewayDeviceStateService.java +++ b/application/src/main/java/org/thingsboard/server/service/gateway_device/DefaultGatewayDeviceStateService.java @@ -15,200 +15,98 @@ */ package org.thingsboard.server.service.gateway_device; -import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.ObjectNode; -import com.google.common.util.concurrent.FutureCallback; -import com.google.common.util.concurrent.ListenableFuture; +import com.fasterxml.jackson.databind.node.TextNode; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.checkerframework.checker.nullness.qual.Nullable; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; -import org.thingsboard.common.util.DonAsynchron; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.kv.AttributeKvEntry; -import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; -import org.thingsboard.server.common.data.kv.JsonDataEntry; -import org.thingsboard.server.common.data.kv.KvEntry; import org.thingsboard.server.common.data.relation.EntityRelation; import org.thingsboard.server.common.data.relation.RelationTypeGroup; -import org.thingsboard.server.dao.attributes.AttributesService; +import org.thingsboard.server.common.data.rpc.ToDeviceRpcRequestBody; +import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest; import org.thingsboard.server.dao.device.DeviceService; import org.thingsboard.server.dao.relation.RelationService; -import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService; +import org.thingsboard.server.service.rpc.TbCoreDeviceRpcService; -import java.util.Collections; import java.util.List; -import java.util.Map; -import java.util.concurrent.atomic.AtomicBoolean; +import java.util.UUID; @Slf4j @Service @RequiredArgsConstructor public class DefaultGatewayDeviceStateService implements GatewayDeviceStateService { - private static final String RENAMED_GATEWAY_DEVICES = "renamedGatewayDevices"; - private static final String DELETED_GATEWAY_DEVICES = "deletedGatewayDevices"; - private static final String HANDLE_DEVICE_RENAMING_PARAMETER = "handleDeviceRenaming"; + private final static String DEVICE_RENAMED_METHOD_NAME = "gateway_device_renamed"; + private final static String DEVICE_DELETED_METHOD_NAME = "gateway_device_deleted"; - private final AttributesService attributesService; - private final RelationService relationService; - private final DeviceService deviceService; - @Lazy - @Autowired - private TelemetrySubscriptionService tsSubService; - @Override - public void update(Device device, Device oldDevice) { - List relationToGatewayList = relationService.findByFromAndType(TenantId.SYS_TENANT_ID, device.getId(), DataConstants.LAST_CONNECTED_GATEWAY, RelationTypeGroup.COMMON); - if (!relationToGatewayList.isEmpty()) { - if (oldDevice != null) { - EntityRelation relationToGateway = relationToGatewayList.get(0); - - Device gatewayDevice = deviceService.findDeviceById(device.getTenantId(), (DeviceId) relationToGateway.getTo()); - if (isHandleDeviceRenamingEnabled(gatewayDevice.getAdditionalInfo())) { - ListenableFuture> renamedGatewayDevicesFuture = attributesService.find(device.getTenantId(), relationToGateway.getTo(), DataConstants.SHARED_SCOPE, Collections.singletonList("renamedGatewayDevices")); - DonAsynchron.withCallback(renamedGatewayDevicesFuture, renamedGatewayDevicesList -> { - ObjectNode renamedGatewayDevicesNode; - KvEntry renamedGatewayDevicesKvEntry; - String newDeviceName = device.getName(); - String oldDeviceName = oldDevice.getName(); + @Value("${server.rest.server_side_rpc.min_timeout:5000}") + protected long minTimeout; - if (renamedGatewayDevicesList.isEmpty()) { - renamedGatewayDevicesNode = JacksonUtil.newObjectNode(); - renamedGatewayDevicesNode.put(oldDeviceName, newDeviceName); - } else { - AttributeKvEntry receivedRenamedGatewayDevicesAttribute = renamedGatewayDevicesList.get(0); - renamedGatewayDevicesNode = (ObjectNode) JacksonUtil.toJsonNode(receivedRenamedGatewayDevicesAttribute.getValueAsString()); - if (renamedGatewayDevicesNode.findValue(newDeviceName) != null && oldDeviceName.equals(renamedGatewayDevicesNode.get(newDeviceName).asText())) { - // If a new device name is the same like the first name or another device was renamed like some existing device - renamedGatewayDevicesNode.remove(newDeviceName); - } else { + @Value("${server.rest.server_side_rpc.default_timeout:10000}") + protected long defaultTimeout; - AtomicBoolean renamedFirstTime = new AtomicBoolean(true); + private final RelationService relationService; + private final DeviceService deviceService; - renamedGatewayDevicesNode.fields().forEachRemaining(entry -> { - // If device was renamed earlier - if (oldDeviceName.equals(entry.getValue().asText())) { - renamedGatewayDevicesNode.put(entry.getKey(), newDeviceName); - renamedFirstTime.set(false); - } - }); - if (renamedFirstTime.get()) { - renamedGatewayDevicesNode.put(oldDeviceName, newDeviceName); - } - } - } + @Lazy + @Autowired + private TbCoreDeviceRpcService deviceRpcService; - renamedGatewayDevicesKvEntry = new JsonDataEntry(RENAMED_GATEWAY_DEVICES, JacksonUtil.toString(renamedGatewayDevicesNode)); - saveGatewayDevicesAttribute(device, relationToGateway, renamedGatewayDevicesKvEntry); - }, - e -> log.error("Cannot get gateway renamed devices attribute", e)); - } - } - } - } @Override - public void delete(Device device) { + public void update(Device device, Device oldDevice) { List relationToGatewayList = relationService.findByFromAndType(TenantId.SYS_TENANT_ID, device.getId(), DataConstants.LAST_CONNECTED_GATEWAY, RelationTypeGroup.COMMON); if (!relationToGatewayList.isEmpty()) { EntityRelation relationToGateway = relationToGatewayList.get(0); - final String[] deletedDeviceName = {device.getName()}; - ListenableFuture> renamedGatewayDevicesFuture = attributesService.find(device.getTenantId(), relationToGateway.getTo(), DataConstants.SHARED_SCOPE, Collections.singletonList("renamedGatewayDevices")); - DonAsynchron.withCallback(renamedGatewayDevicesFuture, renamedGatewayDevicesList -> { - if (!renamedGatewayDevicesList.isEmpty()) { - ObjectNode renamedGatewayDevicesNode = (ObjectNode) JacksonUtil.toJsonNode(renamedGatewayDevicesList.get(0).getValueAsString()); - if (renamedGatewayDevicesNode != null) { - AtomicBoolean renamedListChanged = new AtomicBoolean(false); - if (renamedGatewayDevicesNode.findValue(deletedDeviceName[0]) != null) { - renamedGatewayDevicesNode.remove(deletedDeviceName[0]); - renamedListChanged.set(true); - } - Map renamedGatewayDevicesMap = JacksonUtil.OBJECT_MAPPER.convertValue(renamedGatewayDevicesNode, new TypeReference<>() { - }); - renamedGatewayDevicesMap.forEach((key, value) -> { - // If device was renamed earlier - if (deletedDeviceName[0].equals(value)) { - renamedGatewayDevicesNode.remove(key); - deletedDeviceName[0] = key; - renamedListChanged.set(true); - } - }); - if (renamedListChanged.get()) { - KvEntry renamedGatewayDevicesKvEntry = new JsonDataEntry(RENAMED_GATEWAY_DEVICES, JacksonUtil.toString(renamedGatewayDevicesNode)); - saveGatewayDevicesAttribute(device, relationToGateway, renamedGatewayDevicesKvEntry); - } - } - } - }, e -> log.error("Cannot get gateway renamed devices attribute", e)); - ListenableFuture> deletedGatewayDevicesFuture = attributesService.find(device.getTenantId(), relationToGateway.getTo(), DataConstants.SHARED_SCOPE, Collections.singletonList("deletedGatewayDevices")); - DonAsynchron.withCallback(deletedGatewayDevicesFuture, deletedGatewayDevicesList -> { - ArrayNode deletedGatewayDevicesNode; - if (!deletedGatewayDevicesList.isEmpty()) { - deletedGatewayDevicesNode = (ArrayNode) JacksonUtil.toJsonNode(deletedGatewayDevicesList.get(0).getValueAsString()); - } else { - deletedGatewayDevicesNode = JacksonUtil.OBJECT_MAPPER.createArrayNode(); - } - deletedGatewayDevicesNode.add(deletedDeviceName[0]); - KvEntry deletedGatewayDevicesKvEntry = new JsonDataEntry(DELETED_GATEWAY_DEVICES, JacksonUtil.toString(deletedGatewayDevicesNode)); - saveGatewayDevicesAttribute(device, relationToGateway, deletedGatewayDevicesKvEntry); - }, e -> log.error("Cannot get gateway deleted devices attribute", e)); + Device gatewayDevice = deviceService.findDeviceById(device.getTenantId(), (DeviceId) relationToGateway.getTo()); + ObjectNode renamedDeviceNode = JacksonUtil.newObjectNode(); + renamedDeviceNode.put(device.getName(), oldDevice.getName()); + ToDeviceRpcRequest rpcRequest = formDeviceToGatewayRPCRequest(gatewayDevice, renamedDeviceNode, DEVICE_RENAMED_METHOD_NAME); + deviceRpcService.processRestApiRpcRequest(rpcRequest, fromDeviceRpcResponse -> { + log.trace("Device renamed RPC with id: [{}] processed to gateway device with id: [{}], old device name: [{}], new device name: [{}]", + rpcRequest.getId(), gatewayDevice.getId(), oldDevice.getName(), device.getName()); + }, null); } } @Override - public void checkAndUpdateDeletedGatewayDevicesAttribute(Device device) { + public void delete(Device device) { List relationToGatewayList = relationService.findByFromAndType(TenantId.SYS_TENANT_ID, device.getId(), DataConstants.LAST_CONNECTED_GATEWAY, RelationTypeGroup.COMMON); if (!relationToGatewayList.isEmpty()) { EntityRelation relationToGateway = relationToGatewayList.get(0); - ListenableFuture> deletedGatewayDevicesFuture = attributesService.find(device.getTenantId(), relationToGateway.getTo(), DataConstants.SHARED_SCOPE, Collections.singletonList("deletedGatewayDevices")); - DonAsynchron.withCallback(deletedGatewayDevicesFuture, deletedGatewayDevicesList -> { - ArrayNode deletedGatewayDevicesNode; - if (!deletedGatewayDevicesList.isEmpty()) { - int deletedDeviceIndex = -1; - deletedGatewayDevicesNode = (ArrayNode) JacksonUtil.toJsonNode(deletedGatewayDevicesList.get(0).getValueAsString()); - for (int i = 0; i < deletedGatewayDevicesNode.size(); i++) { - if (deletedGatewayDevicesNode.get(i).asText().equals(device.getName())) { - deletedDeviceIndex = i; - } - } - if (deletedDeviceIndex != -1) { - deletedGatewayDevicesNode.remove(deletedDeviceIndex); - KvEntry deletedGatewayDevicesKvEntry = new JsonDataEntry(DELETED_GATEWAY_DEVICES, JacksonUtil.toString(deletedGatewayDevicesNode)); - saveGatewayDevicesAttribute(device, relationToGateway, deletedGatewayDevicesKvEntry); - } - } - }, e -> log.error("Cannot get gateway deleted devices attribute", e)); + Device gatewayDevice = deviceService.findDeviceById(device.getTenantId(), (DeviceId) relationToGateway.getTo()); + TextNode deletedDeviceNode = new TextNode(device.getName()); + ToDeviceRpcRequest rpcRequest = formDeviceToGatewayRPCRequest(gatewayDevice, deletedDeviceNode, DEVICE_DELETED_METHOD_NAME); + deviceRpcService.processRestApiRpcRequest(rpcRequest, fromDeviceRpcResponse -> { + log.trace("Device deleted RPC with id: [{}] processed to gateway device with id: [{}], deleted device name: [{}]", + rpcRequest.getId(), gatewayDevice.getId(), device.getName()); + }, null); } } - private void saveGatewayDevicesAttribute(Device device, EntityRelation relationToGateway, KvEntry gatewayDevicesKvEntry) { - AttributeKvEntry attrKvEntry = new BaseAttributeKvEntry(System.currentTimeMillis(), gatewayDevicesKvEntry); - tsSubService.saveAndNotify(device.getTenantId(), relationToGateway.getTo(), DataConstants.SHARED_SCOPE, List.of(attrKvEntry), true, new FutureCallback() { - @Override - public void onSuccess(@Nullable Void unused) { - log.trace("Attribute saved for gateway with ID [{}] and data [{}]", relationToGateway.getTo(), gatewayDevicesKvEntry.getJsonValue()); - } - - @Override - public void onFailure(Throwable throwable) { - log.error("Cannot save gateway device attribute", throwable); - } - }); - } - - private boolean isHandleDeviceRenamingEnabled(JsonNode additionalInfo) { - if (additionalInfo.get(HANDLE_DEVICE_RENAMING_PARAMETER) != null) { - return additionalInfo.get(HANDLE_DEVICE_RENAMING_PARAMETER).asBoolean(); - } - return false; + private ToDeviceRpcRequest formDeviceToGatewayRPCRequest(Device gatewayDevice, JsonNode deviceDataNode, String method) { + ToDeviceRpcRequestBody body = new ToDeviceRpcRequestBody(method, JacksonUtil.toString(deviceDataNode)); + long expTime = System.currentTimeMillis() + Math.max(minTimeout, defaultTimeout); + UUID rpcRequestUUID = UUID.randomUUID(); + return new ToDeviceRpcRequest(rpcRequestUUID, + gatewayDevice.getTenantId(), + gatewayDevice.getId(), + true, + expTime, + body, + true, + null, + null + ); } -} \ No newline at end of file +} diff --git a/application/src/main/java/org/thingsboard/server/service/gateway_device/GatewayDeviceStateService.java b/application/src/main/java/org/thingsboard/server/service/gateway_device/GatewayDeviceStateService.java index d4079487a5..1c06967cff 100644 --- a/application/src/main/java/org/thingsboard/server/service/gateway_device/GatewayDeviceStateService.java +++ b/application/src/main/java/org/thingsboard/server/service/gateway_device/GatewayDeviceStateService.java @@ -22,6 +22,4 @@ public interface GatewayDeviceStateService { void update(Device device, Device oldDevice); void delete(Device device); - - void checkAndUpdateDeletedGatewayDevicesAttribute(Device device); } diff --git a/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbClusterService.java b/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbClusterService.java index be44c9ee40..4ffc782803 100644 --- a/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbClusterService.java +++ b/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbClusterService.java @@ -401,13 +401,18 @@ public class DefaultTbClusterService implements TbClusterService { public void onDeviceUpdated(Device device, Device old, boolean notifyEdge) { var created = old == null; broadcastEntityChangeToTransport(device.getTenantId(), device.getId(), device, null); - if (old != null && (!device.getName().equals(old.getName()) || !device.getType().equals(old.getType()))) { - pushMsgToCore(new DeviceNameOrTypeUpdateMsg(device.getTenantId(), device.getId(), device.getName(), device.getType()), null); + if (old != null) { + boolean deviceNameChanged = !device.getName().equals(old.getName()); + if (deviceNameChanged) { + gatewayDeviceStateService.update(device, old); + } + if (deviceNameChanged || !device.getType().equals(old.getType())) { + pushMsgToCore(new DeviceNameOrTypeUpdateMsg(device.getTenantId(), device.getId(), device.getName(), device.getType()), null); + } } broadcastEntityStateChangeEvent(device.getTenantId(), device.getId(), created ? ComponentLifecycleEvent.CREATED : ComponentLifecycleEvent.UPDATED); sendDeviceStateServiceEvent(device.getTenantId(), device.getId(), created, !created, false); otaPackageStateService.update(device, old); - gatewayDeviceStateService.update(device, old); if (!created && notifyEdge) { sendNotificationMsgToEdgeService(device.getTenantId(), null, device.getId(), null, null, EdgeEventActionType.UPDATED); } diff --git a/application/src/main/java/org/thingsboard/server/service/rpc/DefaultTbCoreDeviceRpcService.java b/application/src/main/java/org/thingsboard/server/service/rpc/DefaultTbCoreDeviceRpcService.java index 2b2ade02ee..99e013d6d0 100644 --- a/application/src/main/java/org/thingsboard/server/service/rpc/DefaultTbCoreDeviceRpcService.java +++ b/application/src/main/java/org/thingsboard/server/service/rpc/DefaultTbCoreDeviceRpcService.java @@ -22,10 +22,12 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.thingsboard.common.util.ThingsBoardThreadFactory; -import org.thingsboard.server.common.data.rpc.RpcError; import org.thingsboard.server.actors.ActorSystemContext; +import org.thingsboard.server.cluster.TbClusterService; import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.Device; +import org.thingsboard.server.common.data.User; +import org.thingsboard.server.common.data.rpc.RpcError; import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsgDataType; import org.thingsboard.server.common.msg.TbMsgMetaData; @@ -34,7 +36,6 @@ import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest; import org.thingsboard.server.dao.device.DeviceService; import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; import org.thingsboard.server.queue.util.TbCoreComponent; -import org.thingsboard.server.cluster.TbClusterService; import org.thingsboard.server.service.security.model.SecurityUser; import javax.annotation.PostConstruct; @@ -183,7 +184,7 @@ public class DefaultTbCoreDeviceRpcService implements TbCoreDeviceRpcService { entityNode.put(DataConstants.ADDITIONAL_INFO, msg.getAdditionalInfo()); try { - TbMsg tbMsg = TbMsg.newMsg(DataConstants.RPC_CALL_FROM_SERVER_TO_DEVICE, msg.getDeviceId(), currentUser.getCustomerId(), metaData, TbMsgDataType.JSON, json.writeValueAsString(entityNode)); + TbMsg tbMsg = TbMsg.newMsg(DataConstants.RPC_CALL_FROM_SERVER_TO_DEVICE, msg.getDeviceId(), Optional.ofNullable(currentUser).map(User::getCustomerId).orElse(null), metaData, TbMsgDataType.JSON, json.writeValueAsString(entityNode)); clusterService.pushMsgToRuleEngine(msg.getTenantId(), msg.getDeviceId(), tbMsg, null); } catch (JsonProcessingException e) { throw new RuntimeException(e); diff --git a/application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java b/application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java index 929d87e5f4..04e581ba6b 100644 --- a/application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java +++ b/application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java @@ -95,11 +95,9 @@ import org.thingsboard.server.queue.common.TbProtoQueueMsg; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.apiusage.TbApiUsageStateService; import org.thingsboard.server.service.executors.DbCallbackExecutorService; -import org.thingsboard.server.service.gateway_device.GatewayDeviceStateService; import org.thingsboard.server.service.profile.TbDeviceProfileCache; import org.thingsboard.server.cluster.TbClusterService; import org.thingsboard.server.service.resource.TbResourceService; -import org.thingsboard.server.service.state.DeviceStateService; import java.util.List; import java.util.Optional; @@ -130,7 +128,6 @@ public class DefaultTransportApiService implements TransportApiService { private final DeviceService deviceService; private final RelationService relationService; private final DeviceCredentialsService deviceCredentialsService; - private final DeviceStateService deviceStateService; private final DbCallbackExecutorService dbCallbackExecutorService; private final TbClusterService tbClusterService; private final DataDecodingEncodingService dataDecodingEncodingService; @@ -138,7 +135,6 @@ public class DefaultTransportApiService implements TransportApiService { private final TbResourceService resourceService; private final OtaPackageService otaPackageService; private final OtaPackageDataCache otaPackageDataCache; - private final GatewayDeviceStateService gatewayDeviceStateService; private final ConcurrentMap deviceCreationLocks = new ConcurrentHashMap<>(); @@ -319,8 +315,6 @@ public class DefaultTransportApiService implements TransportApiService { } relationService.saveRelationAsync(TenantId.SYS_TENANT_ID, lastConnectedGatewayRelation); - gatewayDeviceStateService.checkAndUpdateDeletedGatewayDevicesAttribute(device); - GetOrCreateDeviceFromGatewayResponseMsg.Builder builder = GetOrCreateDeviceFromGatewayResponseMsg.newBuilder() .setDeviceInfo(getDeviceInfoProto(device)); DeviceProfile deviceProfile = deviceProfileCache.get(device.getTenantId(), device.getDeviceProfileId()); diff --git a/ui-ngx/src/app/modules/home/components/wizard/device-wizard-dialog.component.html b/ui-ngx/src/app/modules/home/components/wizard/device-wizard-dialog.component.html index cac8ae4c1e..ae0be158d5 100644 --- a/ui-ngx/src/app/modules/home/components/wizard/device-wizard-dialog.component.html +++ b/ui-ngx/src/app/modules/home/components/wizard/device-wizard-dialog.component.html @@ -103,16 +103,10 @@ {{ 'device.is-gateway' | translate }} - -
{{ 'device.overwrite-activity-time' | translate }} - - {{ 'device.handle-device-renaming' | translate }} -
device.description diff --git a/ui-ngx/src/app/modules/home/components/wizard/device-wizard-dialog.component.ts b/ui-ngx/src/app/modules/home/components/wizard/device-wizard-dialog.component.ts index 552e1d5eb8..fdbc25107f 100644 --- a/ui-ngx/src/app/modules/home/components/wizard/device-wizard-dialog.component.ts +++ b/ui-ngx/src/app/modules/home/components/wizard/device-wizard-dialog.component.ts @@ -109,7 +109,6 @@ export class DeviceWizardDialogComponent extends label: ['', Validators.maxLength(255)], gateway: [false], overwriteActivityTime: [false], - handleDeviceRenaming: [false], addProfileType: [0], deviceProfileId: [null, Validators.required], newDeviceProfileTitle: [{value: null, disabled: true}], @@ -327,7 +326,6 @@ export class DeviceWizardDialogComponent extends additionalInfo: { gateway: this.deviceWizardFormGroup.get('gateway').value, overwriteActivityTime: this.deviceWizardFormGroup.get('overwriteActivityTime').value, - handleDeviceRenaming: this.deviceWizardFormGroup.get('handleDeviceRenaming').value, description: this.deviceWizardFormGroup.get('description').value }, customerId: null diff --git a/ui-ngx/src/app/modules/home/pages/device/device.component.html b/ui-ngx/src/app/modules/home/pages/device/device.component.html index 1f89481fd0..1187a34f5d 100644 --- a/ui-ngx/src/app/modules/home/pages/device/device.component.html +++ b/ui-ngx/src/app/modules/home/pages/device/device.component.html @@ -128,16 +128,10 @@ {{ 'device.is-gateway' | translate }} - -
{{ 'device.overwrite-activity-time' | translate }} - - {{ 'device.handle-device-renaming' | translate }} -
device.description diff --git a/ui-ngx/src/app/modules/home/pages/device/device.component.ts b/ui-ngx/src/app/modules/home/pages/device/device.component.ts index cd1c78b3dd..7cd60a89f4 100644 --- a/ui-ngx/src/app/modules/home/pages/device/device.component.ts +++ b/ui-ngx/src/app/modules/home/pages/device/device.component.ts @@ -92,7 +92,6 @@ export class DeviceComponent extends EntityComponent { { gateway: [entity && entity.additionalInfo ? entity.additionalInfo.gateway : false], overwriteActivityTime: [entity && entity.additionalInfo ? entity.additionalInfo.overwriteActivityTime : false], - handleDeviceRenaming: [entity && entity.additionalInfo ? entity.additionalInfo.handleDeviceRenaming : false], description: [entity && entity.additionalInfo ? entity.additionalInfo.description : ''], } ) @@ -122,7 +121,6 @@ export class DeviceComponent extends EntityComponent { additionalInfo: { gateway: entity.additionalInfo ? entity.additionalInfo.gateway : false, overwriteActivityTime: entity.additionalInfo ? entity.additionalInfo.overwriteActivityTime : false, - handleDeviceRenaming: entity.additionalInfo ? entity.additionalInfo.handleDeviceRenaming : false, description: entity.additionalInfo ? entity.additionalInfo.description : '' } }); diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index 092f944ece..807f3781a9 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -1045,7 +1045,6 @@ "unable-delete-device-alias-text": "Device alias '{{deviceAlias}}' can't be deleted as it used by the following widget(s):
{{widgetsList}}", "is-gateway": "Is gateway", "overwrite-activity-time": "Overwrite activity time for connected device", - "handle-device-renaming": "Handle device renaming", "public": "Public", "device-public": "Device is public", "select-device": "Select device", From e69871d8869af3513a746119d14e884939d57bb5 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Wed, 29 Dec 2021 17:50:56 +0200 Subject: [PATCH 038/798] UI: Added constants in baseDetailsPageByEntityType --- ui-ngx/src/app/shared/models/constants.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ui-ngx/src/app/shared/models/constants.ts b/ui-ngx/src/app/shared/models/constants.ts index fa73e0d40e..251f310f9b 100644 --- a/ui-ngx/src/app/shared/models/constants.ts +++ b/ui-ngx/src/app/shared/models/constants.ts @@ -143,9 +143,11 @@ export const baseDetailsPageByEntityType = new Map([ [EntityType.TENANT_PROFILE, '/tenantProfiles'], [EntityType.CUSTOMER, '/customers'], [EntityType.USER, '/users'], + [EntityType.DASHBOARD, '/dashboards'], [EntityType.ASSET, '/assets'], [EntityType.DEVICE, '/devices'], [EntityType.DEVICE_PROFILE, '/deviceProfiles'], + [EntityType.RULE_CHAIN, '/ruleChains'], [EntityType.EDGE, '/edgeInstances'], [EntityType.ENTITY_VIEW, '/entityViews'], [EntityType.TB_RESOURCE, '/settings/resources-library'], From d255c8d16c5b534c77a24905126dead39f4abe48 Mon Sep 17 00:00:00 2001 From: zbeacon Date: Thu, 30 Dec 2021 12:27:45 +0200 Subject: [PATCH 039/798] Refactored --- .../DefaultGatewayDeviceStateService.java | 23 +++++++++++-------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/gateway_device/DefaultGatewayDeviceStateService.java b/application/src/main/java/org/thingsboard/server/service/gateway_device/DefaultGatewayDeviceStateService.java index 84b9f977ba..4fa6be5654 100644 --- a/application/src/main/java/org/thingsboard/server/service/gateway_device/DefaultGatewayDeviceStateService.java +++ b/application/src/main/java/org/thingsboard/server/service/gateway_device/DefaultGatewayDeviceStateService.java @@ -48,7 +48,6 @@ public class DefaultGatewayDeviceStateService implements GatewayDeviceStateServi private final static String DEVICE_RENAMED_METHOD_NAME = "gateway_device_renamed"; private final static String DEVICE_DELETED_METHOD_NAME = "gateway_device_deleted"; - @Value("${server.rest.server_side_rpc.min_timeout:5000}") protected long minTimeout; @@ -62,13 +61,10 @@ public class DefaultGatewayDeviceStateService implements GatewayDeviceStateServi @Autowired private TbCoreDeviceRpcService deviceRpcService; - @Override public void update(Device device, Device oldDevice) { - List relationToGatewayList = relationService.findByFromAndType(TenantId.SYS_TENANT_ID, device.getId(), DataConstants.LAST_CONNECTED_GATEWAY, RelationTypeGroup.COMMON); - if (!relationToGatewayList.isEmpty()) { - EntityRelation relationToGateway = relationToGatewayList.get(0); - Device gatewayDevice = deviceService.findDeviceById(device.getTenantId(), (DeviceId) relationToGateway.getTo()); + Device gatewayDevice = findGatewayDeviceByRelationFromDevice(device); + if (gatewayDevice != null) { ObjectNode renamedDeviceNode = JacksonUtil.newObjectNode(); renamedDeviceNode.put(device.getName(), oldDevice.getName()); ToDeviceRpcRequest rpcRequest = formDeviceToGatewayRPCRequest(gatewayDevice, renamedDeviceNode, DEVICE_RENAMED_METHOD_NAME); @@ -81,10 +77,8 @@ public class DefaultGatewayDeviceStateService implements GatewayDeviceStateServi @Override public void delete(Device device) { - List relationToGatewayList = relationService.findByFromAndType(TenantId.SYS_TENANT_ID, device.getId(), DataConstants.LAST_CONNECTED_GATEWAY, RelationTypeGroup.COMMON); - if (!relationToGatewayList.isEmpty()) { - EntityRelation relationToGateway = relationToGatewayList.get(0); - Device gatewayDevice = deviceService.findDeviceById(device.getTenantId(), (DeviceId) relationToGateway.getTo()); + Device gatewayDevice = findGatewayDeviceByRelationFromDevice(device); + if (gatewayDevice != null) { TextNode deletedDeviceNode = new TextNode(device.getName()); ToDeviceRpcRequest rpcRequest = formDeviceToGatewayRPCRequest(gatewayDevice, deletedDeviceNode, DEVICE_DELETED_METHOD_NAME); deviceRpcService.processRestApiRpcRequest(rpcRequest, fromDeviceRpcResponse -> { @@ -109,4 +103,13 @@ public class DefaultGatewayDeviceStateService implements GatewayDeviceStateServi null ); } + + private Device findGatewayDeviceByRelationFromDevice(Device device) { + List relationToGatewayList = relationService.findByFromAndType(TenantId.SYS_TENANT_ID, device.getId(), DataConstants.LAST_CONNECTED_GATEWAY, RelationTypeGroup.COMMON); + if (!relationToGatewayList.isEmpty()) { + EntityRelation relationToGateway = relationToGatewayList.get(0); + return deviceService.findDeviceById(device.getTenantId(), (DeviceId) relationToGateway.getTo()); + } + return null; + } } From c655b58977990fc57bfe570509c1a7faf2e88e8d Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Thu, 30 Dec 2021 12:31:21 +0200 Subject: [PATCH 040/798] lwm2m delete security files jks and in yml security enable = false --- .../src/main/resources/thingsboard.yml | 10 +- .../credentials/shell/lwM2M_credentials.sh | 359 ----------------- .../credentials/shell/lwM2M_keygen.properties | 57 --- .../lwm2m/src/main/resources/lwm2mserver.jks | Bin 3849 -> 0 bytes pom.xml | 34 -- .../credentials/shell/lwM2M_credentials.sh | 360 ------------------ .../credentials/shell/lwM2M_keygen.properties | 57 --- transport/lwm2m/src/main/data/lwm2mserver.jks | Bin 4017 -> 0 bytes .../src/main/resources/tb-lwm2m-transport.yml | 10 +- 9 files changed, 10 insertions(+), 877 deletions(-) delete mode 100644 common/transport/lwm2m/src/main/resources/credentials/shell/lwM2M_credentials.sh delete mode 100644 common/transport/lwm2m/src/main/resources/credentials/shell/lwM2M_keygen.properties delete mode 100644 common/transport/lwm2m/src/main/resources/lwm2mserver.jks delete mode 100755 transport/lwm2m/src/main/data/credentials/shell/lwM2M_credentials.sh delete mode 100644 transport/lwm2m/src/main/data/credentials/shell/lwM2M_keygen.properties delete mode 100644 transport/lwm2m/src/main/data/lwm2mserver.jks diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index a502afb297..6865aebf9a 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -733,7 +733,7 @@ transport: # Server X509 Certificates support credentials: # Whether to enable LWM2M server X509 Certificate/RPK support - enabled: "${LWM2M_SERVER_CREDENTIALS_ENABLED:true}" + enabled: "${LWM2M_SERVER_CREDENTIALS_ENABLED:false}" # Server credentials type (PEM - pem certificate file; KEYSTORE - java keystore) type: "${LWM2M_SERVER_CREDENTIALS_TYPE:PEM}" # PEM server credentials @@ -769,7 +769,7 @@ transport: # Bootstrap server X509 Certificates support credentials: # Whether to enable LWM2M bootstrap server X509 Certificate/RPK support - enabled: "${LWM2M_BS_CREDENTIALS_ENABLED:true}" + enabled: "${LWM2M_BS_CREDENTIALS_ENABLED:false}" # Server credentials type (PEM - pem certificate file; KEYSTORE - java keystore) type: "${LWM2M_BS_CREDENTIALS_TYPE:PEM}" # PEM server credentials @@ -796,19 +796,19 @@ transport: # X509 trust certificates trust-credentials: # Whether to load X509 trust certificates - enabled: "${LWM2M_TRUST_CREDENTIALS_ENABLED:true}" + enabled: "${LWM2M_TRUST_CREDENTIALS_ENABLED:false}" # Trust certificates store type (PEM - pem certificates file; KEYSTORE - java keystore) type: "${LWM2M_TRUST_CREDENTIALS_TYPE:PEM}" # PEM certificates pem: # Path to the certificates file (holds trust certificates) - cert_file: "${LWM2M_TRUST_PEM_CERT:lwm2mserver.pem}" + cert_file: "${LWM2M_TRUST_PEM_CERT:lwm2mtruststorechain.pem}" # Keystore with trust certificates keystore: # Type of the key store type: "${LWM2M_TRUST_KEY_STORE_TYPE:JKS}" # Path to the key store that holds the X509 certificates - store_file: "${LWM2M_TRUST_KEY_STORE:lwm2mserver.jks}" + store_file: "${LWM2M_TRUST_KEY_STORE:lwm2mtruststorechain.jks}" # Password used to access the key store store_password: "${LWM2M_TRUST_KEY_STORE_PASSWORD:server_ks_password}" recommended_ciphers: "${LWM2M_RECOMMENDED_CIPHERS:false}" diff --git a/common/transport/lwm2m/src/main/resources/credentials/shell/lwM2M_credentials.sh b/common/transport/lwm2m/src/main/resources/credentials/shell/lwM2M_credentials.sh deleted file mode 100644 index f68ca30005..0000000000 --- a/common/transport/lwm2m/src/main/resources/credentials/shell/lwM2M_credentials.sh +++ /dev/null @@ -1,359 +0,0 @@ -#!/bin/sh -# -# Copyright © 2016-2021 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. -# - -#/home/nick/Igor_project/Thingsboard_Perfrmance_test/performance-tests/src/main/resources/credentials/shell/lwM2M_credentials.sh -p LwX509 -s 0 -f 2000 -a client_alias_ -e client_self_signed_ -b bootstrap -d server -j serverKeyStore.jks -k clientKeyStore.jks -c client_ks_password -w server_ks_password - -#p) CLIENT_CN=$CLIENT_PREFIX00000000 -#s) client_start=0 -#f) client_finish=1 -#a) CLIENT_ALIAS=CLIENT_ALIAS_PREFIX_00000000 -#e) CLIENT_SELF_ALIAS=CLIENT_SELF_ALIAS_PREFIX_00000000 -#b) BOOTSTRAP_ALIAS=bootstrap -#d) SERVER_ALIAS=server -#j) SERVER_STORE=serverKeyStore.jks -#k) CLIENT_STORE=clientKeyStore.jks -#c) CLIENT_STORE_PWD=client_ks_password -#w) SERVER_STORE_PWD=server_ks_password -#l) ROOT_KEY_ALIAS=root_key_alias - -while getopts p:s:f:a:e:b:d:j:k:c:w:l: flag; do - case "${flag}" in - p) client_pref=${OPTARG} ;; - s) client_start=${OPTARG} ;; - f) client_finish=${OPTARG} ;; - a) client_alias_pref=${OPTARG} ;; - e) client_self_alias_pref=${OPTARG} ;; - b) bootstrap_alias=${OPTARG} ;; - d) server_alias=${OPTARG} ;; - j) key_store_server_file=${OPTARG} ;; - k) key_store_client_file=${OPTARG} ;; - c) client_key_store_pwd=${OPTARG} ;; - w) server_key_store_pwd=${OPTARG} ;; - w) root_key_alias=${OPTARG} ;; - esac -done - -# cd to dir of script -script_dir=$(dirname $0) -echo "script_dir: $script_dir" -cd $script_dir -# source the properties: -. ./lwM2M_keygen.properties - -if [ -n "$client_pref" ]; then - CLIENT_PREFIX=$client_pref -fi - -if [ -z "$client_start" ]; then - client_start=0 -fi - -if [ -z "$client_finish" ]; then - client_finish=1 -fi - -if [ -n "$client_alias_pref" ]; then - CLIENT_ALIAS_PREFIX=$client_alias_pref -fi - -if [ -n "$client_self_alias_pref" ]; then - CLIENT_SELF_ALIAS_PREFIX=$client_self_alias_pref -fi - -if [ -n "$bootstrap_alias" ]; then - BOOTSTRAP_ALIAS=$bootstrap_alias -fi - -if [ -n "$server_alias" ]; then - SERVER_ALIAS=$server_alias -fi - -if [ -n "$key_store_server_file" ]; then - SERVER_STORE=$key_store_server_file -fi - -if [ -n "$key_store_client_file" ]; then - CLIENT_STORE=$key_store_client_file -fi - -if [ -n "$client_key_store_pwd" ]; then - CLIENT_STORE_PWD=$client_key_store_pwd -fi - -if [ -n "$server_key_store_pwd" ]; then - SERVER_STORE_PWD=$server_key_store_pwd -fi - -if [ -n "$root_key_alias" ]; then - ROOT_KEY_ALIAS=$root_key_alias -fi - -CLIENT_NUMBER=$client_start - -echo "==Start==" -echo "CLIENT_PREFIX: $CLIENT_PREFIX" -echo "client_start: $client_start" -echo "client_finish: $client_finish" -echo "CLIENT_ALIAS_PREFIX: $CLIENT_ALIAS_PREFIX" -echo "CLIENT_SELF_ALIAS_PREFIX: $CLIENT_SELF_ALIAS_PREFIX" -echo "BOOTSTRAP_ALIAS: $BOOTSTRAP_ALIAS" -echo "SERVER_ALIAS: $SERVER_ALIAS" -echo "SERVER_STORE: $SERVER_STORE" -echo "CLIENT_STORE: $CLIENT_STORE" -echo "CLIENT_STORE_PWD: $CLIENT_STORE_PWD" -echo "SERVER_STORE_PWD: $SERVER_STORE_PWD" -echo "CLIENT_NUMBER: $CLIENT_NUMBER" -echo "ROOT_KEY_ALIAS: $ROOT_KEY_ALIAS" - -end_point() { - echo "$CLIENT_PREFIX$(printf "%08d" $CLIENT_NUMBER)" -} - -client_alias_point() { - echo "$CLIENT_ALIAS_PREFIX$(printf "%08d" $CLIENT_NUMBER)" -} - -client_self_alias_point() { - echo "$CLIENT_SELF_ALIAS_PREFIX$(printf "%08d" $CLIENT_NUMBER)" -} - -# Generation of the keystore. -echo "${H0}====START========${RESET}" -echo "${H1}Server Keystore : ${RESET}" -echo "${H1}==================${RESET}" -echo "${H2}Creating the trusted root CA key and certificate...${RESET}" -# -keysize -# 1024 (when using -genkeypair) -keytool \ - -genkeypair \ - -alias $ROOT_KEY_ALIAS \ - -keyalg EC \ - -dname "CN=$ROOT_CN, OU=$ORGANIZATIONAL_UNIT, O=$ORGANIZATION, L=$CITY, ST=$STATE_OR_PROVINCE, C=$TWO_LETTER_COUNTRY_CODE" \ - -validity $VALIDITY \ - -storetype $STORETYPE \ - -keypass $SERVER_STORE_PWD \ - -keystore $SERVER_STORE \ - -storepass $SERVER_STORE_PWD - -echo -echo "${H2}Creating server key and self-signed certificate ...${RESET}" -keytool \ - -genkeypair \ - -alias $SERVER_ALIAS \ - -keyalg EC \ - -dname "CN=$SERVER_SELF_CN, OU=$ORGANIZATIONAL_UNIT, O=$ORGANIZATION, L=$CITY, ST=$STATE_OR_PROVINCE, C=$TWO_LETTER_COUNTRY_CODE" \ - -validity $VALIDITY \ - -storetype $STORETYPE \ - -keypass $SERVER_STORE_PWD \ - -keystore $SERVER_STORE \ - -storepass $SERVER_STORE_PWD -keytool \ - -exportcert \ - -alias $SERVER_ALIAS \ - -keystore $SERVER_STORE \ - -storepass $SERVER_STORE_PWD | - keytool \ - -importcert \ - -alias $SERVER_SELF_ALIAS \ - -keystore $SERVER_STORE \ - -storepass $SERVER_STORE_PWD \ - -noprompt - -echo -echo "${H2}Creating server certificate signed by root CA...${RESET}" -keytool \ - -certreq \ - -alias $SERVER_ALIAS \ - -dname "CN=$SERVER_CN, OU=$ORGANIZATIONAL_UNIT, O=$ORGANIZATION, L=$CITY, ST=$STATE_OR_PROVINCE, C=$TWO_LETTER_COUNTRY_CODE" \ - -keystore $SERVER_STORE \ - -storepass $SERVER_STORE_PWD | - keytool \ - -gencert \ - -alias $ROOT_KEY_ALIAS \ - -keystore $SERVER_STORE \ - -storepass $SERVER_STORE_PWD \ - -storetype $STORETYPE \ - -validity $VALIDITY | - keytool \ - -importcert \ - -alias $SERVER_ALIAS \ - -keystore $SERVER_STORE \ - -storepass $SERVER_STORE_PWD - -echo -echo "${H2}Creating bootstrap key and self-signed certificate ...${RESET}" -keytool \ - -genkeypair \ - -alias $BOOTSTRAP_ALIAS \ - -keyalg EC \ - -dname "CN=$BOOTSTRAP_SELF_CN, OU=$ORGANIZATIONAL_UNIT, O=$ORGANIZATION, L=$CITY, ST=$STATE_OR_PROVINCE, C=$TWO_LETTER_COUNTRY_CODE" \ - -validity $VALIDITY \ - -storetype $STORETYPE \ - -keypass $SERVER_STORE_PWD \ - -keystore $SERVER_STORE \ - -storepass $SERVER_STORE_PWD -keytool \ - -exportcert \ - -alias $BOOTSTRAP_ALIAS \ - -keystore $SERVER_STORE \ - -storepass $SERVER_STORE_PWD | - keytool \ - -importcert \ - -alias $BOOTSTRAP_SELF_ALIAS \ - -keystore $SERVER_STORE \ - -storepass $SERVER_STORE_PWD \ - -noprompt - -echo -echo "${H2}Creating bootstrap certificate signed by root CA...${RESET}" -keytool \ - -certreq \ - -alias $BOOTSTRAP_ALIAS \ - -dname "CN=$BOOTSTRAP_CN, OU=$ORGANIZATIONAL_UNIT, O=$ORGANIZATION, L=$CITY, ST=$STATE_OR_PROVINCE, C=$TWO_LETTER_COUNTRY_CODE" \ - -keystore $SERVER_STORE \ - -storepass $SERVER_STORE_PWD | - keytool \ - -gencert \ - -alias $ROOT_KEY_ALIAS \ - -keystore $SERVER_STORE \ - -storepass $SERVER_STORE_PWD \ - -storetype $STORETYPE \ - -validity $VALIDITY | - keytool \ - -importcert \ - -alias $BOOTSTRAP_ALIAS \ - -keystore $SERVER_STORE \ - -storepass $SERVER_STORE_PWD - -if [ "$client_start" -lt "$client_finish" ]; then - echo - echo "${H2}Import root certificate just to be able to import need by root CA with expected CN to $CLIENT_STORE${RESET}" - keytool \ - -exportcert \ - -alias $ROOT_KEY_ALIAS \ - -keystore $SERVER_STORE \ - -storepass $SERVER_STORE_PWD | - keytool \ - -importcert \ - -alias $ROOT_KEY_ALIAS \ - -keystore $CLIENT_STORE \ - -storepass $CLIENT_STORE_PWD \ - -noprompt -fi - -cert_end_point() { - echo - echo "${H1}Client Keystore : ${RESET}" - echo "${H1}==================${RESET}" - echo "${H2}Creating client key and self-signed certificate with expected CN CLIENT_ALIAS: $CLIENT_ALIAS${RESET}" - keytool \ - -genkeypair \ - -alias $CLIENT_ALIAS \ - -keyalg EC \ - -dname "CN=$CLIENT_SELF_CN, OU=$ORGANIZATIONAL_UNIT, O=$ORGANIZATION, L=$CITY, ST=$STATE_OR_PROVINCE, C=$TWO_LETTER_COUNTRY_CODE" \ - -validity $VALIDITY \ - -storetype $STORETYPE \ - -keypass $CLIENT_STORE_PWD \ - -keystore $CLIENT_STORE \ - -storepass $CLIENT_STORE_PWD - keytool \ - -exportcert \ - -alias $CLIENT_ALIAS \ - -keystore $CLIENT_STORE \ - -storepass $CLIENT_STORE_PWD | - keytool \ - -importcert \ - -alias $CLIENT_SELF_ALIAS \ - -keystore $CLIENT_STORE \ - -storepass $CLIENT_STORE_PWD \ - -noprompt -# -# echo -# echo "${H2}Import root certificate just to be able to import ned by root CA with expected CN...${RESET}" -# keytool \ -# -exportcert \ -# -alias $ROOT_KEY_ALIAS \ -# -keystore $SERVER_STORE \ -# -storepass $SERVER_STORE_PWD | -# keytool \ -# -importcert \ -# -alias $ROOT_KEY_ALIAS \ -# -keystore $CLIENT_STORE \ -# -storepass $CLIENT_STORE_PWD \ -# -noprompt -# - - echo - echo "${H2}Creating client certificate signed by root CA with expected CN CLIENT_ALIAS: $CLIENT_ALIAS CLIENT_CN: $CLIENT_CN${RESET}" - keytool \ - -certreq \ - -alias $CLIENT_ALIAS \ - -dname "CN=$CLIENT_CN, OU=$ORGANIZATIONAL_UNIT, O=$ORGANIZATION, L=$CITY, ST=$STATE_OR_PROVINCE, C=$TWO_LETTER_COUNTRY_CODE" \ - -keystore $CLIENT_STORE \ - -storepass $CLIENT_STORE_PWD | - keytool \ - -gencert \ - -alias $ROOT_KEY_ALIAS \ - -keystore $SERVER_STORE \ - -storepass $SERVER_STORE_PWD \ - -storetype $STORETYPE \ - -validity $VALIDITY | - keytool \ - -importcert \ - -alias $CLIENT_ALIAS \ - -keystore $CLIENT_STORE \ - -storepass $CLIENT_STORE_PWD \ - -noprompt -} - -if [ "$client_start" -lt "$client_finish" ]; then - echo - echo "==Start Client==" - while [ "$CLIENT_NUMBER" -lt "$client_finish" ]; do - echo "number $CLIENT_NUMBER" - echo "finish $client_finish" - CLIENT_CN=$(end_point) - CLIENT_ALIAS=$(client_alias_point) - CLIENT_SELF_ALIAS=$(client_self_alias_point) - echo "CLIENT_CN $CLIENT_CN" - echo "CLIENT_ALIAS $CLIENT_ALIAS" - echo "CLIENT_SELF_ALIAS $CLIENT_SELF_ALIAS" - cert_end_point - CLIENT_NUMBER=$(($CLIENT_NUMBER + 1)) - echo - done -fi - -echo -echo "${H0}!!! Warning ${H2}Migrate ${H1}${SERVER_STORE} ${H2}to ${H1}PKCS12 ${H2}which is an industry standard format..${RESET}" -keytool \ - -importkeystore \ - -srckeystore $SERVER_STORE \ - -destkeystore $SERVER_STORE \ - -deststoretype pkcs12 \ - -srcstorepass $SERVER_STORE_PWD - -if [ "$client_start" -lt "$client_finish" ]; then - echo - echo "${H0}!!! Warning ${H2}Migrate ${H1}${CLIENT_STORE} ${H2}to ${H1}PKCS12 ${H2}which is an industry standard format..${RESET}" - keytool \ - -importkeystore \ - -srckeystore $CLIENT_STORE \ - -destkeystore $CLIENT_STORE \ - -deststoretype pkcs12 \ - -srcstorepass $CLIENT_STORE_PWD -fi diff --git a/common/transport/lwm2m/src/main/resources/credentials/shell/lwM2M_keygen.properties b/common/transport/lwm2m/src/main/resources/credentials/shell/lwM2M_keygen.properties deleted file mode 100644 index 7b3cd9c09a..0000000000 --- a/common/transport/lwm2m/src/main/resources/credentials/shell/lwM2M_keygen.properties +++ /dev/null @@ -1,57 +0,0 @@ -# -# Copyright © 2016-2017 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. -# - -# Keystore common parameters -ROOT_KEY_ALIAS=rootCA -DOMAIN_SUFFIX="$(hostname)" -ROOT_CN="$DOMAIN_SUFFIX $ROOT_KEY_ALIAS" -ORGANIZATIONAL_UNIT=Thingsboard -ORGANIZATION=Thingsboard -CITY=SF -STATE_OR_PROVINCE=CA -TWO_LETTER_COUNTRY_CODE=US -VALIDITY=36500 #days -STORETYPE="JKS" - -#Server -SERVER_STORE=serverKeyStore1.jks -SERVER_STORE_PWD=server_ks_password1 -SERVER_ALIAS=server1 -SERVER_CN="$DOMAIN_SUFFIX server LwM2M signed by root CA" -SERVER_SELF_ALIAS=server_self_signed -SERVER_SELF_CN="$DOMAIN_SUFFIX server LwM2M self-signed" -BOOTSTRAP_ALIAS=bootstrap1 -BOOTSTRAP_CN="$DOMAIN_SUFFIX bootstrap server LwM2M signed by root CA" -BOOTSTRAP_SELF_ALIAS=bootstrap_self_signed -BOOTSTRAP_SELF_CN="$DOMAIN_SUFFIX bootstrap server LwM2M self-signed" - -# Client -CLIENT_STORE=clientKeyStore1.jks -CLIENT_STORE_PWD=client_ks_password1 -CLIENT_ALIAS_PREFIX=client_alias_1 -CLIENT_PREFIX=LwX509___ -CLIENT_SELF_ALIAS_PREFIX=client_self_signed_1 -CLIENT_SELF_CN="$DOMAIN_SUFFIX client LwM2M self-signed" - -# Color output stuff -red=`tput setaf 1` -green=`tput setaf 2` -blue=`tput setaf 4` -bold=`tput bold` -H0=${red}${bold} -H1=${green}${bold} -H2=${blue} -RESET=`tput sgr0` diff --git a/common/transport/lwm2m/src/main/resources/lwm2mserver.jks b/common/transport/lwm2m/src/main/resources/lwm2mserver.jks deleted file mode 100644 index 5fab824aa1b19928fe711701cd27857472a9e739..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3849 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 b(C|b)G3l|ClS0(tPLrtJO%hUKOzz(RVYoVD diff --git a/pom.xml b/pom.xml index c06960c6ca..9dd8c043b2 100755 --- a/pom.xml +++ b/pom.xml @@ -403,39 +403,9 @@ false - - ../common/transport/lwm2m/src/main/resources - - **/*.xml - **/*.jks - - false - - - - - - copy-lwm2m-resources - ${pkg.process-resources.phase} - - copy-resources - - - ../transport/lwm2m/src/main/data - - - ../common/transport/lwm2m/src/main/resources - - **/*.xml - **/*.jks - - false - - - copy-docker-config ${pkg.process-resources.phase} @@ -829,10 +799,6 @@ **/*.proto.js docker/haproxy/** docker/tb-node/** - src/main/resources/models/*.xml - src/main/resources/credentials/*.jks - src/main/resources/credentials/shell/*.jks - src/main/resources/credentials/shell/*.jks.old ui/** src/.browserslistrc **/yarn.lock diff --git a/transport/lwm2m/src/main/data/credentials/shell/lwM2M_credentials.sh b/transport/lwm2m/src/main/data/credentials/shell/lwM2M_credentials.sh deleted file mode 100755 index d623bfad42..0000000000 --- a/transport/lwm2m/src/main/data/credentials/shell/lwM2M_credentials.sh +++ /dev/null @@ -1,360 +0,0 @@ -#!/bin/sh -# -# Copyright © 2016-2021 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. -# - -#/home/nick/Igor_project/Thingsboard_Perfrmance_test/performance-tests/src/main/resources/credentials/shell/lwM2M_credentials.sh -p LwX509 -s 0 -f 2000 -a client_alias_ -e client_self_signed_ -b bootstrap -d server -j serverKeyStore.jks -k clientKeyStore.jks -c client_ks_password -w server_ks_password - -#p) CLIENT_CN=$CLIENT_PREFIX00000000 -#s) client_start=0 -#f) client_finish=1 -#a) CLIENT_ALIAS=CLIENT_ALIAS_PREFIX_00000000 -#e) CLIENT_SELF_ALIAS=CLIENT_SELF_ALIAS_PREFIX_00000000 -#b) BOOTSTRAP_ALIAS=bootstrap -#d) SERVER_ALIAS=server -#j) SERVER_STORE=serverKeyStore.jks -#k) CLIENT_STORE=clientKeyStore.jks -#c) CLIENT_STORE_PWD=client_ks_password -#w) SERVER_STORE_PWD=server_ks_password -#l) ROOT_KEY_ALIAS=root_key_alias - -while getopts p:s:f:a:e:b:d:j:k:c:w:l: flag; do - case "${flag}" in - p) client_pref=${OPTARG} ;; - s) client_start=${OPTARG} ;; - f) client_finish=${OPTARG} ;; - a) client_alias_pref=${OPTARG} ;; - e) client_self_alias_pref=${OPTARG} ;; - b) bootstrap_alias=${OPTARG} ;; - d) server_alias=${OPTARG} ;; - j) key_store_server_file=${OPTARG} ;; - k) key_store_client_file=${OPTARG} ;; - c) client_key_store_pwd=${OPTARG} ;; - w) server_key_store_pwd=${OPTARG} ;; - w) root_key_alias=${OPTARG} ;; - esac -done - -# cd to dir of script -script_dir=$(dirname $0) -echo "script_dir: $script_dir" -cd $script_dir -# source the properties: -. ./lwM2M_keygen.properties - -if [ -n "$client_pref" ]; then - CLIENT_PREFIX=$client_pref -fi - -if [ -z "$client_start" ]; then - client_start=0 -fi - -if [ -z "$client_finish" ]; then - client_finish=1 -fi - -if [ -n "$client_alias_pref" ]; then - CLIENT_ALIAS_PREFIX=$client_alias_pref -fi - -if [ -n "$client_self_alias_pref" ]; then - CLIENT_SELF_ALIAS_PREFIX=$client_self_alias_pref -fi - -if [ -n "$bootstrap_alias" ]; then - BOOTSTRAP_ALIAS=$bootstrap_alias -fi - -if [ -n "$server_alias" ]; then - SERVER_ALIAS=$server_alias -fi - -if [ -n "$key_store_server_file" ]; then - SERVER_STORE=$key_store_server_file -fi - -if [ -n "$key_store_client_file" ]; then - CLIENT_STORE=$key_store_client_file -fi - -if [ -n "$client_key_store_pwd" ]; then - CLIENT_STORE_PWD=$client_key_store_pwd -fi - -if [ -n "$server_key_store_pwd" ]; then - SERVER_STORE_PWD=$server_key_store_pwd -fi - -if [ -n "$root_key_alias" ]; then - ROOT_KEY_ALIAS=$root_key_alias -fi - -CLIENT_NUMBER=$client_start - -echo "==Start==" -echo "CLIENT_PREFIX: $CLIENT_PREFIX" -echo "client_start: $client_start" -echo "client_finish: $client_finish" -echo "CLIENT_ALIAS_PREFIX: $CLIENT_ALIAS_PREFIX" -echo "CLIENT_SELF_ALIAS_PREFIX: $CLIENT_SELF_ALIAS_PREFIX" -echo "BOOTSTRAP_ALIAS: $BOOTSTRAP_ALIAS" -echo "SERVER_ALIAS: $SERVER_ALIAS" -echo "SERVER_STORE: $SERVER_STORE" -echo "CLIENT_STORE: $CLIENT_STORE" -echo "CLIENT_STORE_PWD: $CLIENT_STORE_PWD" -echo "SERVER_STORE_PWD: $SERVER_STORE_PWD" -echo "CLIENT_NUMBER: $CLIENT_NUMBER" -echo "ROOT_KEY_ALIAS: $ROOT_KEY_ALIAS" - -end_point() { - echo "$CLIENT_PREFIX$(printf "%08d" $CLIENT_NUMBER)" -} - -client_alias_point() { - echo "$CLIENT_ALIAS_PREFIX$(printf "%08d" $CLIENT_NUMBER)" -} - -client_self_alias_point() { - echo "$CLIENT_SELF_ALIAS_PREFIX$(printf "%08d" $CLIENT_NUMBER)" -} - -# Generation of the keystore. -echo "${H0}====START========${RESET}" -echo "${H1}Server Keystore : ${RESET}" -echo "${H1}==================${RESET}" -echo "${H2}Creating the trusted root CA key and certificate...${RESET}" -# -keysize -# 1024 (when using -genkeypair) -keytool \ - -genkeypair \ - -alias $ROOT_KEY_ALIAS \ - -keyalg EC \ - -dname "CN=$ROOT_CN, OU=$ORGANIZATIONAL_UNIT, O=$ORGANIZATION, L=$CITY, ST=$STATE_OR_PROVINCE, C=$TWO_LETTER_COUNTRY_CODE" \ - -validity $VALIDITY \ - -storetype $STORETYPE \ - -keypass $SERVER_STORE_PWD \ - -keystore $SERVER_STORE \ - -storepass $SERVER_STORE_PWD - -echo -echo "${H2}Creating server key and self-signed certificate ...${RESET}" -keytool \ - -genkeypair \ - -alias $SERVER_ALIAS \ - -keyalg EC \ - -dname "CN=$SERVER_SELF_CN, OU=$ORGANIZATIONAL_UNIT, O=$ORGANIZATION, L=$CITY, ST=$STATE_OR_PROVINCE, C=$TWO_LETTER_COUNTRY_CODE" \ - -validity $VALIDITY \ - -storetype $STORETYPE \ - -keypass $SERVER_STORE_PWD \ - -keystore $SERVER_STORE \ - -storepass $SERVER_STORE_PWD -keytool \ - -exportcert \ - -alias $SERVER_ALIAS \ - -keystore $SERVER_STORE \ - -storepass $SERVER_STORE_PWD | - keytool \ - -importcert \ - -alias $SERVER_SELF_ALIAS \ - -keystore $SERVER_STORE \ - -storepass $SERVER_STORE_PWD \ - -noprompt - -echo -echo "${H2}Creating server certificate signed by root CA...${RESET}" -keytool \ - -certreq \ - -alias $SERVER_ALIAS \ - -dname "CN=$SERVER_CN, OU=$ORGANIZATIONAL_UNIT, O=$ORGANIZATION, L=$CITY, ST=$STATE_OR_PROVINCE, C=$TWO_LETTER_COUNTRY_CODE" \ - -keystore $SERVER_STORE \ - -storepass $SERVER_STORE_PWD | - keytool \ - -gencert \ - -alias $ROOT_KEY_ALIAS \ - -keystore $SERVER_STORE \ - -storepass $SERVER_STORE_PWD \ - -storetype $STORETYPE \ - -validity $VALIDITY | - keytool \ - -importcert \ - -alias $SERVER_ALIAS \ - -keystore $SERVER_STORE \ - -storepass $SERVER_STORE_PWD - -echo -echo "${H2}Creating bootstrap key and self-signed certificate ...${RESET}" -keytool \ - -genkeypair \ - -alias $BOOTSTRAP_ALIAS \ - -keyalg EC \ - -dname "CN=$BOOTSTRAP_SELF_CN, OU=$ORGANIZATIONAL_UNIT, O=$ORGANIZATION, L=$CITY, ST=$STATE_OR_PROVINCE, C=$TWO_LETTER_COUNTRY_CODE" \ - -validity $VALIDITY \ - -storetype $STORETYPE \ - -keypass $SERVER_STORE_PWD \ - -keystore $SERVER_STORE \ - -storepass $SERVER_STORE_PWD -keytool \ - -exportcert \ - -alias $BOOTSTRAP_ALIAS \ - -keystore $SERVER_STORE \ - -storepass $SERVER_STORE_PWD | - keytool \ - -importcert \ - -alias $BOOTSTRAP_SELF_ALIAS \ - -keystore $SERVER_STORE \ - -storepass $SERVER_STORE_PWD \ - -noprompt - -echo -echo "${H2}Creating bootstrap certificate signed by root CA...${RESET}" -keytool \ - -certreq \ - -alias $BOOTSTRAP_ALIAS \ - -dname "CN=$BOOTSTRAP_CN, OU=$ORGANIZATIONAL_UNIT, O=$ORGANIZATION, L=$CITY, ST=$STATE_OR_PROVINCE, C=$TWO_LETTER_COUNTRY_CODE" \ - -keystore $SERVER_STORE \ - -storepass $SERVER_STORE_PWD | - keytool \ - -gencert \ - -alias $ROOT_KEY_ALIAS \ - -keystore $SERVER_STORE \ - -storepass $SERVER_STORE_PWD \ - -storetype $STORETYPE \ - -validity $VALIDITY | - keytool \ - -importcert \ - -alias $BOOTSTRAP_ALIAS \ - -keystore $SERVER_STORE \ - -storepass $SERVER_STORE_PWD - -if [ "$client_start" -lt "$client_finish" ]; then - echo - echo "${H2}Import root certificate just to be able to import need by root CA with expected CN to $CLIENT_STORE${RESET}" - keytool \ - -exportcert \ - -alias $ROOT_KEY_ALIAS \ - -keystore $SERVER_STORE \ - -storepass $SERVER_STORE_PWD | - keytool \ - -importcert \ - -alias $ROOT_KEY_ALIAS \ - -keystore $CLIENT_STORE \ - -storepass $CLIENT_STORE_PWD \ - -noprompt -fi - -cert_end_point() { - echo - echo "${H1}Client Keystore : ${RESET}" - echo "${H1}==================${RESET}" - echo "${H2}Creating client key and self-signed certificate with expected CN CLIENT_ALIAS: $CLIENT_ALIAS${RESET}" - keytool \ - -genkeypair \ - -alias $CLIENT_ALIAS \ - -keyalg EC \ - -dname "CN=$CLIENT_SELF_CN, OU=$ORGANIZATIONAL_UNIT, O=$ORGANIZATION, L=$CITY, ST=$STATE_OR_PROVINCE, C=$TWO_LETTER_COUNTRY_CODE" \ - -validity $VALIDITY \ - -storetype $STORETYPE \ - -keypass $CLIENT_STORE_PWD \ - -keystore $CLIENT_STORE \ - -storepass $CLIENT_STORE_PWD - keytool \ - -exportcert \ - -alias $CLIENT_ALIAS \ - -keystore $CLIENT_STORE \ - -storepass $CLIENT_STORE_PWD | - keytool \ - -importcert \ - -alias $CLIENT_SELF_ALIAS \ - -keystore $CLIENT_STORE \ - -storepass $CLIENT_STORE_PWD \ - -noprompt -# -# echo -# echo "${H2}Import root certificate just to be able to import ned by root CA with expected CN...${RESET}" -# keytool \ -# -exportcert \ -# -alias $ROOT_KEY_ALIAS \ -# -keystore $SERVER_STORE \ -# -storepass $SERVER_STORE_PWD | -# keytool \ -# -importcert \ -# -alias $ROOT_KEY_ALIAS \ -# -keystore $CLIENT_STORE \ -# -storepass $CLIENT_STORE_PWD \ -# -noprompt -# - - echo - echo "${H2}Creating client certificate signed by root CA with expected CN CLIENT_ALIAS: $CLIENT_ALIAS CLIENT_CN: $CLIENT_CN${RESET}" - keytool \ - -certreq \ - -alias $CLIENT_ALIAS \ - -dname "CN=$CLIENT_CN, OU=$ORGANIZATIONAL_UNIT, O=$ORGANIZATION, L=$CITY, ST=$STATE_OR_PROVINCE, C=$TWO_LETTER_COUNTRY_CODE" \ - -keystore $CLIENT_STORE \ - -storepass $CLIENT_STORE_PWD | - keytool \ - -gencert \ - -alias $ROOT_KEY_ALIAS \ - -keystore $SERVER_STORE \ - -storepass $SERVER_STORE_PWD \ - -storetype $STORETYPE \ - -validity $VALIDITY | - keytool \ - -importcert \ - -alias $CLIENT_ALIAS \ - -keystore $CLIENT_STORE \ - -storepass $CLIENT_STORE_PWD \ - -noprompt -} - -if [ "$client_start" -lt "$client_finish" ]; then - - echo - echo "==Start Client==" - while [ "$CLIENT_NUMBER" -lt "$client_finish" ]; do - echo "number $CLIENT_NUMBER" - echo "finish $client_finish" - CLIENT_CN=$(end_point) - CLIENT_ALIAS=$(client_alias_point) - CLIENT_SELF_ALIAS=$(client_self_alias_point) - echo "CLIENT_CN $CLIENT_CN" - echo "CLIENT_ALIAS $CLIENT_ALIAS" - echo "CLIENT_SELF_ALIAS $CLIENT_SELF_ALIAS" - cert_end_point - CLIENT_NUMBER=$(($CLIENT_NUMBER + 1)) - echo - done -fi - -echo -echo "${H0}!!! Warning ${H2}Migrate ${H1}${SERVER_STORE} ${H2}to ${H1}PKCS12 ${H2}which is an industry standard format..${RESET}" -keytool \ - -importkeystore \ - -srckeystore $SERVER_STORE \ - -destkeystore $SERVER_STORE \ - -deststoretype pkcs12 \ - -srcstorepass $SERVER_STORE_PWD - -if [ "$client_start" -lt "$client_finish" ]; then - echo - echo "${H0}!!! Warning ${H2}Migrate ${H1}${CLIENT_STORE} ${H2}to ${H1}PKCS12 ${H2}which is an industry standard format..${RESET}" - keytool \ - -importkeystore \ - -srckeystore $CLIENT_STORE \ - -destkeystore $CLIENT_STORE \ - -deststoretype pkcs12 \ - -srcstorepass $CLIENT_STORE_PWD -fi diff --git a/transport/lwm2m/src/main/data/credentials/shell/lwM2M_keygen.properties b/transport/lwm2m/src/main/data/credentials/shell/lwM2M_keygen.properties deleted file mode 100644 index 7b3cd9c09a..0000000000 --- a/transport/lwm2m/src/main/data/credentials/shell/lwM2M_keygen.properties +++ /dev/null @@ -1,57 +0,0 @@ -# -# Copyright © 2016-2017 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. -# - -# Keystore common parameters -ROOT_KEY_ALIAS=rootCA -DOMAIN_SUFFIX="$(hostname)" -ROOT_CN="$DOMAIN_SUFFIX $ROOT_KEY_ALIAS" -ORGANIZATIONAL_UNIT=Thingsboard -ORGANIZATION=Thingsboard -CITY=SF -STATE_OR_PROVINCE=CA -TWO_LETTER_COUNTRY_CODE=US -VALIDITY=36500 #days -STORETYPE="JKS" - -#Server -SERVER_STORE=serverKeyStore1.jks -SERVER_STORE_PWD=server_ks_password1 -SERVER_ALIAS=server1 -SERVER_CN="$DOMAIN_SUFFIX server LwM2M signed by root CA" -SERVER_SELF_ALIAS=server_self_signed -SERVER_SELF_CN="$DOMAIN_SUFFIX server LwM2M self-signed" -BOOTSTRAP_ALIAS=bootstrap1 -BOOTSTRAP_CN="$DOMAIN_SUFFIX bootstrap server LwM2M signed by root CA" -BOOTSTRAP_SELF_ALIAS=bootstrap_self_signed -BOOTSTRAP_SELF_CN="$DOMAIN_SUFFIX bootstrap server LwM2M self-signed" - -# Client -CLIENT_STORE=clientKeyStore1.jks -CLIENT_STORE_PWD=client_ks_password1 -CLIENT_ALIAS_PREFIX=client_alias_1 -CLIENT_PREFIX=LwX509___ -CLIENT_SELF_ALIAS_PREFIX=client_self_signed_1 -CLIENT_SELF_CN="$DOMAIN_SUFFIX client LwM2M self-signed" - -# Color output stuff -red=`tput setaf 1` -green=`tput setaf 2` -blue=`tput setaf 4` -bold=`tput bold` -H0=${red}${bold} -H1=${green}${bold} -H2=${blue} -RESET=`tput sgr0` diff --git a/transport/lwm2m/src/main/data/lwm2mserver.jks b/transport/lwm2m/src/main/data/lwm2mserver.jks deleted file mode 100644 index 301f4e2c3ab90931eb166859e71b2c443fa35660..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4017 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 diff --git a/transport/lwm2m/src/main/resources/tb-lwm2m-transport.yml b/transport/lwm2m/src/main/resources/tb-lwm2m-transport.yml index 631968ad35..d7513bdf1f 100644 --- a/transport/lwm2m/src/main/resources/tb-lwm2m-transport.yml +++ b/transport/lwm2m/src/main/resources/tb-lwm2m-transport.yml @@ -114,7 +114,7 @@ transport: # Server X509 Certificates support credentials: # Whether to enable LWM2M server X509 Certificate/RPK support - enabled: "${LWM2M_SERVER_CREDENTIALS_ENABLED:true}" + enabled: "${LWM2M_SERVER_CREDENTIALS_ENABLED:false}" # Server credentials type (PEM - pem certificate file; KEYSTORE - java keystore) type: "${LWM2M_SERVER_CREDENTIALS_TYPE:PEM}" # PEM server credentials @@ -150,7 +150,7 @@ transport: # Bootstrap server X509 Certificates support credentials: # Whether to enable LWM2M bootstrap server X509 Certificate/RPK support - enabled: "${LWM2M_BS_CREDENTIALS_ENABLED:true}" + enabled: "${LWM2M_BS_CREDENTIALS_ENABLED:false}" # Server credentials type (PEM - pem certificate file; KEYSTORE - java keystore) type: "${LWM2M_BS_CREDENTIALS_TYPE:PEM}" # PEM server credentials @@ -177,19 +177,19 @@ transport: # X509 trust certificates trust-credentials: # Whether to load X509 trust certificates - enabled: "${LWM2M_TRUST_CREDENTIALS_ENABLED:true}" + enabled: "${LWM2M_TRUST_CREDENTIALS_ENABLED:false}" # Trust certificates store type (PEM - pem certificates file; KEYSTORE - java keystore) type: "${LWM2M_TRUST_CREDENTIALS_TYPE:PEM}" # PEM certificates pem: # Path to the certificates file (holds trust certificates) - cert_file: "${LWM2M_TRUST_PEM_CERT:lwm2mserver.pem}" + cert_file: "${LWM2M_TRUST_PEM_CERT:lwm2mtruststorechain.pem}" # Keystore with trust certificates keystore: # Type of the key store type: "${LWM2M_TRUST_KEY_STORE_TYPE:JKS}" # Path to the key store that holds the X509 certificates - store_file: "${LWM2M_TRUST_KEY_STORE:lwm2mserver.jks}" + store_file: "${LWM2M_TRUST_KEY_STORE:lwm2mtruststorechain.jks}" # Password used to access the key store store_password: "${LWM2M_TRUST_KEY_STORE_PASSWORD:server_ks_password}" recommended_ciphers: "${LWM2M_RECOMMENDED_CIPHERS:false}" From ff56e0e6220e349515f929159503f49e3695ef9f Mon Sep 17 00:00:00 2001 From: van-vanich Date: Thu, 30 Dec 2021 17:48:51 +0200 Subject: [PATCH 041/798] realize solve problems for issue for with saving last ts --- .../rule/engine/telemetry/TbMsgTimeseriesNode.java | 4 ++-- .../engine/telemetry/TbMsgTimeseriesNodeConfiguration.java | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNode.java index 1d52b7cdfa..711b7c1558 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNode.java @@ -49,7 +49,7 @@ import java.util.concurrent.TimeUnit; "Timestamp in milliseconds will be taken from metadata.ts, otherwise 'now' timestamp will be applied. " + "Allows stopping updating values for incoming keys in the latest ts_kv table if 'skipLatestPersistence' is set to true.", uiResources = {"static/rulenode/rulenode-core-config.js"}, - configDirective = "tbActionNodeTimeseriesConfig", + configDirective = "tbActionNodeTimeseriesConfig!", icon = "file_upload" ) public class TbMsgTimeseriesNode implements TbNode { @@ -77,7 +77,7 @@ public class TbMsgTimeseriesNode implements TbNode { ctx.tellFailure(msg, new IllegalArgumentException("Unsupported msg type: " + msg.getType())); return; } - long ts = getTs(msg); + long ts = config.isSaveWithMsgTs() ? msg.getTs() : getTs(msg); String src = msg.getData(); Map> tsKvMap = JsonConverter.convertToTelemetry(new JsonParser().parse(src), ts); if (tsKvMap.isEmpty()) { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNodeConfiguration.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNodeConfiguration.java index bb661df905..89abac99fe 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNodeConfiguration.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNodeConfiguration.java @@ -23,12 +23,14 @@ public class TbMsgTimeseriesNodeConfiguration implements NodeConfiguration Date: Fri, 31 Dec 2021 13:20:40 +0200 Subject: [PATCH 042/798] UI: Fixed help link for OTA updates --- ui-ngx/src/app/shared/models/constants.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui-ngx/src/app/shared/models/constants.ts b/ui-ngx/src/app/shared/models/constants.ts index 9bf120f8b7..9b1b110a84 100644 --- a/ui-ngx/src/app/shared/models/constants.ts +++ b/ui-ngx/src/app/shared/models/constants.ts @@ -124,7 +124,7 @@ export const HelpLinks = { rulechains: helpBaseUrl + '/docs/user-guide/ui/rule-chains', resources: helpBaseUrl + '/docs/user-guide/ui/resources', dashboards: helpBaseUrl + '/docs/user-guide/ui/dashboards', - otaUpdates: helpBaseUrl + '/docs/user-guide/ui/ota-updates', + otaUpdates: helpBaseUrl + '/docs/user-guide/ota-updates', widgetsBundles: helpBaseUrl + '/docs/user-guide/ui/widget-library#bundles', widgetsConfig: helpBaseUrl + '/docs/user-guide/ui/dashboards#widget-configuration', widgetsConfigTimeseries: helpBaseUrl + '/docs/user-guide/ui/dashboards#timeseries', From 443bb2280fc62f0fbb4ba023d869e2855b7c233f Mon Sep 17 00:00:00 2001 From: desoliture Date: Fri, 31 Dec 2021 13:27:52 +0200 Subject: [PATCH 043/798] add corresponding test --- .../server/msa/AbstractContainerTest.java | 2 + .../connectivity/MqttGatewayClientTest.java | 71 +++++++++++++++++++ 2 files changed, 73 insertions(+) diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/AbstractContainerTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/AbstractContainerTest.java index 412cf3094b..e7dc6f2c44 100644 --- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/AbstractContainerTest.java +++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/AbstractContainerTest.java @@ -22,6 +22,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.collect.ImmutableMap; import com.google.gson.JsonArray; import com.google.gson.JsonObject; +import com.google.gson.JsonParser; import lombok.extern.slf4j.Slf4j; import org.apache.cassandra.cql3.Json; import org.apache.commons.lang3.RandomStringUtils; @@ -69,6 +70,7 @@ public abstract class AbstractContainerTest { protected static String TB_TOKEN; protected static RestClient restClient; protected ObjectMapper mapper = new ObjectMapper(); + protected JsonParser jsonParser = new JsonParser(); @BeforeClass public static void before() throws Exception { diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/connectivity/MqttGatewayClientTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/connectivity/MqttGatewayClientTest.java index 4e7b7cd05b..dfdc68d9d5 100644 --- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/connectivity/MqttGatewayClientTest.java +++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/connectivity/MqttGatewayClientTest.java @@ -20,6 +20,7 @@ import com.google.common.collect.Sets; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListeningExecutorService; import com.google.common.util.concurrent.MoreExecutors; +import com.google.gson.JsonArray; import com.google.gson.JsonObject; import com.google.gson.JsonParser; import io.netty.buffer.ByteBuf; @@ -151,6 +152,76 @@ public class MqttGatewayClientTest extends AbstractContainerTest { Assert.assertTrue(verify(actualLatestTelemetry, "attr4", Long.toString(73))); } + @Test + public void responseDataOnAttributesRequestCheck() throws Exception { + Optional createdDeviceCredentials = restClient.getDeviceCredentialsByDeviceId(createdDevice.getId()); + Assert.assertTrue(createdDeviceCredentials.isPresent()); + WsClient wsClient = subscribeToWebSocket(createdDevice.getId(), "CLIENT_SCOPE", CmdsType.ATTR_SUB_CMDS); + JsonObject sharedAttributes = new JsonObject(); + sharedAttributes.addProperty("attr1", "value1"); + sharedAttributes.addProperty("attr2", true); + sharedAttributes.addProperty("attr3", 42.0); + sharedAttributes.addProperty("attr4", 73); + + mqttClient.on("v1/gateway/attributes/response", listener, MqttQoS.AT_LEAST_ONCE).get(); + ResponseEntity sharedAttributesResponse = restClient.getRestTemplate() + .postForEntity(HTTPS_URL + "/api/plugins/telemetry/DEVICE/{deviceId}/SHARED_SCOPE", + mapper.readTree(sharedAttributes.toString()), ResponseEntity.class, + createdDevice.getId()); + Assert.assertTrue(sharedAttributesResponse.getStatusCode().is2xxSuccessful()); + var event = listener.getEvents().poll(10, TimeUnit.SECONDS); + + JsonObject requestData = new JsonObject(); + requestData.addProperty("id", 1); + requestData.addProperty("device", createdDevice.getName()); + requestData.addProperty("client", false); + requestData.addProperty("key", "attr1"); + + mqttClient.on("v1/gateway/attributes/response", listener, MqttQoS.AT_LEAST_ONCE).get(); + mqttClient.publish("v1/gateway/attributes/request", Unpooled.wrappedBuffer(requestData.toString().getBytes())).get(); + event = listener.getEvents().poll(10, TimeUnit.SECONDS); + + JsonObject responseData = jsonParser.parse(Objects.requireNonNull(event).getMessage()).getAsJsonObject(); + Assert.assertTrue(responseData.has("value")); + Assert.assertEquals(sharedAttributes.get("attr1").getAsString(), responseData.get("value").getAsString()); + + requestData = new JsonObject(); + requestData.addProperty("id", 1); + requestData.addProperty("device", createdDevice.getName()); + requestData.addProperty("client", false); + JsonArray keys = new JsonArray(); + keys.add("attr1"); + keys.add("attr2"); + requestData.add("keys", keys); + + mqttClient.on("v1/gateway/attributes/response", listener, MqttQoS.AT_LEAST_ONCE).get(); + mqttClient.publish("v1/gateway/attributes/request", Unpooled.wrappedBuffer(requestData.toString().getBytes())).get(); + event = listener.getEvents().poll(10, TimeUnit.SECONDS); + responseData = jsonParser.parse(Objects.requireNonNull(event).getMessage()).getAsJsonObject(); + + Assert.assertTrue(responseData.has("values")); + Assert.assertEquals(sharedAttributes.get("attr1").getAsString(), responseData.get("values").getAsJsonObject().get("attr1").getAsString()); + Assert.assertEquals(sharedAttributes.get("attr2").getAsString(), responseData.get("values").getAsJsonObject().get("attr2").getAsString()); + + requestData = new JsonObject(); + requestData.addProperty("id", 1); + requestData.addProperty("device", createdDevice.getName()); + requestData.addProperty("client", false); + keys = new JsonArray(); + keys.add("attr1"); + keys.add("undefined"); + requestData.add("keys", keys); + + mqttClient.on("v1/gateway/attributes/response", listener, MqttQoS.AT_LEAST_ONCE).get(); + mqttClient.publish("v1/gateway/attributes/request", Unpooled.wrappedBuffer(requestData.toString().getBytes())).get(); + event = listener.getEvents().poll(10, TimeUnit.SECONDS); + responseData = jsonParser.parse(Objects.requireNonNull(event).getMessage()).getAsJsonObject(); + + Assert.assertTrue(responseData.has("values")); + Assert.assertEquals(sharedAttributes.get("attr1").getAsString(), responseData.get("values").getAsJsonObject().get("attr1").getAsString()); + Assert.assertEquals(1, responseData.get("values").getAsJsonObject().entrySet().size()); + } + @Test public void requestAttributeValuesFromServer() throws Exception { WsClient wsClient = subscribeToWebSocket(createdDevice.getId(), "CLIENT_SCOPE", CmdsType.ATTR_SUB_CMDS); From 8ec7fd5de8ce0777f15ab9d4ad461e868aaeb50e Mon Sep 17 00:00:00 2001 From: Volodymyr Babak Date: Mon, 3 Jan 2022 15:38:40 +0200 Subject: [PATCH 044/798] Removed unused relation dao code --- .../dao/sql/relation/JpaRelationDao.java | 25 ------------------- 1 file changed, 25 deletions(-) diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/relation/JpaRelationDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/relation/JpaRelationDao.java index fbedef6c4e..a7a1ca6a27 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/relation/JpaRelationDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/relation/JpaRelationDao.java @@ -203,29 +203,4 @@ public class JpaRelationDao extends JpaAbstractDaoListeningExecutorService imple public List findRuleNodeToRuleChainRelations(RuleChainType ruleChainType, int limit) { return DaoUtil.convertDataList(relationRepository.findRuleNodeToRuleChainRelations(ruleChainType, PageRequest.of(0, limit))); } - - private Specification getEntityFieldsSpec(EntityId from, String relationType, RelationTypeGroup typeGroup, EntityType childType) { - return (root, criteriaQuery, criteriaBuilder) -> { - List predicates = new ArrayList<>(); - if (from != null) { - Predicate fromIdPredicate = criteriaBuilder.equal(root.get("fromId"), from.getId()); - predicates.add(fromIdPredicate); - Predicate fromEntityTypePredicate = criteriaBuilder.equal(root.get("fromType"), from.getEntityType().name()); - predicates.add(fromEntityTypePredicate); - } - if (relationType != null) { - Predicate relationTypePredicate = criteriaBuilder.equal(root.get("relationType"), relationType); - predicates.add(relationTypePredicate); - } - if (typeGroup != null) { - Predicate typeGroupPredicate = criteriaBuilder.equal(root.get("relationTypeGroup"), typeGroup.name()); - predicates.add(typeGroupPredicate); - } - if (childType != null) { - Predicate childTypePredicate = criteriaBuilder.equal(root.get("toType"), childType.name()); - predicates.add(childTypePredicate); - } - return criteriaBuilder.and(predicates.toArray(new Predicate[0])); - }; - } } From bbab11d5aa2d01a4d11a5d92bdefc3047ea5466f Mon Sep 17 00:00:00 2001 From: ArtemDzhereleiko Date: Tue, 4 Jan 2022 13:03:26 +0200 Subject: [PATCH 045/798] UI: Fixed cancel event on color picker --- .../shared/components/dialog/color-picker-dialog.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui-ngx/src/app/shared/components/dialog/color-picker-dialog.component.ts b/ui-ngx/src/app/shared/components/dialog/color-picker-dialog.component.ts index 499f857df8..2374749cbc 100644 --- a/ui-ngx/src/app/shared/components/dialog/color-picker-dialog.component.ts +++ b/ui-ngx/src/app/shared/components/dialog/color-picker-dialog.component.ts @@ -67,7 +67,7 @@ export class ColorPickerDialogComponent extends DialogComponent Date: Tue, 4 Jan 2022 17:46:46 +0200 Subject: [PATCH 046/798] add backend support for dynamic values for schedules in alarm rules --- .../device/profile/CustomTimeSchedule.java | 3 ++ .../device/profile/SpecificTimeSchedule.java | 3 ++ .../transport/adaptor/JsonConverter.java | 4 ++ .../rule/engine/profile/AlarmRuleState.java | 41 ++++++++++++++----- .../rule/engine/profile/ProfileState.java | 24 +++++++++++ 5 files changed, 64 insertions(+), 11 deletions(-) diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/CustomTimeSchedule.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/CustomTimeSchedule.java index 89bf54eb25..83e8b3874e 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/CustomTimeSchedule.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/CustomTimeSchedule.java @@ -16,6 +16,7 @@ package org.thingsboard.server.common.data.device.profile; import lombok.Data; +import org.thingsboard.server.common.data.query.DynamicValue; import java.util.List; @@ -25,6 +26,8 @@ public class CustomTimeSchedule implements AlarmSchedule { private String timezone; private List items; + private DynamicValue dynamicValue; + @Override public AlarmScheduleType getType() { return AlarmScheduleType.CUSTOM; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/SpecificTimeSchedule.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/SpecificTimeSchedule.java index 9ef17f25b0..57e57a1b24 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/SpecificTimeSchedule.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/SpecificTimeSchedule.java @@ -16,6 +16,7 @@ package org.thingsboard.server.common.data.device.profile; import lombok.Data; +import org.thingsboard.server.common.data.query.DynamicValue; import java.util.List; import java.util.Set; @@ -28,6 +29,8 @@ public class SpecificTimeSchedule implements AlarmSchedule { private long startsOn; private long endsOn; + private DynamicValue dynamicValue; + @Override public AlarmScheduleType getType() { return AlarmScheduleType.SPECIFIC_TIME; diff --git a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/adaptor/JsonConverter.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/adaptor/JsonConverter.java index be4143e388..af2ac148ce 100644 --- a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/adaptor/JsonConverter.java +++ b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/adaptor/JsonConverter.java @@ -573,6 +573,10 @@ public class JsonConverter { return JSON_PARSER.parse(json); } + public static T parse(String json, Class clazz) { + return fromJson(parse(json), clazz); + } + public static String toJson(JsonElement element) { return GSON.toJson(element); } diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/AlarmRuleState.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/AlarmRuleState.java index 8ae6390b31..724b10c269 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/AlarmRuleState.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/AlarmRuleState.java @@ -40,6 +40,7 @@ import org.thingsboard.server.common.data.query.KeyFilterPredicate; import org.thingsboard.server.common.data.query.NumericFilterPredicate; import org.thingsboard.server.common.data.query.StringFilterPredicate; import org.thingsboard.server.common.msg.tools.SchedulerUtils; +import org.thingsboard.server.common.transport.adaptor.JsonConverter; import java.time.Instant; import java.time.ZoneId; @@ -115,7 +116,7 @@ class AlarmRuleState { } public AlarmEvalResult eval(DataSnapshot data) { - boolean active = isActive(data.getTs()); + boolean active = isActive(data, data.getTs()); switch (spec.getType()) { case SIMPLE: return (active && eval(alarmRule.getCondition(), data)) ? AlarmEvalResult.TRUE : AlarmEvalResult.FALSE; @@ -128,7 +129,7 @@ class AlarmRuleState { } } - private boolean isActive(long eventTs) { + private boolean isActive(DataSnapshot data, long eventTs) { if (eventTs == 0L) { eventTs = System.currentTimeMillis(); } @@ -138,10 +139,28 @@ class AlarmRuleState { switch (alarmRule.getSchedule().getType()) { case ANY_TIME: return true; - case SPECIFIC_TIME: - return isActiveSpecific((SpecificTimeSchedule) alarmRule.getSchedule(), eventTs); - case CUSTOM: - return isActiveCustom((CustomTimeSchedule) alarmRule.getSchedule(), eventTs); + case SPECIFIC_TIME: { + SpecificTimeSchedule originalSchedule = (SpecificTimeSchedule) alarmRule.getSchedule(); + EntityKeyValue dynamicValue = getDynamicValue(data, originalSchedule.getDynamicValue()); + + if (dynamicValue != null) { + SpecificTimeSchedule schedule = JsonConverter.parse(dynamicValue.getJsonValue(), SpecificTimeSchedule.class); + originalSchedule = schedule == null ? originalSchedule : schedule; + } + + return isActiveSpecific(originalSchedule, eventTs); + } + case CUSTOM: { + CustomTimeSchedule originalSchedule = (CustomTimeSchedule) alarmRule.getSchedule(); + EntityKeyValue dynamicValue = getDynamicValue(data, originalSchedule.getDynamicValue()); + + if (dynamicValue != null) { + CustomTimeSchedule schedule = JsonConverter.parse(dynamicValue.getJsonValue(), CustomTimeSchedule.class); + originalSchedule = schedule == null ? originalSchedule : schedule; + } + + return isActiveCustom(originalSchedule, eventTs); + } default: throw new RuntimeException("Unsupported schedule type: " + alarmRule.getSchedule().getType()); } @@ -236,7 +255,7 @@ class AlarmRuleState { if (repeating.getPredicate().getDynamicValue() != null && repeating.getPredicate().getDynamicValue().getSourceAttribute() != null) { - EntityKeyValue repeatingKeyValue = getDynamicPredicateValue(data, repeating.getPredicate().getDynamicValue()); + EntityKeyValue repeatingKeyValue = getDynamicValue(data, repeating.getPredicate().getDynamicValue()); if (repeatingKeyValue != null) { repeatingTimes = repeatingKeyValue.getLngValue(); } @@ -257,7 +276,7 @@ class AlarmRuleState { if (duration.getPredicate().getDynamicValue() != null && duration.getPredicate().getDynamicValue().getSourceAttribute() != null) { - EntityKeyValue durationKeyValue = getDynamicPredicateValue(data, duration.getPredicate().getDynamicValue()); + EntityKeyValue durationKeyValue = getDynamicValue(data, duration.getPredicate().getDynamicValue()); if (durationKeyValue != null) { durationTimeInMs = timeUnit.toMillis(durationKeyValue.getLngValue()); } @@ -276,7 +295,7 @@ class AlarmRuleState { long requiredDurationInMs = resolveRequiredDurationInMs(dataSnapshot); if (requiredDurationInMs > 0 && state.getLastEventTs() > 0 && ts > state.getLastEventTs()) { long duration = state.getDuration() + (ts - state.getLastEventTs()); - if (isActive(ts)) { + if (isActive(dataSnapshot, ts)) { return duration > requiredDurationInMs ? AlarmEvalResult.TRUE : AlarmEvalResult.NOT_YET_TRUE; } else { return AlarmEvalResult.FALSE; @@ -443,7 +462,7 @@ class AlarmRuleState { } private T getPredicateValue(DataSnapshot data, FilterPredicateValue value, AlarmConditionFilter filter, Function transformFunction) { - EntityKeyValue ekv = getDynamicPredicateValue(data, value.getDynamicValue()); + EntityKeyValue ekv = getDynamicValue(data, value.getDynamicValue()); if (ekv != null) { T result = transformFunction.apply(ekv); if (result != null) { @@ -457,7 +476,7 @@ class AlarmRuleState { } } - private EntityKeyValue getDynamicPredicateValue(DataSnapshot data, DynamicValue value) { + private EntityKeyValue getDynamicValue(DataSnapshot data, DynamicValue value) { EntityKeyValue ekv = null; if (value != null) { switch (value.getSourceType()) { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/ProfileState.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/ProfileState.java index 0cd4ed63ee..6d3c48892c 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/ProfileState.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/ProfileState.java @@ -27,6 +27,9 @@ import org.thingsboard.server.common.data.device.profile.AlarmRule; import org.thingsboard.server.common.data.device.profile.DeviceProfileAlarm; import org.thingsboard.server.common.data.device.profile.DurationAlarmConditionSpec; import org.thingsboard.server.common.data.device.profile.RepeatingAlarmConditionSpec; +import org.thingsboard.server.common.data.device.profile.CustomTimeSchedule; +import org.thingsboard.server.common.data.device.profile.SpecificTimeSchedule; +import org.thingsboard.server.common.data.device.profile.AlarmSchedule; import org.thingsboard.server.common.data.id.DeviceProfileId; import org.thingsboard.server.common.data.query.ComplexFilterPredicate; import org.thingsboard.server.common.data.query.DynamicValue; @@ -77,6 +80,7 @@ class ProfileState { addDynamicValuesRecursively(keyFilter.getPredicate(), entityKeys, ruleKeys); } addEntityKeysFromAlarmConditionSpec(alarmRule); + addScheduleDynamicValues(alarmRule.getSchedule()); })); if (alarm.getClearRule() != null) { var clearAlarmKeys = alarmClearKeys.computeIfAbsent(alarm.getId(), id -> new HashSet<>()); @@ -91,6 +95,26 @@ class ProfileState { } } + private void addScheduleDynamicValues(AlarmSchedule schedule) { + DynamicValue dynamicValue = null; + switch (schedule.getType()) { + case SPECIFIC_TIME: + SpecificTimeSchedule specSchedule = (SpecificTimeSchedule) schedule; + dynamicValue = specSchedule.getDynamicValue(); + break; + case CUSTOM: + CustomTimeSchedule custSchedule = (CustomTimeSchedule) schedule; + dynamicValue = custSchedule.getDynamicValue(); + } + + if (dynamicValue != null) { + entityKeys.add( + new AlarmConditionFilterKey(AlarmConditionKeyType.ATTRIBUTE, + dynamicValue.getSourceAttribute()) + ); + } + } + private void addEntityKeysFromAlarmConditionSpec(AlarmRule alarmRule) { AlarmConditionSpec spec = alarmRule.getCondition().getSpec(); if (spec == null) { From 2b203bfdd59cc7b2455411bc6230c2fdec9c5689 Mon Sep 17 00:00:00 2001 From: desoliture Date: Wed, 5 Jan 2022 13:24:53 +0200 Subject: [PATCH 047/798] fix notNull checking for schedule in ProfileState, rename variables and fix logic for parsing dynamicValues in AlarmRuleState --- .../rule/engine/profile/AlarmRuleState.java | 28 +++++++++++-------- .../rule/engine/profile/ProfileState.java | 14 ++++++---- 2 files changed, 25 insertions(+), 17 deletions(-) diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/AlarmRuleState.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/AlarmRuleState.java index 724b10c269..c5ad4607b2 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/AlarmRuleState.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/AlarmRuleState.java @@ -140,26 +140,30 @@ class AlarmRuleState { case ANY_TIME: return true; case SPECIFIC_TIME: { - SpecificTimeSchedule originalSchedule = (SpecificTimeSchedule) alarmRule.getSchedule(); - EntityKeyValue dynamicValue = getDynamicValue(data, originalSchedule.getDynamicValue()); + SpecificTimeSchedule defaultSchedule = (SpecificTimeSchedule) alarmRule.getSchedule(); + EntityKeyValue dynamicValue = getDynamicValue(data, defaultSchedule.getDynamicValue()); - if (dynamicValue != null) { - SpecificTimeSchedule schedule = JsonConverter.parse(dynamicValue.getJsonValue(), SpecificTimeSchedule.class); - originalSchedule = schedule == null ? originalSchedule : schedule; + SpecificTimeSchedule schedule; + try { + schedule = JsonConverter.parse(dynamicValue.getJsonValue(), SpecificTimeSchedule.class); + } catch (Exception e) { + schedule = defaultSchedule; } - return isActiveSpecific(originalSchedule, eventTs); + return isActiveSpecific(schedule, eventTs); } case CUSTOM: { - CustomTimeSchedule originalSchedule = (CustomTimeSchedule) alarmRule.getSchedule(); - EntityKeyValue dynamicValue = getDynamicValue(data, originalSchedule.getDynamicValue()); + CustomTimeSchedule defaultSchedule = (CustomTimeSchedule) alarmRule.getSchedule(); + EntityKeyValue dynamicValue = getDynamicValue(data, defaultSchedule.getDynamicValue()); - if (dynamicValue != null) { - CustomTimeSchedule schedule = JsonConverter.parse(dynamicValue.getJsonValue(), CustomTimeSchedule.class); - originalSchedule = schedule == null ? originalSchedule : schedule; + CustomTimeSchedule schedule; + try { + schedule = JsonConverter.parse(dynamicValue.getJsonValue(), CustomTimeSchedule.class); + } catch (Exception e) { + schedule = defaultSchedule; } - return isActiveCustom(originalSchedule, eventTs); + return isActiveCustom(schedule, eventTs); } default: throw new RuntimeException("Unsupported schedule type: " + alarmRule.getSchedule().getType()); diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/ProfileState.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/ProfileState.java index 6d3c48892c..c8751d65f7 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/ProfileState.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/ProfileState.java @@ -80,7 +80,10 @@ class ProfileState { addDynamicValuesRecursively(keyFilter.getPredicate(), entityKeys, ruleKeys); } addEntityKeysFromAlarmConditionSpec(alarmRule); - addScheduleDynamicValues(alarmRule.getSchedule()); + AlarmSchedule schedule = alarmRule.getSchedule(); + if (schedule != null) { + addScheduleDynamicValues(schedule); + } })); if (alarm.getClearRule() != null) { var clearAlarmKeys = alarmClearKeys.computeIfAbsent(alarm.getId(), id -> new HashSet<>()); @@ -99,12 +102,13 @@ class ProfileState { DynamicValue dynamicValue = null; switch (schedule.getType()) { case SPECIFIC_TIME: - SpecificTimeSchedule specSchedule = (SpecificTimeSchedule) schedule; - dynamicValue = specSchedule.getDynamicValue(); + SpecificTimeSchedule specificTimeSchedule = (SpecificTimeSchedule) schedule; + dynamicValue = specificTimeSchedule.getDynamicValue(); break; case CUSTOM: - CustomTimeSchedule custSchedule = (CustomTimeSchedule) schedule; - dynamicValue = custSchedule.getDynamicValue(); + CustomTimeSchedule customTimeSchedule = (CustomTimeSchedule) schedule; + dynamicValue = customTimeSchedule.getDynamicValue(); + break; } if (dynamicValue != null) { From bb3c730e58c774ecf12e3ba39482fe91b2dba008 Mon Sep 17 00:00:00 2001 From: van-vanich Date: Wed, 5 Jan 2022 13:58:58 +0200 Subject: [PATCH 048/798] add test for sequential timeseries persistence and improve code for TbMsgTimeseriesNode rule node and config --- .../server/service/ServiceSqlTestSuite.java | 1 + .../SequentialTimeseriesPersistenceTest.java | 194 ++++++++++++++++++ .../engine/telemetry/TbMsgTimeseriesNode.java | 6 +- .../TbMsgTimeseriesNodeConfiguration.java | 4 +- 4 files changed, 202 insertions(+), 3 deletions(-) create mode 100644 application/src/test/java/org/thingsboard/server/service/sql/SequentialTimeseriesPersistenceTest.java diff --git a/application/src/test/java/org/thingsboard/server/service/ServiceSqlTestSuite.java b/application/src/test/java/org/thingsboard/server/service/ServiceSqlTestSuite.java index 508272085c..d3ad80f614 100644 --- a/application/src/test/java/org/thingsboard/server/service/ServiceSqlTestSuite.java +++ b/application/src/test/java/org/thingsboard/server/service/ServiceSqlTestSuite.java @@ -23,6 +23,7 @@ import org.thingsboard.server.queue.memory.InMemoryStorage; @RunWith(ClasspathSuite.class) @ClasspathSuite.ClassnameFilters({ "org.thingsboard.server.service.resource.sql.*Test", + "org.thingsboard.server.service.sql.*Test" }) public class ServiceSqlTestSuite { diff --git a/application/src/test/java/org/thingsboard/server/service/sql/SequentialTimeseriesPersistenceTest.java b/application/src/test/java/org/thingsboard/server/service/sql/SequentialTimeseriesPersistenceTest.java new file mode 100644 index 0000000000..9e6b6f0bc1 --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/service/sql/SequentialTimeseriesPersistenceTest.java @@ -0,0 +1,194 @@ +package org.thingsboard.server.service.sql; + +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import lombok.extern.slf4j.Slf4j; +import org.jetbrains.annotations.NotNull; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.thingsboard.rule.engine.telemetry.TbMsgTimeseriesNode; +import org.thingsboard.rule.engine.telemetry.TbMsgTimeseriesNodeConfiguration; +import org.thingsboard.server.common.data.Device; +import org.thingsboard.server.common.data.Tenant; +import org.thingsboard.server.common.data.User; +import org.thingsboard.server.common.data.asset.Asset; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.kv.BasicTsKvEntry; +import org.thingsboard.server.common.data.kv.JsonDataEntry; +import org.thingsboard.server.common.data.kv.LongDataEntry; +import org.thingsboard.server.common.data.kv.TsKvEntry; +import org.thingsboard.server.common.data.security.Authority; +import org.thingsboard.server.common.msg.TbMsg; +import org.thingsboard.server.common.msg.TbMsgDataType; +import org.thingsboard.server.common.msg.TbMsgMetaData; +import org.thingsboard.server.common.msg.session.SessionMsgType; +import org.thingsboard.server.controller.AbstractControllerTest; +import org.thingsboard.server.dao.service.DaoSqlTest; +import org.thingsboard.server.dao.timeseries.TimeseriesService; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@DaoSqlTest +@Slf4j +public class SequentialTimeseriesPersistenceTest extends AbstractControllerTest { + + static final int TIMEOUT = 30; + + final String TOTALIZER = "Totalizer"; + final int TTL = 99999; + final String GENERIC_CUMULATIVE_OBJ = "genericCumulativeObj"; + final List ts = List.of(10L, 20L, 30L, 40L, 60L, 70L, 50L, 80L); + final List msgValue = List.of(1L, 2L, 3L, 4L, 5L, 6L, 7L, 8L); + + @Autowired + TimeseriesService timeseriesService; + + TbMsgTimeseriesNodeConfiguration configuration; + Tenant savedTenant; + User tenantAdmin; + + @Before + public void beforeTest() throws Exception { + configuration = new TbMsgTimeseriesNodeConfiguration(); + configuration.setIgnoreMetadataTs(true); + + loginSysAdmin(); + + Tenant tenant = new Tenant(); + tenant.setTitle("My tenant"); + savedTenant = doPost("/api/tenant", tenant, Tenant.class); + Assert.assertNotNull(savedTenant); + + tenantAdmin = new User(); + tenantAdmin.setAuthority(Authority.TENANT_ADMIN); + tenantAdmin.setTenantId(savedTenant.getId()); + tenantAdmin.setEmail("tenant2@thingsboard.org"); + tenantAdmin.setFirstName("Joe"); + tenantAdmin.setLastName("Downs"); + + tenantAdmin = createUserAndLogin(tenantAdmin, "testPassword1"); + } + + @After + public void afterTest() throws Exception { + loginSysAdmin(); + doDelete("/api/tenant/" + savedTenant.getId().getId().toString()).andExpect(status().isOk()); + } + + @Test + public void testSequentialTimeseriesPersistence() throws Exception { + Asset asset = saveAsset("Asset"); + + Device deviceA = saveDevice("Device A"); + Device deviceB = saveDevice("Device B"); + Device deviceC = saveDevice("Device C"); + Device deviceD = saveDevice("Device D"); + List devices = List.of(deviceA, deviceB, deviceC, deviceD); + + for (int i = 0; i < 2; i++) { + int idx = i * (devices.size()); + saveLatestTsForAssetAndDevice(devices, asset, idx); + checkDiffBetweenLatestTsForDevicesAndAsset(devices, asset); + } + } + + Device saveDevice(String name) throws Exception { + Device device = new Device(); + device.setName(name); + device.setType("default"); + Device savedDevice = doPost("/api/device", device, Device.class); + Assert.assertNotNull(savedDevice); + return savedDevice; + } + + Asset saveAsset(String name) throws Exception { + Asset asset = new Asset(); + asset.setName(name); + asset.setType("default"); + Asset savedAsset = doPost("/api/asset", asset, Asset.class); + Assert.assertNotNull(savedAsset); + return savedAsset; + } + + private void saveLatestTsForAssetAndDevice(List devices, Asset asset, int idx) throws ExecutionException, InterruptedException, TimeoutException { + for (Device device : devices) { + TbMsg tbMsg = TbMsg.newMsg(SessionMsgType.POST_TELEMETRY_REQUEST.name(), + device.getId(), + getTbMsgMetadata(device.getName(), ts.get(idx)), + TbMsgDataType.JSON, + getTbMsgData(msgValue.get(idx))); + saveDeviceTsEntry(device.getId(), tbMsg, msgValue.get(idx)); + saveAssetTsEntry(asset, device.getName(), msgValue.get(idx), TbMsgTimeseriesNode.computeTs(tbMsg, configuration.isIgnoreMetadataTs())); + idx++; + } + } + + void checkDiffBetweenLatestTsForDevicesAndAsset(List devices, Asset asset) throws ExecutionException, InterruptedException, TimeoutException { + TsKvEntry assetTsKvEntry = getTsKvLatest(asset.getId(), GENERIC_CUMULATIVE_OBJ); + Assert.assertTrue(assetTsKvEntry.getJsonValue().isPresent()); + JsonObject assetJsonObject = new JsonParser().parse(assetTsKvEntry.getJsonValue().get()).getAsJsonObject(); + for (Device device : devices) { + Long assetValue = assetJsonObject.get(device.getName()).getAsLong(); + TsKvEntry deviceLatest = getTsKvLatest(device.getId(), TOTALIZER); + Assert.assertTrue(deviceLatest.getLongValue().isPresent()); + Long deviceValue = deviceLatest.getLongValue().get(); + Assert.assertEquals(assetValue, deviceValue); + } + } + + String getTbMsgData(long value) { + return "{\"Totalizer\": " + value + "}"; + } + + TbMsgMetaData getTbMsgMetadata(String name, long ts) { + Map metadata = new HashMap<>(); + metadata.put("deviceName", name); + metadata.put("ts", String.valueOf(ts)); + return new TbMsgMetaData(metadata); + } + + void saveDeviceTsEntry(EntityId entityId, TbMsg tbMsg, long value) throws ExecutionException, InterruptedException, TimeoutException { + TsKvEntry tsKvEntry = new BasicTsKvEntry(TbMsgTimeseriesNode.computeTs(tbMsg, configuration.isIgnoreMetadataTs()), new LongDataEntry(TOTALIZER, value)); + saveTimeseries(entityId, tsKvEntry); + } + + void saveAssetTsEntry(Asset asset, String key, long value, long ts) throws ExecutionException, InterruptedException, TimeoutException { + Optional tsKvEntryOpt = getTsKvLatest(asset.getId(), GENERIC_CUMULATIVE_OBJ).getJsonValue(); + TsKvEntry saveTsKvEntry = new BasicTsKvEntry(ts, new JsonDataEntry(GENERIC_CUMULATIVE_OBJ, getJsonObject(key, value, tsKvEntryOpt).toString())); + saveTimeseries(asset.getId(), saveTsKvEntry); + } + + @NotNull + private JsonObject getJsonObject(String key, long value, Optional tsKvEntryOpt) { + JsonObject jsonObject = new JsonObject(); + if (tsKvEntryOpt.isPresent()) { + jsonObject = new JsonParser().parse(tsKvEntryOpt.get()).getAsJsonObject(); + } + jsonObject.addProperty(key, value); + return jsonObject; + } + + private void saveTimeseries(EntityId entityId, TsKvEntry saveTsKvEntry) throws InterruptedException, ExecutionException, TimeoutException { + timeseriesService.save(savedTenant.getId(), entityId, List.of(saveTsKvEntry), TTL).get(TIMEOUT, TimeUnit.SECONDS); + } + + TsKvEntry getTsKvLatest(EntityId entityId, String key) throws InterruptedException, ExecutionException, TimeoutException { + List tsKvEntries = timeseriesService.findLatest( + savedTenant.getTenantId(), + entityId, + List.of(key)).get(TIMEOUT, TimeUnit.SECONDS); + Assert.assertEquals(1, tsKvEntries.size()); + return tsKvEntries.get(0); + } +} diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNode.java index 711b7c1558..47cf0f1bf2 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNode.java @@ -77,7 +77,7 @@ public class TbMsgTimeseriesNode implements TbNode { ctx.tellFailure(msg, new IllegalArgumentException("Unsupported msg type: " + msg.getType())); return; } - long ts = config.isSaveWithMsgTs() ? msg.getTs() : getTs(msg); + long ts = computeTs(msg, config.isIgnoreMetadataTs()); String src = msg.getData(); Map> tsKvMap = JsonConverter.convertToTelemetry(new JsonParser().parse(src), ts); if (tsKvMap.isEmpty()) { @@ -102,6 +102,10 @@ public class TbMsgTimeseriesNode implements TbNode { } } + public static long computeTs(TbMsg msg, boolean saveWithMsgTs) { + return saveWithMsgTs ? System.currentTimeMillis() : getTs(msg); + } + public static long getTs(TbMsg msg) { long ts = -1; String tsStr = msg.getMetaData().getValue("ts"); diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNodeConfiguration.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNodeConfiguration.java index 89abac99fe..6c9f7ac884 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNodeConfiguration.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNodeConfiguration.java @@ -23,14 +23,14 @@ public class TbMsgTimeseriesNodeConfiguration implements NodeConfiguration Date: Wed, 5 Jan 2022 14:48:45 +0200 Subject: [PATCH 049/798] improve node details and add license for test class --- .../sql/SequentialTimeseriesPersistenceTest.java | 15 +++++++++++++++ .../engine/telemetry/TbMsgTimeseriesNode.java | 15 ++++++++++----- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/application/src/test/java/org/thingsboard/server/service/sql/SequentialTimeseriesPersistenceTest.java b/application/src/test/java/org/thingsboard/server/service/sql/SequentialTimeseriesPersistenceTest.java index 9e6b6f0bc1..c360adf509 100644 --- a/application/src/test/java/org/thingsboard/server/service/sql/SequentialTimeseriesPersistenceTest.java +++ b/application/src/test/java/org/thingsboard/server/service/sql/SequentialTimeseriesPersistenceTest.java @@ -1,3 +1,18 @@ +/** + * Copyright © 2016-2021 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.service.sql; import com.google.gson.JsonObject; diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNode.java index 47cf0f1bf2..2334851a08 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNode.java @@ -46,10 +46,15 @@ import java.util.concurrent.TimeUnit; configClazz = TbMsgTimeseriesNodeConfiguration.class, nodeDescription = "Saves timeseries data", nodeDetails = "Saves timeseries telemetry data based on configurable TTL parameter. Expects messages with 'POST_TELEMETRY_REQUEST' message type. " + - "Timestamp in milliseconds will be taken from metadata.ts, otherwise 'now' timestamp will be applied. " + - "Allows stopping updating values for incoming keys in the latest ts_kv table if 'skipLatestPersistence' is set to true.", + "Timestamp in milliseconds will be taken from metadata.ts, otherwise 'now' message timestamp will be applied. " + + "Allows stopping updating values for incoming keys in the latest ts_kv table if 'skipLatestPersistence' is set to true.\n " + + "Enable 'ignoreMetadataTs' param to ignore the timestamp that arrives from message metadata. " + + "Useful for all sorts of sequential processing if you merge messages from multiple sources (devices, assets, etc).\n" + + "For example, if you count number of messages from multiple devices into asset time-series value. " + + "Typically, you fetch the previous value of the counter, increment it and then save the value. " + + "If you use timestamp of the original message, the value may be ignored, since it has outdated timestamp comparing to the previous message.", uiResources = {"static/rulenode/rulenode-core-config.js"}, - configDirective = "tbActionNodeTimeseriesConfig!", + configDirective = "tbActionNodeTimeseriesConfig", icon = "file_upload" ) public class TbMsgTimeseriesNode implements TbNode { @@ -102,8 +107,8 @@ public class TbMsgTimeseriesNode implements TbNode { } } - public static long computeTs(TbMsg msg, boolean saveWithMsgTs) { - return saveWithMsgTs ? System.currentTimeMillis() : getTs(msg); + public static long computeTs(TbMsg msg, boolean ignoreMetadataTs) { + return ignoreMetadataTs ? System.currentTimeMillis() : getTs(msg); } public static long getTs(TbMsg msg) { From 93bf95c17a63cf5e40e898172fb2a91d3f8ce28b Mon Sep 17 00:00:00 2001 From: van-vanich Date: Wed, 5 Jan 2022 15:00:13 +0200 Subject: [PATCH 050/798] improve code style --- .../sql/SequentialTimeseriesPersistenceTest.java | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/application/src/test/java/org/thingsboard/server/service/sql/SequentialTimeseriesPersistenceTest.java b/application/src/test/java/org/thingsboard/server/service/sql/SequentialTimeseriesPersistenceTest.java index c360adf509..e4fb0318b4 100644 --- a/application/src/test/java/org/thingsboard/server/service/sql/SequentialTimeseriesPersistenceTest.java +++ b/application/src/test/java/org/thingsboard/server/service/sql/SequentialTimeseriesPersistenceTest.java @@ -17,7 +17,6 @@ package org.thingsboard.server.service.sql; import com.google.gson.JsonObject; import com.google.gson.JsonParser; -import lombok.extern.slf4j.Slf4j; import org.jetbrains.annotations.NotNull; import org.junit.After; import org.junit.Assert; @@ -55,7 +54,6 @@ import java.util.concurrent.TimeoutException; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @DaoSqlTest -@Slf4j public class SequentialTimeseriesPersistenceTest extends AbstractControllerTest { static final int TIMEOUT = 30; @@ -112,7 +110,7 @@ public class SequentialTimeseriesPersistenceTest extends AbstractControllerTest List devices = List.of(deviceA, deviceB, deviceC, deviceD); for (int i = 0; i < 2; i++) { - int idx = i * (devices.size()); + int idx = i * devices.size(); saveLatestTsForAssetAndDevice(devices, asset, idx); checkDiffBetweenLatestTsForDevicesAndAsset(devices, asset); } @@ -136,7 +134,7 @@ public class SequentialTimeseriesPersistenceTest extends AbstractControllerTest return savedAsset; } - private void saveLatestTsForAssetAndDevice(List devices, Asset asset, int idx) throws ExecutionException, InterruptedException, TimeoutException { + void saveLatestTsForAssetAndDevice(List devices, Asset asset, int idx) throws ExecutionException, InterruptedException, TimeoutException { for (Device device : devices) { TbMsg tbMsg = TbMsg.newMsg(SessionMsgType.POST_TELEMETRY_REQUEST.name(), device.getId(), @@ -185,7 +183,7 @@ public class SequentialTimeseriesPersistenceTest extends AbstractControllerTest } @NotNull - private JsonObject getJsonObject(String key, long value, Optional tsKvEntryOpt) { + JsonObject getJsonObject(String key, long value, Optional tsKvEntryOpt) { JsonObject jsonObject = new JsonObject(); if (tsKvEntryOpt.isPresent()) { jsonObject = new JsonParser().parse(tsKvEntryOpt.get()).getAsJsonObject(); @@ -194,7 +192,7 @@ public class SequentialTimeseriesPersistenceTest extends AbstractControllerTest return jsonObject; } - private void saveTimeseries(EntityId entityId, TsKvEntry saveTsKvEntry) throws InterruptedException, ExecutionException, TimeoutException { + void saveTimeseries(EntityId entityId, TsKvEntry saveTsKvEntry) throws InterruptedException, ExecutionException, TimeoutException { timeseriesService.save(savedTenant.getId(), entityId, List.of(saveTsKvEntry), TTL).get(TIMEOUT, TimeUnit.SECONDS); } From b64eccf3332a2812a931ef697af44a5463d7f0b7 Mon Sep 17 00:00:00 2001 From: desoliture Date: Wed, 5 Jan 2022 16:24:12 +0200 Subject: [PATCH 051/798] add noXss validation for ruleNodes name --- .../java/org/thingsboard/server/common/data/rule/RuleNode.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleNode.java b/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleNode.java index 8201807808..8904d2821d 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleNode.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleNode.java @@ -27,6 +27,7 @@ import org.thingsboard.server.common.data.SearchTextBasedWithAdditionalInfo; import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.RuleNodeId; import org.thingsboard.server.common.data.validation.Length; +import org.thingsboard.server.common.data.validation.NoXss; @ApiModel @Data @@ -41,6 +42,7 @@ public class RuleNode extends SearchTextBasedWithAdditionalInfo impl @Length(fieldName = "type") @ApiModelProperty(position = 4, value = "Full Java Class Name of the rule node implementation. ", example = "com.mycompany.iot.rule.engine.ProcessingNode") private String type; + @NoXss @Length(fieldName = "name") @ApiModelProperty(position = 5, value = "User defined name of the rule node. Used on UI and for logging. ", example = "Process sensor reading") private String name; From ec602248c0cc22da1461e50176fdbca2ebf75519 Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Wed, 5 Jan 2022 16:28:37 +0200 Subject: [PATCH 052/798] lwm2m validate trust certs --- .../TbLwM2MDtlsCertificateVerifier.java | 50 ++++++++++--------- .../config/ssl/AbstractSslCredentials.java | 26 +++++++--- 2 files changed, 47 insertions(+), 29 deletions(-) diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/TbLwM2MDtlsCertificateVerifier.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/TbLwM2MDtlsCertificateVerifier.java index bae7522c78..babf385bc8 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/TbLwM2MDtlsCertificateVerifier.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/TbLwM2MDtlsCertificateVerifier.java @@ -50,13 +50,22 @@ import org.thingsboard.server.transport.lwm2m.server.store.TbMainSecurityStore; import javax.annotation.PostConstruct; import javax.security.auth.x500.X500Principal; +import java.security.InvalidAlgorithmParameterException; +import java.security.NoSuchAlgorithmException; import java.security.PublicKey; import java.security.cert.CertPath; +import java.security.cert.CertPathValidator; +import java.security.cert.CertPathValidatorException; import java.security.cert.CertificateEncodingException; +import java.security.cert.CertificateException; import java.security.cert.CertificateExpiredException; +import java.security.cert.CertificateFactory; import java.security.cert.CertificateNotYetValidException; +import java.security.cert.PKIXParameters; +import java.security.cert.TrustAnchor; import java.security.cert.X509Certificate; import java.util.Arrays; +import java.util.Collections; import java.util.List; import static org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mTypeServer.CLIENT; @@ -119,8 +128,8 @@ public class TbLwM2MDtlsCertificateVerifier implements NewAdvancedCertificateVer TbLwM2MSecurityInfo securityInfo = null; // verify if trust - if (config.getTrustSslCredentials().getTrustedCertificates().length > 0) { - if (verifyIssuer(cert, config.getTrustSslCredentials().getTrustedCertificates()) != null) { + if (config.getTrustSslCredentials() != null && config.getTrustSslCredentials().getTrustedCertificates().length > 0) { + if (verifyTrust(cert, config.getTrustSslCredentials().getTrustedCertificates()) != null) { String endpoint = config.getTrustSslCredentials().getValueFromSubjectNameByKey(cert.getSubjectX500Principal().getName(), "CN"); securityInfo = StringUtils.isNotEmpty(endpoint) ? securityInfoValidator.getEndpointSecurityInfoByCredentialsId(endpoint, CLIENT) : null; } @@ -193,31 +202,26 @@ public class TbLwM2MDtlsCertificateVerifier implements NewAdvancedCertificateVer } - private X509Certificate verifyIssuer(X509Certificate certificate, X509Certificate[] certificates) { - String issuerCN = config.getTrustSslCredentials().getValueFromSubjectNameByKey(certificate.getIssuerX500Principal().getName(), "CN"); - if (!StringUtils.isBlank(issuerCN)) { + private X509Certificate verifyTrust(X509Certificate certificate, X509Certificate[] certificates) { + try { + CertificateFactory cf = CertificateFactory.getInstance("X.509"); + CertPath cp = cf.generateCertPath(Arrays.asList(new X509Certificate[]{certificate})); for (int index = 0; index < certificates.length; ++index) { - X509Certificate trust = certificates[index]; - String trustCN = config.getTrustSslCredentials().getValueFromSubjectNameByKey(trust.getSubjectX500Principal().getName(), "CN"); - if (!StringUtils.isBlank(trustCN) && issuerCN.length() >= trustCN.length() && issuerCN.substring(issuerCN.length()-trustCN.length()).equals(trustCN)) { - if (verifyCertificate(certificate)) { - return certificate; - } + X509Certificate caCert = certificates[index]; + try { + TrustAnchor trustAnchor = new TrustAnchor(caCert, null); + CertPathValidator cpv = CertPathValidator.getInstance("PKIX"); + PKIXParameters pkixParams = new PKIXParameters( + Collections.singleton(trustAnchor)); + pkixParams.setRevocationEnabled(false); + if (cpv.validate(cp, pkixParams) != null) return certificate; + } catch (NoSuchAlgorithmException | InvalidAlgorithmParameterException | CertPathValidatorException e) { + log.trace("[{}]. [{}]", certificate.getSubjectDN(), e.getMessage()); } } + } catch (CertificateException e) { + log.trace("[{}] certPath not valid. [{}]", certificate.getSubjectDN(), e.getMessage()); } return null; } - - private static boolean verifyCertificate(X509Certificate certificate) { - try { - // date - certificate.checkValidity(); - // Validate X509. - SecurityUtil.certificate.decode(certificate.getEncoded()); - return true; - } catch (Exception e) { - return false; - } - } } diff --git a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/config/ssl/AbstractSslCredentials.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/config/ssl/AbstractSslCredentials.java index 01b7242805..3170aea9a2 100644 --- a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/config/ssl/AbstractSslCredentials.java +++ b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/config/ssl/AbstractSslCredentials.java @@ -61,7 +61,7 @@ public abstract class AbstractSslCredentials implements SslCredentials { this.keyPasswordArray = keyPassword.toCharArray(); } this.keyStore = this.loadKeyStore(trustsOnly, this.keyPasswordArray); - Set trustedCerts = getTrustedCerts(this.keyStore); + Set trustedCerts = getTrustedCerts(this.keyStore, trustsOnly); this.trusts = trustedCerts.toArray(new X509Certificate[0]); if (!trustsOnly) { PrivateKeyEntry privateKeyEntry = null; @@ -179,7 +179,7 @@ public abstract class AbstractSslCredentials implements SslCredentials { return entry; } - private static Set getTrustedCerts(KeyStore ks) { + private static Set getTrustedCerts(KeyStore ks, boolean trustsOnly) { Set set = new HashSet<>(); try { for (Enumeration e = ks.aliases(); e.hasMoreElements(); ) { @@ -187,19 +187,33 @@ public abstract class AbstractSslCredentials implements SslCredentials { if (ks.isCertificateEntry(alias)) { Certificate cert = ks.getCertificate(alias); if (cert instanceof X509Certificate) { - set.add((X509Certificate)cert); + if (trustsOnly) { + // is CA certificate + if (((X509Certificate) cert).getBasicConstraints()>=0) { + set.add((X509Certificate) cert); + } + } else { + set.add((X509Certificate) cert); + } } } else if (ks.isKeyEntry(alias)) { Certificate[] certs = ks.getCertificateChain(alias); if ((certs != null) && (certs.length > 0) && (certs[0] instanceof X509Certificate)) { - set.add((X509Certificate)certs[0]); + if (trustsOnly) { + for (Certificate cert : certs) { + // is CA certificate + if (((X509Certificate) cert).getBasicConstraints()>=0) { + set.add((X509Certificate) cert); + } + } + } else { + set.add((X509Certificate)certs[0]); + } } } } } catch (KeyStoreException ignored) {} return Collections.unmodifiableSet(set); } - - } From e5e79a22f6e5ac0ae48634a77b67a841c61346f8 Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Wed, 5 Jan 2022 18:11:34 +0200 Subject: [PATCH 053/798] lwm2m tests with credentials RPK and X509 - ignore --- .../security/sql/RpkLwM2MIntegrationTest.java | 2 ++ .../sql/X509_NoTrustLwM2MIntegrationTest.java | 2 ++ .../sql/X509_TrustLwM2MIntegrationTest.java | 2 ++ .../resources/application-test.properties | 34 +++++++++---------- 4 files changed, 23 insertions(+), 17 deletions(-) diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/RpkLwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/RpkLwM2MIntegrationTest.java index 9e74beaa6b..87ca415bd0 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/RpkLwM2MIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/RpkLwM2MIntegrationTest.java @@ -16,6 +16,7 @@ package org.thingsboard.server.transport.lwm2m.security.sql; import org.eclipse.leshan.client.object.Security; +import org.junit.Ignore; import org.junit.Test; import org.thingsboard.server.common.data.device.credentials.lwm2m.RPKClientCredential; import org.thingsboard.server.transport.lwm2m.security.AbstractSecurityLwM2MIntegrationTest; @@ -29,6 +30,7 @@ import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.SHORT_SERVE public class RpkLwM2MIntegrationTest extends AbstractSecurityLwM2MIntegrationTest { + @Ignore @Test public void testConnectWithRPKAndObserveTelemetry() throws Exception { RPKClientCredential rpkClientCredentials = new RPKClientCredential(); diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/X509_NoTrustLwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/X509_NoTrustLwM2MIntegrationTest.java index f55c21dcc2..d1bc813b50 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/X509_NoTrustLwM2MIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/X509_NoTrustLwM2MIntegrationTest.java @@ -16,6 +16,7 @@ package org.thingsboard.server.transport.lwm2m.security.sql; import org.eclipse.leshan.client.object.Security; +import org.junit.Ignore; import org.junit.Test; import org.thingsboard.server.common.data.device.credentials.lwm2m.X509ClientCredential; import org.thingsboard.server.common.transport.util.SslUtil; @@ -28,6 +29,7 @@ import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.SHORT_SERVE public class X509_NoTrustLwM2MIntegrationTest extends AbstractSecurityLwM2MIntegrationTest { + @Ignore @Test public void testConnectWithCertAndObserveTelemetry() throws Exception { X509ClientCredential credentials = new X509ClientCredential(); diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/X509_TrustLwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/X509_TrustLwM2MIntegrationTest.java index 32d176d598..7c5e48a4b7 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/X509_TrustLwM2MIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/X509_TrustLwM2MIntegrationTest.java @@ -16,6 +16,7 @@ package org.thingsboard.server.transport.lwm2m.security.sql; import org.eclipse.leshan.client.object.Security; +import org.junit.Ignore; import org.junit.Test; import org.thingsboard.server.common.data.device.credentials.lwm2m.X509ClientCredential; import org.thingsboard.server.transport.lwm2m.security.AbstractSecurityLwM2MIntegrationTest; @@ -27,6 +28,7 @@ import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.SHORT_SERVE public class X509_TrustLwM2MIntegrationTest extends AbstractSecurityLwM2MIntegrationTest { + @Ignore @Test public void testConnectAndObserveTelemetry() throws Exception { X509ClientCredential credentials = new X509ClientCredential(); diff --git a/application/src/test/resources/application-test.properties b/application/src/test/resources/application-test.properties index 651b00ed67..856745797a 100644 --- a/application/src/test/resources/application-test.properties +++ b/application/src/test/resources/application-test.properties @@ -1,20 +1,20 @@ -transport.lwm2m.server.security.credentials.enabled=true -transport.lwm2m.server.security.credentials.type=KEYSTORE -transport.lwm2m.server.security.credentials.keystore.store_file=lwm2m/credentials/serverKeyStore.jks -transport.lwm2m.server.security.credentials.keystore.store_password=server -transport.lwm2m.server.security.credentials.keystore.key_alias=server -transport.lwm2m.server.security.credentials.keystore.key_password=server -transport.lwm2m.bootstrap.enabled=false -transport.lwm2m.bootstrap.security.credentials.enabled=true -transport.lwm2m.bootstrap.security.credentials.type=KEYSTORE -transport.lwm2m.bootstrap.security.credentials.keystore.store_file=lwm2m/credentials/serverKeyStore.jks -transport.lwm2m.bootstrap.security.credentials.keystore.store_password=server -transport.lwm2m.bootstrap.security.credentials.keystore.key_alias=server -transport.lwm2m.bootstrap.security.credentials.keystore.key_password=server -transport.lwm2m.security.trust-credentials.enabled=true -transport.lwm2m.security.trust-credentials.type=KEYSTORE -transport.lwm2m.security.trust-credentials.keystore.store_file=lwm2m/credentials/serverKeyStore.jks -transport.lwm2m.security.trust-credentials.keystore.store_password=server +#transport.lwm2m.server.security.credentials.enabled=true +#transport.lwm2m.server.security.credentials.type=KEYSTORE +#transport.lwm2m.server.security.credentials.keystore.store_file=lwm2m/credentials/serverKeyStore.jks +#transport.lwm2m.server.security.credentials.keystore.store_password=server +#transport.lwm2m.server.security.credentials.keystore.key_alias=server +#transport.lwm2m.server.security.credentials.keystore.key_password=server +#transport.lwm2m.bootstrap.enabled=false +#transport.lwm2m.bootstrap.security.credentials.enabled=true +#transport.lwm2m.bootstrap.security.credentials.type=KEYSTORE +#transport.lwm2m.bootstrap.security.credentials.keystore.store_file=lwm2m/credentials/serverKeyStore.jks +#transport.lwm2m.bootstrap.security.credentials.keystore.store_password=server +#transport.lwm2m.bootstrap.security.credentials.keystore.key_alias=server +#transport.lwm2m.bootstrap.security.credentials.keystore.key_password=server +#transport.lwm2m.security.trust-credentials.enabled=true +#transport.lwm2m.security.trust-credentials.type=KEYSTORE +#transport.lwm2m.security.trust-credentials.keystore.store_file=lwm2m/credentials/serverKeyStore.jks +#transport.lwm2m.security.trust-credentials.keystore.store_password=server edges.enabled=true edges.storage.no_read_records_sleep=500 From 659d8abf35fa6f41024f70f299b88e96ce96e302 Mon Sep 17 00:00:00 2001 From: van-vanich Date: Wed, 5 Jan 2022 18:15:16 +0200 Subject: [PATCH 054/798] improve node details and improve field naming --- .../sql/SequentialTimeseriesPersistenceTest.java | 6 +++--- .../rule/engine/telemetry/TbMsgTimeseriesNode.java | 13 ++++++++----- .../telemetry/TbMsgTimeseriesNodeConfiguration.java | 4 ++-- 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/application/src/test/java/org/thingsboard/server/service/sql/SequentialTimeseriesPersistenceTest.java b/application/src/test/java/org/thingsboard/server/service/sql/SequentialTimeseriesPersistenceTest.java index e4fb0318b4..e0a0260f7f 100644 --- a/application/src/test/java/org/thingsboard/server/service/sql/SequentialTimeseriesPersistenceTest.java +++ b/application/src/test/java/org/thingsboard/server/service/sql/SequentialTimeseriesPersistenceTest.java @@ -74,7 +74,7 @@ public class SequentialTimeseriesPersistenceTest extends AbstractControllerTest @Before public void beforeTest() throws Exception { configuration = new TbMsgTimeseriesNodeConfiguration(); - configuration.setIgnoreMetadataTs(true); + configuration.setUseServerTs(true); loginSysAdmin(); @@ -142,7 +142,7 @@ public class SequentialTimeseriesPersistenceTest extends AbstractControllerTest TbMsgDataType.JSON, getTbMsgData(msgValue.get(idx))); saveDeviceTsEntry(device.getId(), tbMsg, msgValue.get(idx)); - saveAssetTsEntry(asset, device.getName(), msgValue.get(idx), TbMsgTimeseriesNode.computeTs(tbMsg, configuration.isIgnoreMetadataTs())); + saveAssetTsEntry(asset, device.getName(), msgValue.get(idx), TbMsgTimeseriesNode.computeTs(tbMsg, configuration.isUseServerTs())); idx++; } } @@ -172,7 +172,7 @@ public class SequentialTimeseriesPersistenceTest extends AbstractControllerTest } void saveDeviceTsEntry(EntityId entityId, TbMsg tbMsg, long value) throws ExecutionException, InterruptedException, TimeoutException { - TsKvEntry tsKvEntry = new BasicTsKvEntry(TbMsgTimeseriesNode.computeTs(tbMsg, configuration.isIgnoreMetadataTs()), new LongDataEntry(TOTALIZER, value)); + TsKvEntry tsKvEntry = new BasicTsKvEntry(TbMsgTimeseriesNode.computeTs(tbMsg, configuration.isUseServerTs()), new LongDataEntry(TOTALIZER, value)); saveTimeseries(entityId, tsKvEntry); } diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNode.java index 2334851a08..2c426dfdec 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNode.java @@ -48,11 +48,14 @@ import java.util.concurrent.TimeUnit; nodeDetails = "Saves timeseries telemetry data based on configurable TTL parameter. Expects messages with 'POST_TELEMETRY_REQUEST' message type. " + "Timestamp in milliseconds will be taken from metadata.ts, otherwise 'now' message timestamp will be applied. " + "Allows stopping updating values for incoming keys in the latest ts_kv table if 'skipLatestPersistence' is set to true.\n " + - "Enable 'ignoreMetadataTs' param to ignore the timestamp that arrives from message metadata. " + + "
" + + "Enable 'useServerTs' param to use the timestamp of the message processing instead of the timestamp from the message. " + "Useful for all sorts of sequential processing if you merge messages from multiple sources (devices, assets, etc).\n" + - "For example, if you count number of messages from multiple devices into asset time-series value. " + - "Typically, you fetch the previous value of the counter, increment it and then save the value. " + - "If you use timestamp of the original message, the value may be ignored, since it has outdated timestamp comparing to the previous message.", + "
" + + "In the case of sequential processing, the platform guarantees that the messages are processed in the order of their submission to the queue. " + + "However, the timestamp of the messages originated by multiple devices/servers may be unsynchronized long before they are pushed to the queue. " + + "The DB layer has certain optimizations to ignore the updates of the \"attributes\" and \"latest values\" tables if the new record has a timestamp that is older than the previous record. " + + "So, to make sure that all the messages will be processed correctly, one should enable this parameter for sequential message processing scenarios.", uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbActionNodeTimeseriesConfig", icon = "file_upload" @@ -82,7 +85,7 @@ public class TbMsgTimeseriesNode implements TbNode { ctx.tellFailure(msg, new IllegalArgumentException("Unsupported msg type: " + msg.getType())); return; } - long ts = computeTs(msg, config.isIgnoreMetadataTs()); + long ts = computeTs(msg, config.isUseServerTs()); String src = msg.getData(); Map> tsKvMap = JsonConverter.convertToTelemetry(new JsonParser().parse(src), ts); if (tsKvMap.isEmpty()) { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNodeConfiguration.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNodeConfiguration.java index 6c9f7ac884..0d59c325fa 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNodeConfiguration.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNodeConfiguration.java @@ -23,14 +23,14 @@ public class TbMsgTimeseriesNodeConfiguration implements NodeConfiguration Date: Wed, 5 Jan 2022 17:47:01 +0200 Subject: [PATCH 055/798] lwm2m add to test shell credentials --- ...cfssl_chain_trusts_and_clients_for_test.sh | 299 ++++++++++++++++++ .../shell/lwm2m_cfssl_chain_for_test_All.sh | 65 ++++ .../lwm2m_cfssl_chain_server_for_test.sh | 298 +++++++++++++++++ 3 files changed, 662 insertions(+) create mode 100755 application/src/test/resources/lwm2m/credentials/shell/lwM2M_cfssl_chain_trusts_and_clients_for_test.sh create mode 100755 application/src/test/resources/lwm2m/credentials/shell/lwm2m_cfssl_chain_for_test_All.sh create mode 100755 application/src/test/resources/lwm2m/credentials/shell/lwm2m_cfssl_chain_server_for_test.sh diff --git a/application/src/test/resources/lwm2m/credentials/shell/lwM2M_cfssl_chain_trusts_and_clients_for_test.sh b/application/src/test/resources/lwm2m/credentials/shell/lwM2M_cfssl_chain_trusts_and_clients_for_test.sh new file mode 100755 index 0000000000..26f47266a7 --- /dev/null +++ b/application/src/test/resources/lwm2m/credentials/shell/lwM2M_cfssl_chain_trusts_and_clients_for_test.sh @@ -0,0 +1,299 @@ +#!/usr/bin/env bash + +# Change working directory +cd -- "$( + dirname "${0}" +)" || exit 1 + +readonly TRUST_PATH="Trust" +readonly CA_ROOT_CERT_KEY="ca-root" +readonly CA_ROOT_ALIAS="root" +readonly CA_INTERMEDIATE_CERT_KEY_PREF="intermediate_ca" +CA_INTERMEDIATE_START=0 +CA_INTERMEDIATE_FINISH=2 +CA_INTERMEDIATE_NUMBER=${CA_INTERMEDIATE_START} +CA_INTERMEDIATE_CERT_SIGN=${CA_ROOT_CERT_KEY} +CA_LIST_CERT_FOR_CAT="" +readonly CA_TRUST_STORE_ALL_CHAIN="lwm2mtruststorechain" +readonly CA_TRUST_STORE_PWD="server_ks_password" +readonly CA_TRUST_CERT_ALIAS="root" +readonly CA_TRUST_CERT_CHAIN_JKS="lwm2mtruststorechain" +readonly CA_TRUST_STORE_CHAIN_ALIAS="trust_cert_chain_alias" + +readonly CLIENT_PATH="Client" +readonly CLIENT_JKS_FOR_TEST="lwm2mclient" +readonly CLIENT_CERT_KEY_PREF="LwX509" +readonly CLIENT_CERT_ALIAS_PREF="client_alias_" +readonly CLIENT_STORE_PWD="client_ks_password" +readonly CLIENT_HOST_NAME="thingsboard_test.io" +CLIENT_START=0 +CLIENT_FINISH=1 +CLIENT_NUMBER=${CLIENT_START} + +SERVER_HOST_NAME="localhost.localdomain" +SERVER_LOCAL_HOST_NAME="localhost" +SERVER_PUBLIC_HOST_NAMES="-" + +readonly CF_COMMANDS=" + cfssl + cfssljson +" + +if [ ! -z "$1" ]; then + CA_INTERMEDIATE_START=$1 + CA_INTERMEDIATE_NUMBER=${CA_INTERMEDIATE_START} +fi + +if [ ! -z "$2" ]; then + CA_INTERMEDIATE_FINISH=$2 +fi + +if [ ! -z "$3" ]; then + CLIENT_START=$1 + CLIENT_NUMBER=${CLIENT_START} +fi + +if [ ! -z "$4" ]; then + CLIENT_FINISH=$4 +fi + +# Change working directory +rm -rf ${TRUST_PATH} +mkdir -p ${TRUST_PATH} +rm -rf ${CLIENT_PATH} +mkdir -p ${CLIENT_PATH} +cd -- "$( + dirname "${0}" +)" || exit 1 + + +rm *.csr +rm *.p12 +rm *.json +rm *.pem +rm *.jks + +intermediate_common_name() { + echo "${CA_INTERMEDIATE_CERT_KEY_PREF}${CA_INTERMEDIATE_NUMBER}" +} + +set_list_sert_for_cat() { + local first="$1" + echo "$first ${CA_LIST_CERT_FOR_CAT}" +} + +client_common_name() { + echo "${CLIENT_CERT_KEY_PREF}$(printf "%08d" ${CLIENT_NUMBER})" +} + +client_alias_name() { + echo "${CLIENT_CERT_ALIAS_PREF}$(printf "%08d" ${CLIENT_NUMBER})" +} + +for COMMAND in ${CF_COMMANDS}; do + if ! command -v ${COMMAND} &> /dev/null; then + echo "ERROR: Missing command ${COMMAND}" >&2 + echo "Install the package from: https://pkg.cfssl.org/" >&2 + exit 1 + fi +done + +tee ./${TRUST_PATH}/ca-config.json 1> /dev/null <<-CONFIG +{ + "signing": { + "default": { + "expiry": "8760h", + "names": [ + { + "C": "UK", + "ST": "Kyiv city", + "L": "Kyiv", + "O": "Thingsboard", + "OU": "DEVELOPER_TEST" + } + ] + }, + "profiles": { + "server": { + "expiry": "43800h", + "key": { + "algo": "ecdsa", + "size": 256 + }, + "usages": [ + "signing", + "key encipherment", + "server auth" + ] + }, + "client": { + "expiry": "43800h", + "key": { + "algo": "ecdsa", + "size": 256 + }, + "usages": [ + "signing", + "key encipherment", + "client auth" + ] + }, + "client-server": { + "expiry": "43800h", + "key": { + "algo": "ecdsa", + "size": 256 + }, + "usages": [ + "signing", + "key encipherment", + "server auth", + "client auth" + ] + } + } + } +} +CONFIG + +tee ./${TRUST_PATH}/ca-root-to-intermediate-config.json 1> /dev/null <<-CONFIG +{ + "signing": { + "default": { + "expiry": "43800h", + "ca_constraint": { + "is_ca": true, + "max_path_len": 0, + "max_path_len_zero": true + }, + "key": { + "algo": "ecdsa", + "size": 256 + }, + "usages": [ + "digital signature", + "cert sign", + "crl sign", + "signing" + ] + } + } +} +CONFIG + +echo "====================================================" +echo -e "Generate the root of certificates: \n-${CA_ROOT_KEY}-key.pem (certificate key)\n-${CA_ROOT_KEY}.pem (certificate)\n-${CA_ROOT_KEY}.csr (sign request)" +echo "====================================================" +cfssl genkey \ + -initca \ + - \ + <<-CONFIG | cfssljson -bare ./${TRUST_PATH}/${CA_ROOT_CERT_KEY} +{ + "CN": "ROOT CA", + "key": { + "algo": "ecdsa", + "size": 256 + }, + "names": [ + { + "C": "UK", + "ST": "Kyiv city", + "L": "Kyiv", + "O": "Thingsboard", + "OU": "DEVELOPER_TEST" + } + ], + "ca": { + "expiry": "131400h" + } +} +CONFIG +CA_LIST_CERT_FOR_CAT=$(set_list_sert_for_cat ./${TRUST_PATH}/${CA_ROOT_CERT_KEY}.pem) + +echo "====================================================" +echo -e "Generate and Signed the intermediates of our certificates: \n-${CA_INTERMEDIATE_CERT_KEY_PREF}?-key.pem (certificate key)\n-${CA_INTERMEDIATE_CERT_KEY_PREF}?.pem (certificate)\n-${CA_INTERMEDIATE_CERT_KEY_PREF}?.csr (sign request)" +echo "====================================================" + +while [[ ${CA_INTERMEDIATE_NUMBER} -lt ${CA_INTERMEDIATE_FINISH} ]]; +do + CA_INTERMEDIATE_CERT_KEY=$(intermediate_common_name) + CA_INTERMEDIATE_NUMBER=$((${CA_INTERMEDIATE_NUMBER} + 1)) + + cfssl gencert \ + -ca ./${TRUST_PATH}/${CA_INTERMEDIATE_CERT_SIGN}.pem \ + -ca-key ./${TRUST_PATH}/${CA_INTERMEDIATE_CERT_SIGN}-key.pem \ + -config ./${TRUST_PATH}/ca-root-to-intermediate-config.json \ + -hostname "${SERVER_HOST_NAME},${SERVER_LOCAL_HOST_NAME}${SERVER_PUBLIC_HOST_NAMES:+, }${SERVER_PUBLIC_HOST_NAMES}" \ + - \ + <<-CONFIG | cfssljson -bare ./${TRUST_PATH}/${CA_INTERMEDIATE_CERT_KEY} + { + "CN": "${CA_INTERMEDIATE_CERT_KEY}", + "names": [ + { + "C": "UK", + "ST": "Kyiv city", + "L": "Kyiv", + "O": "Thingsboard", + "OU": "DEVELOPER_TEST" + } + ] + } +CONFIG + #openssl x509 -in ${CA_INTERMEDIATE_CERT_KEY}.pem -text -noout + CA_LIST_CERT_FOR_CAT=$(set_list_sert_for_cat ./${TRUST_PATH}/${CA_INTERMEDIATE_CERT_KEY}.pem) + CA_INTERMEDIATE_CERT_SIGN=${CA_INTERMEDIATE_CERT_KEY} +done + +echo "====================================================" +echo -e "Add the CA_certificate to keystore: ${CA_TRUST_CERT_CHAIN_JKS}.jks" +echo "====================================================" +cat ${CA_LIST_CERT_FOR_CAT} > ./${TRUST_PATH}/${CA_TRUST_STORE_ALL_CHAIN}.pem +openssl pkcs12 -export -in ./${TRUST_PATH}/${CA_TRUST_STORE_ALL_CHAIN}.pem -inkey ./${TRUST_PATH}/${CA_INTERMEDIATE_CERT_KEY}-key.pem -out ./${TRUST_PATH}/${CA_INTERMEDIATE_CERT_KEY}.p12 -name ${CA_TRUST_STORE_CHAIN_ALIAS} -CAfile ./${TRUST_PATH}/${CA_INTERMEDIATE_CERT_KEY}.pem -caname ${CA_ROOT_ALIAS} -passin pass:${CA_TRUST_STORE_PWD} -passout pass:${CA_TRUST_STORE_PWD} +keytool -importkeystore -deststorepass ${CA_TRUST_STORE_PWD} -destkeypass ${CA_TRUST_STORE_PWD} -destkeystore ./${TRUST_PATH}/${CA_TRUST_CERT_CHAIN_JKS}.jks -srckeystore ./${TRUST_PATH}/${CA_INTERMEDIATE_CERT_KEY}.p12 -srcstoretype PKCS12 -srcstorepass ${CA_TRUST_STORE_PWD} -alias ${CA_TRUST_STORE_CHAIN_ALIAS} + +keytool -list -v -keystore ./${TRUST_PATH}/lwm2mtruststorechain.jks -storepass server_ks_password -storetype PKCS12 + +echo "====================================================" +echo -e "Generate and Signed the clients of our certificates: \n-${CLIENT_CERT_KEY_PREF}?-key.pem (certificate key)\n-${CLIENT_CERT_KEY_PREF}?.pem (certificate)\n-${CCLIENT_CERT_KEY_PREF}?.csr (sign request)" +echo "====================================================" + + +while [[ ${CLIENT_NUMBER} -lt ${CLIENT_FINISH} ]]; +do + CLIENT_CERT_KEY=$(client_common_name) + CLIENT_CERT_ALIAS=$(client_alias_name) + CLIENT_NUMBER=$((${CLIENT_NUMBER} + 1)) + + cfssl gencert \ + -ca ./${TRUST_PATH}/${CA_INTERMEDIATE_CERT_KEY}.pem \ + -ca-key ./${TRUST_PATH}/${CA_INTERMEDIATE_CERT_KEY}-key.pem \ + -config ./${TRUST_PATH}/ca-config.json \ + -profile client \ + -hostname "${CLIENT_HOST_NAME}" \ + - \ + <<-CONFIG | cfssljson -bare ./${CLIENT_PATH}/${CLIENT_CERT_KEY} +{ + "CN": "${CLIENT_CERT_KEY}" +} +CONFIG + +echo "====================================================" +echo -e "Add the client certificate (${CLIENT_CERT_KEY}.pem) to keystore: ${CLIENT_JKS_FOR_TEST}.jks" +echo "====================================================" +cat ./${CLIENT_PATH}/${CLIENT_CERT_KEY}.pem ${CA_LIST_CERT_FOR_CAT} > ./${CLIENT_PATH}/${CLIENT_CERT_KEY}_chain.pem +openssl pkcs12 -export -in ./${CLIENT_PATH}/${CLIENT_CERT_KEY}_chain.pem -inkey ./${CLIENT_PATH}/${CLIENT_CERT_KEY}-key.pem -out ./${CLIENT_PATH}/${CLIENT_CERT_KEY}.p12 -name ${CLIENT_CERT_ALIAS} -CAfile ./${TRUST_PATH}/${CA_INTERMEDIATE_CERT_KEY}.pem -caname ${CA_ROOT_ALIAS} -passin pass:${CLIENT_STORE_PWD} -passout pass:${CLIENT_STORE_PWD} +keytool -importkeystore -deststorepass ${CLIENT_STORE_PWD} -destkeypass ${CLIENT_STORE_PWD} -destkeystore ./${CLIENT_PATH}/${CLIENT_JKS_FOR_TEST}.jks -srckeystore ./${CLIENT_PATH}/${CLIENT_CERT_KEY}.p12 -srcstoretype PKCS12 -srcstorepass ${CLIENT_STORE_PWD} -alias ${CLIENT_CERT_ALIAS} + +done + +keytool -list -v -keystore ./${CLIENT_PATH}/lwm2mclient.jks -storepass client_ks_password -storetype PKCS12 + +rm ./${TRUST_PATH}/*.p12 +rm ./${TRUST_PATH}/*.csr +rm ./${TRUST_PATH}/*.json +rm ./${TRUST_PATH}/${CA_ROOT_CERT_KEY}* +rm ./${TRUST_PATH}/${CA_INTERMEDIATE_CERT_KEY_PREF}* + + +rm ./${CLIENT_PATH}/*.p12 2> /dev/null +rm ./${CLIENT_PATH}/*.csr 2> /dev/null diff --git a/application/src/test/resources/lwm2m/credentials/shell/lwm2m_cfssl_chain_for_test_All.sh b/application/src/test/resources/lwm2m/credentials/shell/lwm2m_cfssl_chain_for_test_All.sh new file mode 100755 index 0000000000..b3b114cb28 --- /dev/null +++ b/application/src/test/resources/lwm2m/credentials/shell/lwm2m_cfssl_chain_for_test_All.sh @@ -0,0 +1,65 @@ +#!/usr/bin/env bash + +readonly INTERMEDIATE_START=0 +readonly INTERMEDIATE_FINISH=2 +readonly CLIENT_START=0 +readonly CLIENT_FINISH=5 + +IS_IHFO=false +IS_SERVER_CREATED_KEY=true +IS_TRUST_CLIENT_CREATED_KEY=true + +cd -- "$( + dirname "${0}" +)" || exit 1 + +Help() +{ + # Display Help + echo "Description of the script functions." + echo + echo "Syntax: scriptTemplate [-g|h|v|V]" + echo "options:" + echo "h Print this Help." + echo "v Verbose mode." + echo "V Print software version and exit." + echo +} + +if [ "$1" == "-h" ] ; then + echo -e "Usage 2: ./`basename $0` \"Information is not displayed\" : \"Keys for the server are generated\" : \"Keys for the clients and trusts are generated\"" + echo -e "Usage 1: ./`basename $0` true \"Information is displayed\" : \"Keys for the server are generated\" : \"Keys for the clients and trusts are generated\"" + echo -e "Usage 3: ./`basename $0` true false \"Information is displayed\" : \"Keys for the server are not generated\" : \"Keys for the clients and trusts are generated\"" + echo -e "Usage 4: ./`basename $0` true false false \"Information is displayed\" : \"Keys for the server are not generated\" : \"Keys for the clients and trusts are not generated\"" + echo -e "Usage 4: ./`basename $0` true true false \"Information is displayed\" : \"Keys for the server are generated\" : \"Keys for the clients and trusts are not generated\"" + echo "This Help File: ./`basename $0` -h" + exit 0 +fi + +if [ -n "$1" ]; then + IS_IHFO=$1 +fi + +if [ -n "$2" ]; then + IS_SERVER_CREATED_KEY=$2 +fi + +if [ -n "$3" ]; then + IS_TRUST_CLIENT_CREATED_KEY=$3 +fi + +if [ "$IS_IHFO" = false ] ; then + if [ "$IS_SERVER_CREATED_KEY" = true ] ; then + ./lwm2m_cfssl_chain_server_for_test.sh > /dev/null 2>&1 & + fi + if [ "$IS_TRUST_CLIENT_CREATED_KEY" = true ] ; then + ./lwM2M_cfssl_chain_trusts_and_clients_for_test.sh ${INTERMEDIATE_START} ${INTERMEDIATE_FINISH} ${CLIENT_START} ${CLIENT_FINISH} > /dev/null 2>&1 & + fi +else + if [ "$IS_SERVER_CREATED_KEY" = true ] ; then + ./lwm2m_cfssl_chain_server_for_test.sh + fi + if [ "$IS_TRUST_CLIENT_CREATED_KEY" = true ] ; then + ./lwM2M_cfssl_chain_trusts_and_clients_for_test.sh ${INTERMEDIATE_START} ${INTERMEDIATE_FINISH} ${CLIENT_START} ${CLIENT_FINISH} + fi +fi \ No newline at end of file diff --git a/application/src/test/resources/lwm2m/credentials/shell/lwm2m_cfssl_chain_server_for_test.sh b/application/src/test/resources/lwm2m/credentials/shell/lwm2m_cfssl_chain_server_for_test.sh new file mode 100755 index 0000000000..efe6ed46dd --- /dev/null +++ b/application/src/test/resources/lwm2m/credentials/shell/lwm2m_cfssl_chain_server_for_test.sh @@ -0,0 +1,298 @@ +#!/usr/bin/env bash + +# REF: https://github.com/cloudflare/cfssl + +# Change working directory +cd -- "$( + dirname "${0}" +)" || exit 1 + +readonly CA_ROOT_CERT_KEY="ca-root" +readonly CA_ROOT_ALIAS="root" +readonly CA_INTERMEDIATE_CERT_KEY_PREF="intermediate_ca" +CA_INTERMEDIATE_NUMBER=0 +CA_LIST_CERT_FOR_CAT="" + +readonly CF_COMMANDS=" + cfssl + cfssljson +" + +readonly SERVER_JKS_FOR_TEST="lwm2mserver" +readonly STORE_PASS_PWD="server_ks_password" +readonly SERVER_PATH="Server" +readonly SERVER_CERT_KEY="lwm2mserver" +readonly SERVER_CERT_CHAIN="lwm2mserver_chain" +readonly SERVER_CERT_ALIAS="server" +readonly BS_SERVER_CERT_KEY="lwm2mserverbs" +readonly BS_SERVER_CERT_CHAIN="lwm2mserverbs_chain" +readonly BS_SERVER_CERT_ALIAS="bootstrap" + +SERVER_HOST_NAME="localhost.localdomain" +SERVER_LOCAL_HOST_NAME="localhost" +SERVER_PUBLIC_HOST_NAMES="-" + +intermediate_common_name() { + echo "${CA_INTERMEDIATE_CERT_KEY_PREF}${CA_INTERMEDIATE_NUMBER}" +} + +set_list_sert_for_cat() { + local first="$1" + echo "$first ${CA_LIST_CERT_FOR_CAT}" +} + + +# Change working directory +rm -rf ${SERVER_PATH} +mkdir -p ${SERVER_PATH} + +cd -- "$( + dirname ./${SERVER_PATH} +)" || exit 1 + + +rm *.csr +rm *.p12 +rm *.json +rm *.pem +rm *.jks + +CA_INTERMEDIATE_CERT_SIGN=${CA_ROOT_CERT_KEY} +CA_INTERMEDIATE_CERT_KEY=$(intermediate_common_name) +CA_INTERMEDIATE_NUMBER=$((${CA_INTERMEDIATE_NUMBER} + 1)) +CA_LIST_CERT_FOR_CAT="" + +for COMMAND in ${CF_COMMANDS}; do + if ! command -v ${COMMAND} &> /dev/null; then + echo "ERROR: Missing command ${COMMAND}" >&2 + echo "Install the package from: https://pkg.cfssl.org/" >&2 + exit 1 + fi +done + +tee ./${SERVER_PATH}/ca-config.json 1> /dev/null <<-CONFIG +{ + "signing": { + "default": { + "expiry": "8760h", + "names": [ + { + "C": "UK", + "ST": "Kyiv city", + "L": "Kyiv", + "O": "Thingsboard", + "OU": "DEVELOPER_TEST" + } + ] + }, + "profiles": { + "server": { + "expiry": "43800h", + "key": { + "algo": "ecdsa", + "size": 256 + }, + "usages": [ + "signing", + "key encipherment", + "server auth" + ] + }, + "client": { + "expiry": "43800h", + "key": { + "algo": "ecdsa", + "size": 256 + }, + "usages": [ + "signing", + "key encipherment", + "client auth" + ] + }, + "client-server": { + "expiry": "43800h", + "key": { + "algo": "ecdsa", + "size": 256 + }, + "usages": [ + "signing", + "key encipherment", + "server auth", + "client auth" + ] + } + } + } +} +CONFIG + +tee ./${SERVER_PATH}/ca-root-to-intermediate-config.json 1> /dev/null <<-CONFIG +{ + "signing": { + "default": { + "expiry": "43800h", + "ca_constraint": { + "is_ca": true, + "max_path_len": 0, + "max_path_len_zero": true + }, + "key": { + "algo": "ecdsa", + "size": 256 + }, + "usages": [ + "digital signature", + "cert sign", + "crl sign", + "signing" + ] + } + } +} +CONFIG + +echo "====================================================" +echo -e "Generate the root of certificates: \n-${CA_ROOT_KEY}-key.pem (certificate key)\n-${CA_ROOT_KEY}.pem (certificate)\n-${CA_ROOT_KEY}.csr (sign request)" +echo "====================================================" +cfssl genkey \ + -initca \ + - \ + <<-CONFIG | cfssljson -bare ./${SERVER_PATH}/${CA_ROOT_CERT_KEY} +{ + "CN": "ROOT CA for servers", + "key": { + "algo": "ecdsa", + "size": 256 + }, + "names": [ + { + "C": "UK", + "ST": "Kyiv city", + "L": "Kyiv", + "O": "Thingsboard", + "OU": "DEVELOPER_TEST" + } + ], + "ca": { + "expiry": "131400h" + } +} +CONFIG +CA_LIST_CERT_FOR_CAT=$(set_list_sert_for_cat ./${SERVER_PATH}/${CA_ROOT_CERT_KEY}.pem) + +echo "====================================================" +echo -e "Generate and Signed the first intermediates of our certificates: \n-${CA_INTERMEDIATE_CERT_KEY}-key.pem (certificate key)\n-${CA_INTERMEDIATE_CERT_KEY}.pem (certificate)\n-${CA_INTERMEDIATE_CERT_KEY}.csr (sign request)" +echo "====================================================" +cfssl gencert \ + -ca ./${SERVER_PATH}/${CA_INTERMEDIATE_CERT_SIGN}.pem \ + -ca-key ./${SERVER_PATH}/${CA_INTERMEDIATE_CERT_SIGN}-key.pem \ + -config ./${SERVER_PATH}/ca-root-to-intermediate-config.json \ + -hostname "${SERVER_HOST_NAME},${SERVER_LOCAL_HOST_NAME}${SERVER_PUBLIC_HOST_NAMES:+, }${SERVER_PUBLIC_HOST_NAMES}" \ + - \ + <<-CONFIG | cfssljson -bare ./${SERVER_PATH}/${CA_INTERMEDIATE_CERT_KEY} +{ + "CN": "${CA_INTERMEDIATE_CERT_KEY}", + "names": [ + { + "C": "UK", + "ST": "Kyiv city", + "L": "Kyiv", + "O": "Thingsboard", + "OU": "DEVELOPER_TEST" + } + ] +} +CONFIG +CA_LIST_CERT_FOR_CAT=$(set_list_sert_for_cat ./${SERVER_PATH}/${CA_INTERMEDIATE_CERT_KEY}.pem) + + +## Lwm2m Server certificate +echo "====================================================" +echo -e "Generate and Signed the server certificate: \n-${SERVER_CERT_KEY}-key.pem (certificate key)\n-${SERVER_CERT_KEY}.pem (certificate)\n-${SERVER_CERT_KEY}.csr (sign request)" +echo "====================================================" +cfssl gencert \ + -ca ./${SERVER_PATH}/${CA_INTERMEDIATE_CERT_KEY}.pem \ + -ca-key ./${SERVER_PATH}/${CA_INTERMEDIATE_CERT_KEY}-key.pem \ + -config ./${SERVER_PATH}/ca-config.json \ + -profile server \ + -hostname "${SERVER_HOST_NAME},${SERVER_LOCAL_HOST_NAME}${SERVER_PUBLIC_HOST_NAMES:+, }${SERVER_PUBLIC_HOST_NAMES}" \ + - \ + <<-CONFIG | cfssljson -bare ./${SERVER_PATH}/${SERVER_CERT_KEY} +{ + "CN": "${SERVER_LOCAL_HOST_NAME}" +} +CONFIG + +echo "====================================================" +echo -e "Add the server certificate (${SERVER_CERT_KEY}.pem) to keystore: ${SERVER_JKS_FOR_TEST}.jks" +echo "====================================================" +cat ./${SERVER_PATH}/${SERVER_CERT_KEY}.pem ${CA_LIST_CERT_FOR_CAT} > ./${SERVER_PATH}/${SERVER_CERT_CHAIN}.pem +openssl pkcs12 -export -in ./${SERVER_PATH}/${SERVER_CERT_CHAIN}.pem -inkey ./${SERVER_PATH}/${SERVER_CERT_KEY}-key.pem -out ./${SERVER_PATH}/${SERVER_CERT_KEY}.p12 -name ${SERVER_CERT_ALIAS} -CAfile ./${SERVER_PATH}/${CA_INTERMEDIATE_CERT_KEY}.pem -caname ${CA_ROOT_ALIAS} -passin pass:${STORE_PASS_PWD} -passout pass:${STORE_PASS_PWD} +keytool -importkeystore -deststorepass ${STORE_PASS_PWD} -destkeypass ${STORE_PASS_PWD} -destkeystore ./${SERVER_PATH}/${SERVER_JKS_FOR_TEST}.jks -srckeystore ./${SERVER_PATH}/${SERVER_CERT_KEY}.p12 -srcstoretype PKCS12 -srcstorepass ${STORE_PASS_PWD} -alias ${SERVER_CERT_ALIAS} + + +CA_INTERMEDIATE_CERT_SIGN=${CA_INTERMEDIATE_CERT_KEY} +CA_INTERMEDIATE_CERT_KEY=$(intermediate_common_name) +CA_INTERMEDIATE_NUMBER=$((${CA_INTERMEDIATE_NUMBER} + 1)) +echo "====================================================" +echo -e "Generate and Signed the second intermediates of our certificates: \n-${CA_INTERMEDIATE_CERT_KEY}-key.pem (certificate key)\n-${CA_INTERMEDIATE_CERT_KEY}.pem (certificate)\n-${CA_INTERMEDIATE_CERT_KEY}.csr (sign request)" +echo "====================================================" +cfssl gencert \ + -ca ./${SERVER_PATH}/${CA_INTERMEDIATE_CERT_SIGN}.pem \ + -ca-key ./${SERVER_PATH}/${CA_INTERMEDIATE_CERT_SIGN}-key.pem \ + -config ./${SERVER_PATH}/ca-root-to-intermediate-config.json \ + -hostname "${SERVER_HOST_NAME},${SERVER_LOCAL_HOST_NAME}${SERVER_PUBLIC_HOST_NAMES:+, }${SERVER_PUBLIC_HOST_NAMES}" \ + - \ + <<-CONFIG | cfssljson -bare ./${SERVER_PATH}/${CA_INTERMEDIATE_CERT_KEY} +{ + "CN": "${CA_INTERMEDIATE_CERT_KEY}", + "names": [ + { + "C": "UK", + "ST": "Kyiv city", + "L": "Kyiv", + "O": "Thingsboard", + "OU": "DEVELOPER_TEST" + } + ] +} +CONFIG +CA_LIST_CERT_FOR_CAT=$(set_list_sert_for_cat ./${SERVER_PATH}/${CA_INTERMEDIATE_CERT_KEY}.pem) + +## Bootstrap server certificate +echo "====================================================" +echo -e "Generate and Signed the server certificate: \n-${BS_SERVER_CERT_KEY}-key.pem (certificate key)\n-${BS_SERVER_CERT_KEY}.pem (certificate)\n-${BS_SERVER_CERT_KEY}.csr (sign request)" +echo "====================================================" +cfssl gencert \ + -ca ./${SERVER_PATH}/${CA_INTERMEDIATE_CERT_KEY}.pem \ + -ca-key ./${SERVER_PATH}/${CA_INTERMEDIATE_CERT_KEY}-key.pem \ + -config ./${SERVER_PATH}/ca-config.json \ + -profile server \ + -hostname "${SERVER_HOST_NAME},${SERVER_LOCAL_HOST_NAME}${SERVER_PUBLIC_HOST_NAMES:+, }${SERVER_PUBLIC_HOST_NAMES}" \ + - \ + <<-CONFIG | cfssljson -bare ./${SERVER_PATH}/${BS_SERVER_CERT_KEY} +{ + "CN": "${SERVER_LOCAL_HOST_NAME}" +} +CONFIG + +echo "====================================================" +echo -e "Add the Bootstrap server certificate (${BS_SERVER_CERT_KEY}.pem) to keystore: ${SERVER_JKS_FOR_TEST}.jks" +echo "====================================================" +cat ./${SERVER_PATH}/${BS_SERVER_CERT_KEY}.pem ${CA_LIST_CERT_FOR_CAT} > ./${SERVER_PATH}/${BS_SERVER_CERT_CHAIN}.pem +openssl pkcs12 -export -in ./${SERVER_PATH}/${BS_SERVER_CERT_CHAIN}.pem -inkey ./${SERVER_PATH}/${BS_SERVER_CERT_KEY}-key.pem -out ./${SERVER_PATH}/${BS_SERVER_CERT_KEY}.p12 -name ${BS_SERVER_CERT_ALIAS} -CAfile ./${SERVER_PATH}/${CA_INTERMEDIATE_CERT_KEY}.pem -caname ${CA_ROOT_ALIAS} -passin pass:${STORE_PASS_PWD} -passout pass:${STORE_PASS_PWD} +keytool -importkeystore -deststorepass ${STORE_PASS_PWD} -destkeypass ${STORE_PASS_PWD} -destkeystore ./${SERVER_PATH}/${SERVER_JKS_FOR_TEST}.jks -srckeystore ./${SERVER_PATH}/${BS_SERVER_CERT_KEY}.p12 -srcstoretype PKCS12 -srcstorepass ${STORE_PASS_PWD} -alias ${BS_SERVER_CERT_ALIAS} + + +keytool -list -v -keystore ./${SERVER_PATH}/lwm2mserver.jks -storepass server_ks_password -storetype PKCS12 + +rm ./${SERVER_PATH}/*.p12 2> /dev/null +rm ./${SERVER_PATH}/*.csr 2> /dev/null +rm ./${SERVER_PATH}/*.json 2> /dev/null +rm ./${SERVER_PATH}/${CA_INTERMEDIATE_CERT_KEY_PREF}* 2> /dev/null +rm ./${SERVER_PATH}/${CA_ROOT_CERT_KEY}* 2> /dev/null +mv ./${SERVER_PATH}/${SERVER_CERT_KEY}-key.pem ./${SERVER_PATH}/${SERVER_CERT_KEY}_key.pem +mv ./${SERVER_PATH}/${BS_SERVER_CERT_KEY}-key.pem ./${SERVER_PATH}/${BS_SERVER_CERT_KEY}_key.pem + From 46e301b2dbc5bc9aaa6e8850961ee5fad1f08093 Mon Sep 17 00:00:00 2001 From: desoliture Date: Wed, 5 Jan 2022 18:14:37 +0200 Subject: [PATCH 056/798] Refactoring for alarm rule schedule add getDynamicValue method to AlarmSchedule interface, refactor main isActive processing methods for work with schedules and dynamic values --- .../data/device/profile/AlarmSchedule.java | 3 ++ .../data/device/profile/AnyTimeSchedule.java | 7 +++ .../device/profile/SpecificTimeSchedule.java | 1 - .../rule/engine/profile/AlarmRuleState.java | 47 ++++++++----------- .../rule/engine/profile/ProfileState.java | 15 +----- 5 files changed, 31 insertions(+), 42 deletions(-) diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/AlarmSchedule.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/AlarmSchedule.java index 1d5eceb1aa..1fc39ef147 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/AlarmSchedule.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/AlarmSchedule.java @@ -18,6 +18,7 @@ package org.thingsboard.server.common.data.device.profile; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonSubTypes; import com.fasterxml.jackson.annotation.JsonTypeInfo; +import org.thingsboard.server.common.data.query.DynamicValue; import java.io.Serializable; @@ -34,4 +35,6 @@ public interface AlarmSchedule extends Serializable { AlarmScheduleType getType(); + DynamicValue getDynamicValue(); + } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/AnyTimeSchedule.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/AnyTimeSchedule.java index e02086902f..94eea60950 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/AnyTimeSchedule.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/AnyTimeSchedule.java @@ -15,6 +15,8 @@ */ package org.thingsboard.server.common.data.device.profile; +import org.thingsboard.server.common.data.query.DynamicValue; + public class AnyTimeSchedule implements AlarmSchedule { @Override @@ -22,4 +24,9 @@ public class AnyTimeSchedule implements AlarmSchedule { return AlarmScheduleType.ANY_TIME; } + @Override + public DynamicValue getDynamicValue() { + return null; + } + } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/SpecificTimeSchedule.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/SpecificTimeSchedule.java index 57e57a1b24..0b8dfde791 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/SpecificTimeSchedule.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/SpecificTimeSchedule.java @@ -18,7 +18,6 @@ package org.thingsboard.server.common.data.device.profile; import lombok.Data; import org.thingsboard.server.common.data.query.DynamicValue; -import java.util.List; import java.util.Set; @Data diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/AlarmRuleState.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/AlarmRuleState.java index c5ad4607b2..abcc414206 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/AlarmRuleState.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/AlarmRuleState.java @@ -27,6 +27,7 @@ import org.thingsboard.server.common.data.device.profile.AlarmConditionSpec; import org.thingsboard.server.common.data.device.profile.AlarmConditionSpecType; import org.thingsboard.server.common.data.device.profile.AlarmRule; import org.thingsboard.server.common.data.device.profile.CustomTimeSchedule; +import org.thingsboard.server.common.data.device.profile.AlarmSchedule; import org.thingsboard.server.common.data.device.profile.CustomTimeScheduleItem; import org.thingsboard.server.common.data.device.profile.DurationAlarmConditionSpec; import org.thingsboard.server.common.data.device.profile.RepeatingAlarmConditionSpec; @@ -139,35 +140,27 @@ class AlarmRuleState { switch (alarmRule.getSchedule().getType()) { case ANY_TIME: return true; - case SPECIFIC_TIME: { - SpecificTimeSchedule defaultSchedule = (SpecificTimeSchedule) alarmRule.getSchedule(); - EntityKeyValue dynamicValue = getDynamicValue(data, defaultSchedule.getDynamicValue()); - - SpecificTimeSchedule schedule; - try { - schedule = JsonConverter.parse(dynamicValue.getJsonValue(), SpecificTimeSchedule.class); - } catch (Exception e) { - schedule = defaultSchedule; - } - - return isActiveSpecific(schedule, eventTs); - } - case CUSTOM: { - CustomTimeSchedule defaultSchedule = (CustomTimeSchedule) alarmRule.getSchedule(); - EntityKeyValue dynamicValue = getDynamicValue(data, defaultSchedule.getDynamicValue()); + case SPECIFIC_TIME: + return isActiveSpecific((SpecificTimeSchedule) getSchedule(data, alarmRule), eventTs); + case CUSTOM: + return isActiveCustom((CustomTimeSchedule) getSchedule(data, alarmRule), eventTs); + default: + throw new RuntimeException("Unsupported schedule type: " + alarmRule.getSchedule().getType()); + } + } - CustomTimeSchedule schedule; - try { - schedule = JsonConverter.parse(dynamicValue.getJsonValue(), CustomTimeSchedule.class); - } catch (Exception e) { - schedule = defaultSchedule; - } + private AlarmSchedule getSchedule(DataSnapshot data, AlarmRule alarmRule) { + AlarmSchedule schedule = alarmRule.getSchedule(); + EntityKeyValue dynamicValue = getDynamicValue(data, schedule.getDynamicValue()); - return isActiveCustom(schedule, eventTs); + if (dynamicValue != null) { + try { + return JsonConverter.parse(dynamicValue.getJsonValue(), alarmRule.getSchedule().getClass()); + } catch (Exception e) { + log.trace("Failed to parse AlarmSchedule from dynamicValue: {}", dynamicValue.getJsonValue(), e); } - default: - throw new RuntimeException("Unsupported schedule type: " + alarmRule.getSchedule().getType()); } + return schedule; } private boolean isActiveSpecific(SpecificTimeSchedule schedule, long eventTs) { @@ -252,7 +245,7 @@ class AlarmRuleState { long repeatingTimes = 0; AlarmConditionSpec alarmConditionSpec = getSpec(); AlarmConditionSpecType specType = alarmConditionSpec.getType(); - if(specType.equals(AlarmConditionSpecType.REPEATING)) { + if (specType.equals(AlarmConditionSpecType.REPEATING)) { RepeatingAlarmConditionSpec repeating = (RepeatingAlarmConditionSpec) spec; repeatingTimes = repeating.getPredicate().getDefaultValue(); @@ -272,7 +265,7 @@ class AlarmRuleState { long durationTimeInMs = 0; AlarmConditionSpec alarmConditionSpec = getSpec(); AlarmConditionSpecType specType = alarmConditionSpec.getType(); - if(specType.equals(AlarmConditionSpecType.DURATION)) { + if (specType.equals(AlarmConditionSpecType.DURATION)) { DurationAlarmConditionSpec duration = (DurationAlarmConditionSpec) spec; TimeUnit timeUnit = duration.getUnit(); diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/ProfileState.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/ProfileState.java index c8751d65f7..dcfcea98ba 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/ProfileState.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/ProfileState.java @@ -27,8 +27,6 @@ import org.thingsboard.server.common.data.device.profile.AlarmRule; import org.thingsboard.server.common.data.device.profile.DeviceProfileAlarm; import org.thingsboard.server.common.data.device.profile.DurationAlarmConditionSpec; import org.thingsboard.server.common.data.device.profile.RepeatingAlarmConditionSpec; -import org.thingsboard.server.common.data.device.profile.CustomTimeSchedule; -import org.thingsboard.server.common.data.device.profile.SpecificTimeSchedule; import org.thingsboard.server.common.data.device.profile.AlarmSchedule; import org.thingsboard.server.common.data.id.DeviceProfileId; import org.thingsboard.server.common.data.query.ComplexFilterPredicate; @@ -99,18 +97,7 @@ class ProfileState { } private void addScheduleDynamicValues(AlarmSchedule schedule) { - DynamicValue dynamicValue = null; - switch (schedule.getType()) { - case SPECIFIC_TIME: - SpecificTimeSchedule specificTimeSchedule = (SpecificTimeSchedule) schedule; - dynamicValue = specificTimeSchedule.getDynamicValue(); - break; - case CUSTOM: - CustomTimeSchedule customTimeSchedule = (CustomTimeSchedule) schedule; - dynamicValue = customTimeSchedule.getDynamicValue(); - break; - } - + DynamicValue dynamicValue = schedule.getDynamicValue(); if (dynamicValue != null) { entityKeys.add( new AlarmConditionFilterKey(AlarmConditionKeyType.ATTRIBUTE, From 03256deee78c9b30d2b13a646c5c53db9a03bfbc Mon Sep 17 00:00:00 2001 From: desoliture Date: Thu, 6 Jan 2022 14:05:12 +0200 Subject: [PATCH 057/798] add corresponding tests for schedule from dynamic values --- .../profile/TbDeviceProfileNodeTest.java | 179 ++++++++++++++++++ 1 file changed, 179 insertions(+) diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/profile/TbDeviceProfileNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/profile/TbDeviceProfileNodeTest.java index b2c79102c4..63dc9a81d2 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/profile/TbDeviceProfileNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/profile/TbDeviceProfileNodeTest.java @@ -44,6 +44,8 @@ import org.thingsboard.server.common.data.device.profile.DeviceProfileAlarm; import org.thingsboard.server.common.data.device.profile.DeviceProfileData; import org.thingsboard.server.common.data.device.profile.DurationAlarmConditionSpec; import org.thingsboard.server.common.data.device.profile.RepeatingAlarmConditionSpec; +import org.thingsboard.server.common.data.device.profile.CustomTimeSchedule; +import org.thingsboard.server.common.data.device.profile.CustomTimeScheduleItem; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.DeviceProfileId; @@ -71,6 +73,7 @@ import java.math.RoundingMode; import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.ArrayList; import java.util.Optional; import java.util.TreeMap; import java.util.UUID; @@ -1086,6 +1089,182 @@ public class TbDeviceProfileNodeTest { verify(ctx, Mockito.never()).tellFailure(Mockito.any(), Mockito.any()); } + @Test + public void testActiveAlarmScheduleFromDynamicValuesWhenDefaultScheduleIsInactive() throws Exception { + init(); + + DeviceProfile deviceProfile = new DeviceProfile(); + deviceProfile.setId(deviceProfileId); + DeviceProfileData deviceProfileData = new DeviceProfileData(); + + Device device = new Device(); + device.setId(deviceId); + device.setCustomerId(customerId); + + AttributeKvCompositeKey compositeKeyActiveSchedule = new AttributeKvCompositeKey( + EntityType.TENANT, deviceId.getId(), "SERVER_SCOPE", "dynamicValueActiveSchedule" + ); + + AttributeKvEntity attributeKvEntityActiveSchedule = new AttributeKvEntity(); + attributeKvEntityActiveSchedule.setId(compositeKeyActiveSchedule); + attributeKvEntityActiveSchedule.setJsonValue( + "{\"timezone\":\"Europe/Kiev\",\"items\":[{\"enabled\":true,\"dayOfWeek\":1,\"startsOn\":0,\"endsOn\":8.64e+7},{\"enabled\":true,\"dayOfWeek\":2,\"startsOn\":0,\"endsOn\":8.64e+7},{\"enabled\":true,\"dayOfWeek\":3,\"startsOn\":0,\"endsOn\":8.64e+7},{\"enabled\":true,\"dayOfWeek\":4,\"startsOn\":0,\"endsOn\":8.64e+7},{\"enabled\":true,\"dayOfWeek\":5,\"startsOn\":0,\"endsOn\":8.64e+7},{\"enabled\":true,\"dayOfWeek\":6,\"startsOn\":8.64e+7,\"endsOn\":8.64e+7},{\"enabled\":true,\"dayOfWeek\":7,\"startsOn\":0,\"endsOn\":8.64e+7}],\"dynamicValue\":null}" + ); + attributeKvEntityActiveSchedule.setLastUpdateTs(0L); + + AttributeKvEntry entryActiveSchedule = attributeKvEntityActiveSchedule.toData(); + + ListenableFuture> listListenableFutureActiveSchedule = + Futures.immediateFuture(Collections.singletonList(entryActiveSchedule)); + + AlarmConditionFilter highTempFilter = new AlarmConditionFilter(); + highTempFilter.setKey(new AlarmConditionFilterKey(AlarmConditionKeyType.TIME_SERIES, "temperature")); + highTempFilter.setValueType(EntityKeyValueType.NUMERIC); + NumericFilterPredicate highTemperaturePredicate = new NumericFilterPredicate(); + highTemperaturePredicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER); + highTemperaturePredicate.setValue(new FilterPredicateValue<>( + 0.0, + null, + null + )); + highTempFilter.setPredicate(highTemperaturePredicate); + AlarmCondition alarmCondition = new AlarmCondition(); + alarmCondition.setCondition(Collections.singletonList(highTempFilter)); + + CustomTimeSchedule schedule = new CustomTimeSchedule(); + schedule.setItems(Collections.emptyList()); + schedule.setDynamicValue(new DynamicValue<>(DynamicValueSourceType.CURRENT_DEVICE, "dynamicValueActiveSchedule", false)); + + AlarmRule alarmRule = new AlarmRule(); + alarmRule.setCondition(alarmCondition); + alarmRule.setSchedule(schedule); + DeviceProfileAlarm deviceProfileAlarmActiveSchedule = new DeviceProfileAlarm(); + deviceProfileAlarmActiveSchedule.setId("highTemperatureAlarmID"); + deviceProfileAlarmActiveSchedule.setAlarmType("highTemperatureAlarm"); + deviceProfileAlarmActiveSchedule.setCreateRules(new TreeMap<>(Collections.singletonMap(AlarmSeverity.CRITICAL, alarmRule))); + + deviceProfileData.setAlarms(Collections.singletonList(deviceProfileAlarmActiveSchedule)); + deviceProfile.setProfileData(deviceProfileData); + + Mockito.when(cache.get(tenantId, deviceId)).thenReturn(deviceProfile); + Mockito.when(timeseriesService.findLatest(tenantId, deviceId, Collections.singleton("temperature"))) + .thenReturn(Futures.immediateFuture(Collections.emptyList())); + Mockito.when(alarmService.findLatestByOriginatorAndType(tenantId, deviceId, "highTemperatureAlarm")) + .thenReturn(Futures.immediateFuture(null)); + Mockito.when(alarmService.createOrUpdateAlarm(Mockito.any())).thenAnswer(AdditionalAnswers.returnsFirstArg()); + Mockito.when(ctx.getAttributesService()).thenReturn(attributesService); + Mockito.when(attributesService.find(eq(tenantId), eq(deviceId), Mockito.anyString(), Mockito.anySet())) + .thenReturn(listListenableFutureActiveSchedule); + + TbMsg theMsg = TbMsg.newMsg("ALARM", deviceId, new TbMsgMetaData(), ""); + Mockito.when(ctx.newMsg(Mockito.anyString(), Mockito.anyString(), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.anyString())) + .thenReturn(theMsg); + + ObjectNode data = mapper.createObjectNode(); + data.put("temperature", 35); + TbMsg msg = TbMsg.newMsg(SessionMsgType.POST_TELEMETRY_REQUEST.name(), deviceId, new TbMsgMetaData(), + TbMsgDataType.JSON, mapper.writeValueAsString(data), null, null); + + node.onMsg(ctx, msg); + verify(ctx).tellSuccess(msg); + verify(ctx).enqueueForTellNext(theMsg, "Alarm Created"); + verify(ctx, Mockito.never()).tellFailure(Mockito.any(), Mockito.any()); + } + + @Test + public void testInactiveAlarmScheduleFromDynamicValuesWhenDefaultScheduleIsActive() throws Exception { + init(); + + DeviceProfile deviceProfile = new DeviceProfile(); + deviceProfile.setId(deviceProfileId); + DeviceProfileData deviceProfileData = new DeviceProfileData(); + + Device device = new Device(); + device.setId(deviceId); + device.setCustomerId(customerId); + + AttributeKvCompositeKey compositeKeyInactiveSchedule = new AttributeKvCompositeKey( + EntityType.TENANT, deviceId.getId(), "SERVER_SCOPE", "dynamicValueInactiveSchedule" + ); + + AttributeKvEntity attributeKvEntityInactiveSchedule = new AttributeKvEntity(); + attributeKvEntityInactiveSchedule.setId(compositeKeyInactiveSchedule); + attributeKvEntityInactiveSchedule.setJsonValue( + "{\"timezone\":\"Europe/Kiev\",\"items\":[{\"enabled\":false,\"dayOfWeek\":1,\"startsOn\":0,\"endsOn\":0},{\"enabled\":false,\"dayOfWeek\":2,\"startsOn\":0,\"endsOn\":0},{\"enabled\":false,\"dayOfWeek\":3,\"startsOn\":0,\"endsOn\":0},{\"enabled\":false,\"dayOfWeek\":4,\"startsOn\":0,\"endsOn\":0},{\"enabled\":false,\"dayOfWeek\":5,\"startsOn\":0,\"endsOn\":0},{\"enabled\":false,\"dayOfWeek\":6,\"startsOn\":0,\"endsOn\":0},{\"enabled\":false,\"dayOfWeek\":7,\"startsOn\":0,\"endsOn\":0}],\"dynamicValue\":null}" + ); + + attributeKvEntityInactiveSchedule.setLastUpdateTs(0L); + + AttributeKvEntry entryInactiveSchedule = attributeKvEntityInactiveSchedule.toData(); + + ListenableFuture> listListenableFutureInactiveSchedule = + Futures.immediateFuture(Collections.singletonList(entryInactiveSchedule)); + + AlarmConditionFilter highTempFilter = new AlarmConditionFilter(); + highTempFilter.setKey(new AlarmConditionFilterKey(AlarmConditionKeyType.TIME_SERIES, "temperature")); + highTempFilter.setValueType(EntityKeyValueType.NUMERIC); + NumericFilterPredicate highTemperaturePredicate = new NumericFilterPredicate(); + highTemperaturePredicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER); + highTemperaturePredicate.setValue(new FilterPredicateValue<>( + 0.0, + null, + null + )); + + highTempFilter.setPredicate(highTemperaturePredicate); + AlarmCondition alarmCondition = new AlarmCondition(); + alarmCondition.setCondition(Collections.singletonList(highTempFilter)); + + CustomTimeSchedule schedule = new CustomTimeSchedule(); + + List items = new ArrayList<>(); + for (int i = 0; i < 7; i++) { + CustomTimeScheduleItem item = new CustomTimeScheduleItem(); + item.setEnabled(true); + item.setDayOfWeek(i + 1); + item.setEndsOn(0); + item.setStartsOn(0); + items.add(item); + } + + schedule.setItems(items); + schedule.setDynamicValue(new DynamicValue<>(DynamicValueSourceType.CURRENT_DEVICE, "dynamicValueInactiveSchedule", false)); + + AlarmRule alarmRule = new AlarmRule(); + alarmRule.setCondition(alarmCondition); + alarmRule.setSchedule(schedule); + DeviceProfileAlarm deviceProfileAlarmNonactiveSchedule = new DeviceProfileAlarm(); + deviceProfileAlarmNonactiveSchedule.setId("highTemperatureAlarmID"); + deviceProfileAlarmNonactiveSchedule.setAlarmType("highTemperatureAlarm"); + deviceProfileAlarmNonactiveSchedule.setCreateRules(new TreeMap<>(Collections.singletonMap(AlarmSeverity.CRITICAL, alarmRule))); + + deviceProfileData.setAlarms(Collections.singletonList(deviceProfileAlarmNonactiveSchedule)); + deviceProfile.setProfileData(deviceProfileData); + + Mockito.when(cache.get(tenantId, deviceId)).thenReturn(deviceProfile); + Mockito.when(timeseriesService.findLatest(tenantId, deviceId, Collections.singleton("temperature"))) + .thenReturn(Futures.immediateFuture(Collections.emptyList())); + Mockito.when(alarmService.findLatestByOriginatorAndType(tenantId, deviceId, "highTemperatureAlarm")) + .thenReturn(Futures.immediateFuture(null)); + Mockito.when(alarmService.createOrUpdateAlarm(Mockito.any())).thenAnswer(AdditionalAnswers.returnsFirstArg()); + Mockito.when(ctx.getAttributesService()).thenReturn(attributesService); + Mockito.when(attributesService.find(eq(tenantId), eq(deviceId), Mockito.anyString(), Mockito.anySet())) + .thenReturn(listListenableFutureInactiveSchedule); + + TbMsg theMsg = TbMsg.newMsg("ALARM", deviceId, new TbMsgMetaData(), ""); + Mockito.when(ctx.newMsg(Mockito.anyString(), Mockito.anyString(), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.anyString())) + .thenReturn(theMsg); + + ObjectNode data = mapper.createObjectNode(); + data.put("temperature", 35); + TbMsg msg = TbMsg.newMsg(SessionMsgType.POST_TELEMETRY_REQUEST.name(), deviceId, new TbMsgMetaData(), + TbMsgDataType.JSON, mapper.writeValueAsString(data), null, null); + + node.onMsg(ctx, msg); + verify(ctx).tellSuccess(msg); + verify(ctx, Mockito.never()).enqueueForTellNext(theMsg, "Alarm Created"); + verify(ctx, Mockito.never()).tellFailure(Mockito.any(), Mockito.any()); + } @Test public void testCurrentCustomersAttributeForDynamicValue() throws Exception { From 8f485010f0b0a4328c29cb23f57c37c1a705e3ee Mon Sep 17 00:00:00 2001 From: desoliture Date: Thu, 6 Jan 2022 16:14:46 +0200 Subject: [PATCH 058/798] refactoring --- .../rule/engine/profile/TbDeviceProfileNodeTest.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/profile/TbDeviceProfileNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/profile/TbDeviceProfileNodeTest.java index 63dc9a81d2..30e2943110 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/profile/TbDeviceProfileNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/profile/TbDeviceProfileNodeTest.java @@ -1246,14 +1246,11 @@ public class TbDeviceProfileNodeTest { .thenReturn(Futures.immediateFuture(Collections.emptyList())); Mockito.when(alarmService.findLatestByOriginatorAndType(tenantId, deviceId, "highTemperatureAlarm")) .thenReturn(Futures.immediateFuture(null)); - Mockito.when(alarmService.createOrUpdateAlarm(Mockito.any())).thenAnswer(AdditionalAnswers.returnsFirstArg()); Mockito.when(ctx.getAttributesService()).thenReturn(attributesService); Mockito.when(attributesService.find(eq(tenantId), eq(deviceId), Mockito.anyString(), Mockito.anySet())) .thenReturn(listListenableFutureInactiveSchedule); TbMsg theMsg = TbMsg.newMsg("ALARM", deviceId, new TbMsgMetaData(), ""); - Mockito.when(ctx.newMsg(Mockito.anyString(), Mockito.anyString(), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.anyString())) - .thenReturn(theMsg); ObjectNode data = mapper.createObjectNode(); data.put("temperature", 35); From 60758375ed02f06e06cce51c31ccf5171bd7c8e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emre=20Karda=C5=9Flar?= Date: Sat, 8 Jan 2022 01:03:54 +0300 Subject: [PATCH 059/798] fix problematic letters for different lang --- .../org/thingsboard/server/dao/audit/AuditLogLevelFilter.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dao/src/main/java/org/thingsboard/server/dao/audit/AuditLogLevelFilter.java b/dao/src/main/java/org/thingsboard/server/dao/audit/AuditLogLevelFilter.java index b73cafc121..effc79f711 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/audit/AuditLogLevelFilter.java +++ b/dao/src/main/java/org/thingsboard/server/dao/audit/AuditLogLevelFilter.java @@ -19,6 +19,7 @@ import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.audit.ActionType; import java.util.HashMap; +import java.util.Locale; import java.util.Map; public class AuditLogLevelFilter { @@ -28,7 +29,7 @@ public class AuditLogLevelFilter { public AuditLogLevelFilter(Map mask) { entityTypeMask.clear(); mask.forEach((entityTypeStr, logLevelMaskStr) -> { - EntityType entityType = EntityType.valueOf(entityTypeStr.toUpperCase()); + EntityType entityType = EntityType.valueOf(entityTypeStr.toUpperCase(Locale.ENGLISH)); AuditLogLevelMask logLevelMask = AuditLogLevelMask.valueOf(logLevelMaskStr.toUpperCase()); entityTypeMask.put(entityType, logLevelMask); }); From eccbd3290c5c443ae60fae111ce9539d28a6e85d Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Sat, 8 Jan 2022 13:06:46 +0200 Subject: [PATCH 060/798] lwm2m tests with NoSec, PSK, X509-trust. RPK, X509_NoTrust - ignore --- .../AbstractSecurityLwM2MIntegrationTest.java | 198 ++++++++++-------- 1 file changed, 105 insertions(+), 93 deletions(-) diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/AbstractSecurityLwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/AbstractSecurityLwM2MIntegrationTest.java index ea30b43436..0c983f9dcf 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/AbstractSecurityLwM2MIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/AbstractSecurityLwM2MIntegrationTest.java @@ -15,7 +15,6 @@ */ package org.thingsboard.server.transport.lwm2m.security; -import org.eclipse.leshan.core.util.Hex; import org.thingsboard.server.common.data.device.credentials.lwm2m.LwM2MBootstrapClientCredentials; import org.thingsboard.server.common.data.device.credentials.lwm2m.NoSecBootstrapClientCredential; import org.thingsboard.server.dao.service.DaoSqlTest; @@ -24,144 +23,157 @@ import org.thingsboard.server.transport.lwm2m.client.LwM2MTestClient; import java.io.IOException; import java.io.InputStream; -import java.math.BigInteger; -import java.security.AlgorithmParameters; import java.security.GeneralSecurityException; -import java.security.KeyFactory; import java.security.KeyStore; import java.security.PrivateKey; import java.security.PublicKey; import java.security.cert.Certificate; import java.security.cert.X509Certificate; -import java.security.spec.ECGenParameterSpec; -import java.security.spec.ECParameterSpec; -import java.security.spec.ECPoint; -import java.security.spec.ECPrivateKeySpec; -import java.security.spec.ECPublicKeySpec; -import java.security.spec.KeySpec; @DaoSqlTest public abstract class AbstractSecurityLwM2MIntegrationTest extends AbstractLwM2MIntegrationTest { + protected final String CREDENTIALS_PATH = "lwm2m/credentials/"; // client public key or id used for PSK protected final String pskIdentity; // client public key or id used for PSK protected final String pskKey; // client private/secret key used for PSK - protected final PublicKey clientPublicKey; // client public key used for RPK - protected final PrivateKey clientPrivateKey; // client private key used for RPK - protected final PublicKey serverPublicKey; // server public key used for RPK - protected final PrivateKey serverPrivateKey; // server private key used for RPK - - // client private key used for X509 - protected final PrivateKey clientPrivateKeyFromCert; - // server private key used for X509 - protected final PrivateKey serverPrivateKeyFromCert; - // client certificate signed by rootCA with a good CN (CN start by leshan_integration_test) - protected final X509Certificate clientX509Cert; - // client certificate signed by rootCA but with bad CN (CN does not start by leshan_integration_test) - protected final X509Certificate clientX509CertWithBadCN; - // client certificate self-signed with a good CN (CN start by leshan_integration_test) - protected final X509Certificate clientX509CertSelfSigned; - // client certificate signed by another CA (not rootCA) with a good CN (CN start by leshan_integration_test) - protected final X509Certificate clientX509CertNotTrusted; - // server certificate signed by rootCA - protected final X509Certificate serverX509Cert; +// protected final PublicKey clientPublicKey; // client public key used for RPK +// protected final PrivateKey clientPrivateKey; // client private key used for RPK + + + +// // client certificate signed by rootCA but with bad CN (CN does not start by leshan_integration_test) +// protected final X509Certificate clientX509CertWithBadCN; +// // client certificate self-signed with a good CN (CN start by leshan_integration_test) +// protected final X509Certificate clientX509CertSelfSigned; +// // client certificate signed by another CA (not rootCA) with a good CN (CN start by leshan_integration_test) +// protected final X509Certificate clientX509CertNotTrusted; + // self-signed server certificate - protected final X509Certificate serverX509CertSelfSigned; - // rootCA used by the server - protected final X509Certificate rootCAX509Cert; +// protected final X509Certificate serverX509CertSelfSigned; +// // rootCA used by the server +// protected final X509Certificate rootCAX509Cert; // certificates trustedby the server (should contain rootCA) - protected final Certificate[] trustedCertificates = new Certificate[1]; - protected static final String ENDPOINT = "deviceAEndpoint"; + // Server + protected static final String SERVER_JKS_FOR_TEST = "lwm2mserver"; + protected static final String SERVER_STORE_PWD = "server_ks_password"; + protected static final String SERVER_CERT_ALIAS = "server"; + protected final X509Certificate serverX509Cert; // server certificate signed by rootCA +// protected final PrivateKey serverPrivateKeyFromCert; // server private key used for RPK and X509 + protected final PublicKey serverPublicKeyFromCert; // server public key used for RPK +// // Server Trust +// protected final Certificate[] trustedCertificates = new Certificate[1]; + + // Client protected LwM2MTestClient client; + protected static final String CLIENT_ENDPOINT_NO_SEC = "deviceNoSec"; + protected static final String CLIENT_ENDPOINT_RPK = "deviceRPK"; + protected static final String CLIENT_ENDPOINT_NO_TRUST = "deviceAEndpoint"; + protected static final String CLIENT_ENDPOINT_TRUST = "LwX50900000000"; + protected static final String CLIENT_JKS_FOR_TEST = "lwm2mclient"; + protected static final String CLIENT_STORE_PWD = "client_ks_password"; + + protected static final String CLIENT_CERT_ALIAS = "client_alias_00000000"; + + protected final X509Certificate clientX509Cert; // client certificate signed by intermediate, rootCA with a good CN ("host name") + protected final PrivateKey clientPrivateKeyFromCert; // client private key used for X509 and RPK + protected final PublicKey clientPublicKeyFromCert; // client public key used for RPK + private final String[] resources = new String[]{"1.xml", "2.xml", "3.xml", "5.xml", "9.xml"}; + private final LwM2MBootstrapClientCredentials defaultBootstrapCredentials; - private final String[] resources = new String[]{"1.xml", "2.xml", "3.xml", "5.xml", "9.xml"}; + public AbstractSecurityLwM2MIntegrationTest() { // create client credentials setResources(this.resources); - setEndpoint(ENDPOINT); + setEndpoint(CLIENT_ENDPOINT_NO_TRUST); try { - // Get keys PSK +// Get keys PSK this.pskIdentity = "SOME_PSK_ID"; this.pskKey = "73656372657450534b73656372657450"; - // Get point values - byte[] publicX = Hex - .decodeHex("89c048261979208666f2bfb188be1968fc9021c416ce12828c06f4e314c167b5".toCharArray()); - byte[] publicY = Hex - .decodeHex("cbf1eb7587f08e01688d9ada4be859137ca49f79394bad9179326b3090967b68".toCharArray()); - byte[] privateS = Hex - .decodeHex("e67b68d2aaeb6550f19d98cade3ad62b39532e02e6b422e1f7ea189dabaea5d2".toCharArray()); - - // Get Elliptic Curve Parameter spec for secp256r1 - AlgorithmParameters algoParameters = AlgorithmParameters.getInstance("EC"); - algoParameters.init(new ECGenParameterSpec("secp256r1")); - ECParameterSpec parameterSpec = algoParameters.getParameterSpec(ECParameterSpec.class); - - // Create key specs - KeySpec publicKeySpec = new ECPublicKeySpec(new ECPoint(new BigInteger(publicX), new BigInteger(publicY)), - parameterSpec); - KeySpec privateKeySpec = new ECPrivateKeySpec(new BigInteger(privateS), parameterSpec); - - // Get keys RPK - clientPublicKey = KeyFactory.getInstance("EC").generatePublic(publicKeySpec); - clientPrivateKey = KeyFactory.getInstance("EC").generatePrivate(privateKeySpec); +// // Get point values +// byte[] publicX = Hex +// .decodeHex("89c048261979208666f2bfb188be1968fc9021c416ce12828c06f4e314c167b5".toCharArray()); +// byte[] publicY = Hex +// .decodeHex("cbf1eb7587f08e01688d9ada4be859137ca49f79394bad9179326b3090967b68".toCharArray()); +// byte[] privateS = Hex +// .decodeHex("e67b68d2aaeb6550f19d98cade3ad62b39532e02e6b422e1f7ea189dabaea5d2".toCharArray()); +// +// // Get Elliptic Curve Parameter spec for secp256r1 +// AlgorithmParameters algoParameters = AlgorithmParameters.getInstance("EC"); +// algoParameters.init(new ECGenParameterSpec("secp256r1")); +// ECParameterSpec parameterSpec = algoParameters.getParameterSpec(ECParameterSpec.class); +// +// // Create key specs +// KeySpec publicKeySpec = new ECPublicKeySpec(new ECPoint(new BigInteger(publicX), new BigInteger(publicY)), +// parameterSpec); +// KeySpec privateKeySpec = new ECPrivateKeySpec(new BigInteger(privateS), parameterSpec); +// +// // Get keys RPK +// clientPublicKey = KeyFactory.getInstance("EC").generatePublic(publicKeySpec); +// clientPrivateKey = KeyFactory.getInstance("EC").generatePrivate(privateKeySpec); // Get certificates from key store - char[] clientKeyStorePwd = "client".toCharArray(); + char[] clientKeyStorePwd = CLIENT_STORE_PWD.toCharArray(); KeyStore clientKeyStore = KeyStore.getInstance(KeyStore.getDefaultType()); - try (InputStream clientKeyStoreFile = this.getClass().getClassLoader().getResourceAsStream("lwm2m/credentials/clientKeyStore.jks")) { + try (InputStream clientKeyStoreFile = this.getClass().getClassLoader().getResourceAsStream(CREDENTIALS_PATH + CLIENT_JKS_FOR_TEST + ".jks")) { clientKeyStore.load(clientKeyStoreFile, clientKeyStorePwd); } - clientPrivateKeyFromCert = (PrivateKey) clientKeyStore.getKey("client", clientKeyStorePwd); - clientX509Cert = (X509Certificate) clientKeyStore.getCertificate("client"); - clientX509CertWithBadCN = (X509Certificate) clientKeyStore.getCertificate("client_bad_cn"); - clientX509CertSelfSigned = (X509Certificate) clientKeyStore.getCertificate("client_self_signed"); - clientX509CertNotTrusted = (X509Certificate) clientKeyStore.getCertificate("client_not_trusted"); + clientPrivateKeyFromCert = (PrivateKey) clientKeyStore.getKey(CLIENT_CERT_ALIAS, clientKeyStorePwd); + clientX509Cert = (X509Certificate) clientKeyStore.getCertificate(CLIENT_CERT_ALIAS); + clientPublicKeyFromCert = clientX509Cert.getPublicKey(); + +// clientX509CertWithBadCN = (X509Certificate) clientKeyStore.getCertificate("client_bad_cn"); +// clientX509CertSelfSigned = (X509Certificate) clientKeyStore.getCertificate("client_self_signed"); +// clientX509CertNotTrusted = (X509Certificate) clientKeyStore.getCertificate("client_not_trusted"); } catch (GeneralSecurityException | IOException e) { throw new RuntimeException(e); } // create server credentials try { - // Get point values - byte[] publicX = Hex - .decodeHex("fcc28728c123b155be410fc1c0651da374fc6ebe7f96606e90d927d188894a73".toCharArray()); - byte[] publicY = Hex - .decodeHex("d2ffaa73957d76984633fc1cc54d0b763ca0559a9dff9706e9f4557dacc3f52a".toCharArray()); - byte[] privateS = Hex - .decodeHex("1dae121ba406802ef07c193c1ee4df91115aabd79c1ed7f4c0ef7ef6a5449400".toCharArray()); - - // Get Elliptic Curve Parameter spec for secp256r1 - AlgorithmParameters algoParameters = AlgorithmParameters.getInstance("EC"); - algoParameters.init(new ECGenParameterSpec("secp256r1")); - ECParameterSpec parameterSpec = algoParameters.getParameterSpec(ECParameterSpec.class); - - // Create key specs - KeySpec publicKeySpec = new ECPublicKeySpec(new ECPoint(new BigInteger(publicX), new BigInteger(publicY)), - parameterSpec); - KeySpec privateKeySpec = new ECPrivateKeySpec(new BigInteger(privateS), parameterSpec); - - // Get keys - serverPublicKey = KeyFactory.getInstance("EC").generatePublic(publicKeySpec); - serverPrivateKey = KeyFactory.getInstance("EC").generatePrivate(privateKeySpec); +// // Get point values +// byte[] publicX = Hex +// .decodeHex("fcc28728c123b155be410fc1c0651da374fc6ebe7f96606e90d927d188894a73".toCharArray()); +// byte[] publicY = Hex +// .decodeHex("d2ffaa73957d76984633fc1cc54d0b763ca0559a9dff9706e9f4557dacc3f52a".toCharArray()); +// byte[] privateS = Hex +// .decodeHex("1dae121ba406802ef07c193c1ee4df91115aabd79c1ed7f4c0ef7ef6a5449400".toCharArray()); +// +// // Get Elliptic Curve Parameter spec for secp256r1 +// AlgorithmParameters algoParameters = AlgorithmParameters.getInstance("EC"); +// algoParameters.init(new ECGenParameterSpec("secp256r1")); +// ECParameterSpec parameterSpec = algoParameters.getParameterSpec(ECParameterSpec.class); +// +// // Create key specs +// KeySpec publicKeySpec = new ECPublicKeySpec(new ECPoint(new BigInteger(publicX), new BigInteger(publicY)), +// parameterSpec); +// KeySpec privateKeySpec = new ECPrivateKeySpec(new BigInteger(privateS), parameterSpec); +// +// // Get keys +// serverPublicKey = KeyFactory.getInstance("EC").generatePublic(publicKeySpec); +// serverPrivateKey = KeyFactory.getInstance("EC").generatePrivate(privateKeySpec); + // Get certificates from key store - char[] serverKeyStorePwd = "server".toCharArray(); + char[] serverKeyStorePwd = SERVER_STORE_PWD.toCharArray(); KeyStore serverKeyStore = KeyStore.getInstance(KeyStore.getDefaultType()); - try (InputStream serverKeyStoreFile = this.getClass().getClassLoader().getResourceAsStream("lwm2m/credentials/serverKeyStore.jks")) { + try (InputStream serverKeyStoreFile = this.getClass().getClassLoader().getResourceAsStream(CREDENTIALS_PATH + SERVER_JKS_FOR_TEST + ".jks")) { serverKeyStore.load(serverKeyStoreFile, serverKeyStorePwd); } - serverPrivateKeyFromCert = (PrivateKey) serverKeyStore.getKey("server", serverKeyStorePwd); - rootCAX509Cert = (X509Certificate) serverKeyStore.getCertificate("rootCA"); - serverX509Cert = (X509Certificate) serverKeyStore.getCertificate("server"); - serverX509CertSelfSigned = (X509Certificate) serverKeyStore.getCertificate("server_self_signed"); - trustedCertificates[0] = serverX509Cert; +// serverPrivateKeyFromCert = (PrivateKey) serverKeyStore.getKey("server", serverKeyStorePwd); + serverX509Cert = (X509Certificate) serverKeyStore.getCertificate(SERVER_CERT_ALIAS); + serverPublicKeyFromCert = serverX509Cert.getPublicKey(); +// rootCAX509Cert = (X509Certificate) serverKeyStore.getCertificate("rootCA"); + +// serverX509CertSelfSigned = (X509Certificate) serverKeyStore.getCertificate("server_self_signed"); +// trustedCertificates[0] = serverX509Cert; } catch (GeneralSecurityException | IOException e) { throw new RuntimeException(e); } From ba970c5d88eb1a723b12e2d8da7822e79c352554 Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Sat, 8 Jan 2022 13:07:55 +0200 Subject: [PATCH 061/798] lwm2m tests with NoSec, PSK, X509-trust. RPK, X509_NoTrust - ignore --- .../sql/NoSecLwM2MIntegrationTest.java | 5 +++-- .../security/sql/PskLwm2mIntegrationTest.java | 5 +++-- .../security/sql/RpkLwM2MIntegrationTest.java | 12 ++++++------ .../sql/X509_NoTrustLwM2MIntegrationTest.java | 18 +++++++++--------- .../sql/X509_TrustLwM2MIntegrationTest.java | 6 ++---- .../resources/application-test.properties | 18 +++++++++--------- .../lwm2m/credentials/clientKeyStore.jks | Bin 4810 -> 0 bytes .../lwm2m/credentials/lwm2mclient.jks | Bin 0 -> 17660 bytes .../lwm2m/credentials/lwm2mserver.jks | Bin 0 -> 6432 bytes .../credentials/lwm2mtruststorechain.jks | Bin 0 -> 2982 bytes .../lwm2m/credentials/serverKeyStore.jks | Bin 3806 -> 0 bytes 11 files changed, 32 insertions(+), 32 deletions(-) delete mode 100644 application/src/test/resources/lwm2m/credentials/clientKeyStore.jks create mode 100644 application/src/test/resources/lwm2m/credentials/lwm2mclient.jks create mode 100644 application/src/test/resources/lwm2m/credentials/lwm2mserver.jks create mode 100644 application/src/test/resources/lwm2m/credentials/lwm2mtruststorechain.jks delete mode 100644 application/src/test/resources/lwm2m/credentials/serverKeyStore.jks diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/NoSecLwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/NoSecLwM2MIntegrationTest.java index 0e86c6a438..8331b99fff 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/NoSecLwM2MIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/NoSecLwM2MIntegrationTest.java @@ -16,6 +16,7 @@ package org.thingsboard.server.transport.lwm2m.security.sql; import lombok.extern.slf4j.Slf4j; +import org.junit.Ignore; import org.junit.Test; import org.thingsboard.server.common.data.device.credentials.lwm2m.NoSecClientCredential; import org.thingsboard.server.transport.lwm2m.security.AbstractSecurityLwM2MIntegrationTest; @@ -28,8 +29,8 @@ public class NoSecLwM2MIntegrationTest extends AbstractSecurityLwM2MIntegrationT @Test public void testConnectAndObserveTelemetry() throws Exception { - NoSecClientCredential clientCredentials = createNoSecClientCredentials(ENDPOINT); - super.basicTestConnectionObserveTelemetry(SECURITY, clientCredentials, COAP_CONFIG, ENDPOINT); + NoSecClientCredential clientCredentials = createNoSecClientCredentials(CLIENT_ENDPOINT_NO_SEC); + super.basicTestConnectionObserveTelemetry(SECURITY, clientCredentials, COAP_CONFIG, CLIENT_ENDPOINT_NO_SEC); } } diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/PskLwm2mIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/PskLwm2mIntegrationTest.java index af9a668376..5850e52015 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/PskLwm2mIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/PskLwm2mIntegrationTest.java @@ -17,6 +17,7 @@ package org.thingsboard.server.transport.lwm2m.security.sql; import org.eclipse.leshan.client.object.Security; import org.eclipse.leshan.core.util.Hex; +import org.junit.Ignore; import org.junit.Test; import org.thingsboard.server.common.data.device.credentials.lwm2m.PSKClientCredential; import org.thingsboard.server.transport.lwm2m.security.AbstractSecurityLwM2MIntegrationTest; @@ -33,13 +34,13 @@ public class PskLwm2mIntegrationTest extends AbstractSecurityLwM2MIntegrationTes @Test public void testConnectWithPSKAndObserveTelemetry() throws Exception { PSKClientCredential clientCredentials = new PSKClientCredential(); - clientCredentials.setEndpoint(ENDPOINT); + clientCredentials.setEndpoint(CLIENT_ENDPOINT_NO_TRUST); clientCredentials.setKey(pskKey); clientCredentials.setIdentity(pskIdentity); Security security = psk(SECURE_URI, SHORT_SERVER_ID, pskIdentity.getBytes(StandardCharsets.UTF_8), Hex.decodeHex(pskKey.toCharArray())); - super.basicTestConnectionObserveTelemetry(security, clientCredentials, SECURE_COAP_CONFIG, ENDPOINT); + super.basicTestConnectionObserveTelemetry(security, clientCredentials, SECURE_COAP_CONFIG, CLIENT_ENDPOINT_NO_TRUST); } } diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/RpkLwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/RpkLwM2MIntegrationTest.java index 87ca415bd0..e6c26a05d0 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/RpkLwM2MIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/RpkLwM2MIntegrationTest.java @@ -34,13 +34,13 @@ public class RpkLwM2MIntegrationTest extends AbstractSecurityLwM2MIntegrationTes @Test public void testConnectWithRPKAndObserveTelemetry() throws Exception { RPKClientCredential rpkClientCredentials = new RPKClientCredential(); - rpkClientCredentials.setEndpoint(ENDPOINT); - rpkClientCredentials.setKey(new String(Base64.encodeBase64(clientPublicKey.getEncoded()))); + rpkClientCredentials.setEndpoint(CLIENT_ENDPOINT_RPK); + rpkClientCredentials.setKey(new String(Base64.encodeBase64(clientPrivateKeyFromCert.getEncoded()))); Security security = rpk(SECURE_URI, SHORT_SERVER_ID, - clientPublicKey.getEncoded(), - clientPrivateKey.getEncoded(), - serverX509Cert.getPublicKey().getEncoded()); - super.basicTestConnectionObserveTelemetry(security, rpkClientCredentials, SECURE_COAP_CONFIG, ENDPOINT); + clientPublicKeyFromCert.getEncoded(), + clientPrivateKeyFromCert.getEncoded(), + serverPublicKeyFromCert.getEncoded()); + super.basicTestConnectionObserveTelemetry(security, rpkClientCredentials, SECURE_COAP_CONFIG, CLIENT_ENDPOINT_RPK); } } diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/X509_NoTrustLwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/X509_NoTrustLwM2MIntegrationTest.java index d1bc813b50..6ca430327d 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/X509_NoTrustLwM2MIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/X509_NoTrustLwM2MIntegrationTest.java @@ -32,15 +32,15 @@ public class X509_NoTrustLwM2MIntegrationTest extends AbstractSecurityLwM2MInteg @Ignore @Test public void testConnectWithCertAndObserveTelemetry() throws Exception { - X509ClientCredential credentials = new X509ClientCredential(); - credentials.setEndpoint(ENDPOINT); - credentials.setCert(SslUtil.getCertificateString(clientX509CertNotTrusted)); - Security security = x509(SECURE_URI, - SHORT_SERVER_ID, - clientX509CertNotTrusted.getEncoded(), - clientPrivateKeyFromCert.getEncoded(), - serverX509Cert.getEncoded()); - super.basicTestConnectionObserveTelemetry(security, credentials, SECURE_COAP_CONFIG, ENDPOINT); +// X509ClientCredential credentials = new X509ClientCredential(); +// credentials.setEndpoint(CLIENT_ENDPOINT_NO_TRUST); +// credentials.setCert(SslUtil.getCertificateString(clientX509CertNotTrusted)); +// Security security = x509(SECURE_URI, +// SHORT_SERVER_ID, +// clientX509CertNotTrusted.getEncoded(), +// clientPrivateKeyNotTrustedFromCert.getEncoded(), +// serverX509Cert.getEncoded()); +// super.basicTestConnectionObserveTelemetry(security, credentials, SECURE_COAP_CONFIG, CLIENT_ENDPOINT_NO_TRUST); } } diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/X509_TrustLwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/X509_TrustLwM2MIntegrationTest.java index 7c5e48a4b7..28a10fd278 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/X509_TrustLwM2MIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/X509_TrustLwM2MIntegrationTest.java @@ -16,7 +16,6 @@ package org.thingsboard.server.transport.lwm2m.security.sql; import org.eclipse.leshan.client.object.Security; -import org.junit.Ignore; import org.junit.Test; import org.thingsboard.server.common.data.device.credentials.lwm2m.X509ClientCredential; import org.thingsboard.server.transport.lwm2m.security.AbstractSecurityLwM2MIntegrationTest; @@ -28,17 +27,16 @@ import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.SHORT_SERVE public class X509_TrustLwM2MIntegrationTest extends AbstractSecurityLwM2MIntegrationTest { - @Ignore @Test public void testConnectAndObserveTelemetry() throws Exception { X509ClientCredential credentials = new X509ClientCredential(); - credentials.setEndpoint(ENDPOINT); + credentials.setEndpoint(CLIENT_ENDPOINT_TRUST); Security security = x509(SECURE_URI, SHORT_SERVER_ID, clientX509Cert.getEncoded(), clientPrivateKeyFromCert.getEncoded(), serverX509Cert.getEncoded()); - super.basicTestConnectionObserveTelemetry(security, credentials, SECURE_COAP_CONFIG, ENDPOINT); + super.basicTestConnectionObserveTelemetry(security, credentials, SECURE_COAP_CONFIG, CLIENT_ENDPOINT_TRUST); } } diff --git a/application/src/test/resources/application-test.properties b/application/src/test/resources/application-test.properties index 856745797a..dd1cd2807c 100644 --- a/application/src/test/resources/application-test.properties +++ b/application/src/test/resources/application-test.properties @@ -1,19 +1,19 @@ -#transport.lwm2m.server.security.credentials.enabled=true -#transport.lwm2m.server.security.credentials.type=KEYSTORE -#transport.lwm2m.server.security.credentials.keystore.store_file=lwm2m/credentials/serverKeyStore.jks +transport.lwm2m.server.security.credentials.enabled=true +transport.lwm2m.server.security.credentials.type=KEYSTORE +transport.lwm2m.server.security.credentials.keystore.store_file=lwm2m/credentials/lwm2mserver.jks #transport.lwm2m.server.security.credentials.keystore.store_password=server #transport.lwm2m.server.security.credentials.keystore.key_alias=server #transport.lwm2m.server.security.credentials.keystore.key_password=server #transport.lwm2m.bootstrap.enabled=false -#transport.lwm2m.bootstrap.security.credentials.enabled=true -#transport.lwm2m.bootstrap.security.credentials.type=KEYSTORE -#transport.lwm2m.bootstrap.security.credentials.keystore.store_file=lwm2m/credentials/serverKeyStore.jks +transport.lwm2m.bootstrap.security.credentials.enabled=true +transport.lwm2m.bootstrap.security.credentials.type=KEYSTORE +transport.lwm2m.bootstrap.security.credentials.keystore.store_file=lwm2m/credentials/lwm2mserver.jks #transport.lwm2m.bootstrap.security.credentials.keystore.store_password=server #transport.lwm2m.bootstrap.security.credentials.keystore.key_alias=server #transport.lwm2m.bootstrap.security.credentials.keystore.key_password=server -#transport.lwm2m.security.trust-credentials.enabled=true -#transport.lwm2m.security.trust-credentials.type=KEYSTORE -#transport.lwm2m.security.trust-credentials.keystore.store_file=lwm2m/credentials/serverKeyStore.jks +transport.lwm2m.security.trust-credentials.enabled=true +transport.lwm2m.security.trust-credentials.type=KEYSTORE +transport.lwm2m.security.trust-credentials.keystore.store_file=lwm2m/credentials/lwm2mtruststorechain.jks #transport.lwm2m.security.trust-credentials.keystore.store_password=server edges.enabled=true diff --git a/application/src/test/resources/lwm2m/credentials/clientKeyStore.jks b/application/src/test/resources/lwm2m/credentials/clientKeyStore.jks deleted file mode 100644 index a6c9ae7faed05c48ec1218e23a42c3025ae852d2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4810 zcmeHKdpwls9)IVZ8?$1V+`8z^r7faoTyo7Kw~~aYeB{y?OqdySK}BLOgxw-rlBDFa z(TZxxaVZgsq;!#Oed=sfBHG$cn>de|>D0_gr#|2u2XxmVxY?(+F|Bkr}Xrw_K2ni-bWfz@s=6CB*GFPk0g!dufX^N|u{P18lHZ>cz+#X=+GmPnADKMv-z)w>@4JBhb z95ADsN(Mgk=M8{HB8%2Ragqh`I5OS=A$$_)db__`Kf&{*+vqJu#2QlLoO2`e+65gI z-$gJeM>`{JIjxqb>8Ge_!lDMXA86YTCXKbnuGg^n=F+^#I@_T_2Ve*0!Y$8LB&ZQ) z8fIy2sm?Jp&$_ZJ{#<&$?-nG%2PjVctc1f&65w0nNI+@oo;Gz)1bBFnJ_Nig0-VtK z$i+`9(rT0kqX*_j_w@*Fd+BSSSe=pDsrHJU_wP4v8bC4krd)?AT1kHN7-kN~$tF(BaxIDJ$Zha)0{i0J#> zovXR|mu?hxEl}b7x^;3Dt#7!s^lHm~yIG-8)Li9+;k)X(YBi1vEBDnnA>Ez!mT$3`Q4`n9W+^X!+{4QYO-S3QT6X}EDl&Ek_+k0!5+ zYpLBXV#z;PQ2-f{2g6>%3gV9>L+Jzg5U|p$u#jbig&&wj*8pl_N-2dfV^|EPy%Rfx z%VDtvV0v2U9!Lqw-7z#C_5+1q>%r{Zrb<@rA-bN~cDcLZR##D3?^iAxJ|7PzzqBvV z*yMKZMJcE6p^Zs|bIl@!0%}5XejyKV-=T%)i-5C@=hRPSl+Ag>Zzi$gZEP1x(*mCg z~3 zcUGX)I%_?*E_{3-ZJa+cD?i<-R#=W=gwmu2hr~i!y5^m%St(XSs=P&_3t+#~|>9H!PwQ{m$UWGO2k zOFy7X*9MqKKnj|XB2NyK>?oEc2$yW@k1ZvUBV)Y!3qJ=m9_lR#FWA#Q!FtBRKlEK@R$tJlo_n3b=vB)WQS;b+(7BZC~gcdc{5>TRX>Hkx@c6+Pa||hi){oR?wITG zsg(^leJ5-T^ZT;2SlX2DLO$JrktE@ie zV(3OUHZe6@YW@KxHl`D?&h_4cZl}NScqNqV38S?a8?N=awxM`xRG?1AJYVaiudOGe zpVML!quB?GvXVcrfBp2AXyKVh6d6+wN(pE9O zsn;F7ks3QNc{q&6s497^$v>WtV0af}+7+|4Ma=vf{-UO`3IbZ2(e6)Sd+W8OGKhnve&F{pX!=}TYj4pS)bm=q zHg>r3!Tw%uPva9JIo&dQB!vMD}d)xMoZa~hgv(AwfIo@}ty=^|8i z?n)v@R27DTkau$Rd!dmn;Z)#ji%U2;K!PP4ijz*-ZqJH)6dp%CKfEigBQr|i7~(ZE zSNc%JWgeC&#P;~!=vb~|O|B&94a$Fi-o|$Jf-!&9>IK^k`kO|fskCT;0hJLwRNGo% zCZ`=e)~M|P>N&lva`&9jLkU_gBIznXQxTUp{5Q4tLBl^m{$qix3_<2fg!XQil~?o@ z=_63e`7j(La*k_O?v!R8_|N5RCM#z%zkh4i=|vq~di=F*tao-9r*Ip2(xUaGp`_g-CJ{w$T`QZdmIwKlWK4#oVF!}tB|TGZ8X?OTd* z)Y^xIg29viIhbnLsgyi}V&_7UU{xeV6q_Rm6!3(60W(Bg+a1{O7R+)8WGRd{wH@(s zK${GhI`sLXb^*4`8j_Y-$d*hvP4oP|D)0ZkP5Rn*Om0!NWp4tgDGpG|L5z=m>HIMn zbp-;Pp~*g28Bx;KSa>euVO`z+-kczeW;a<=?+S=f1MQ;`&`d}d^5AfFY+egkWc!CP39AfyQ}=1VlHt()E_ z1M7$I_@8ZhJ%PtNiS+k2y&uujiVJc5Gsor4Iq{=&2E7sHiH7dql=}VmE~1%wAjS=W z-US%MD#xzn+KIdbuX4}O1#U6Evu~aWyJvEnGFWtBU;QUP+JGyKj%mKTb$|Y4AvgC` M^f%}Gep;{ccMhiIP5=M^ diff --git a/application/src/test/resources/lwm2m/credentials/lwm2mclient.jks b/application/src/test/resources/lwm2m/credentials/lwm2mclient.jks new file mode 100644 index 0000000000000000000000000000000000000000..490812c01ab483d3462a09cdd18bc405fa23abe5 GIT binary patch literal 17660 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 KeSQK10fwM$Q%GO{ literal 0 HcmV?d00001 diff --git a/application/src/test/resources/lwm2m/credentials/lwm2mserver.jks b/application/src/test/resources/lwm2m/credentials/lwm2mserver.jks new file mode 100644 index 0000000000000000000000000000000000000000..a1923e942f923b8d84a989055ec50c2441f79259 GIT binary patch literal 6432 zcmbW)bxa&gyC`rLcPYC#i?bA6q!@YtiC< zdU8&_@7&x!?#az0GnwakW|H}3lF2~BDQQpvXwYzqSxg-ENTtYY0ss~uFPx$eJ)ENZ zZ@3d0j@tWwq);2t!%=JhhAW}r04+?s|6YZU3qZ{adxD1DLv8+5k3j%6gbM$AiVx*L z2U6Q15bsYIme>Y!z041qhoW3B?om-8#HavzC_X0kf9;2ggMk92#l(3RsRXb_LkF;< z69<2=RD1lmyme__asL=|dW9Yq@8V&NxmtD6_bx(hAzw)a3 zp7kA$)lkLrx*}#0;d$tz;Q88;v;*<}HKYqOtSB3J)*M;Qp>Pm|t#@W|cK6P30)iRP zR^YqlEIxthOpGI4|7JpYBRPhdU+&hvR|>;h;a*;HEnp=vz~-)W@fmvB{k>&qY~0At z{SK^>5cJCDiB}3r{jbyF0C@?)_$VGI)+p{M-v8M{8UCFF@dD9b=-E42)AI@n@Cpb* zMR-MoUJ3m*)&Ku64T{6c=&LcU#ew1M-He7M?rkL@|2L+!nHsg_J831B#iV8hOVOYH znp*m8bmBLOdpVLyVn@&}K&K}29D_qEC1e7-m}@&@hNM_JOZ?Qu#jmFUGoqh|@nD_8 z(oIHTImsEb%S0!*x*BVfnpD-hLG{ANPA#a77W!J|tBOob@}PPhkS zWu*wYsV1J%*M`iQQrlnt??se9*hf90h&{W}Brf8F08 ztwPHF(dR2e&f4cB?ELMsGFsmMjkU}M<>89U2yduYEuf1JJvlTS^sbWWV?{&qp(mHc zDI0b15&xyb;I3^vw8`!8s$=j)4eZf^Wp`q+pRD)RgLXuB5lsw6iGl~AEbr(Jcl_<4 zd}q6QzPwLD*367)M*iC-n z*1|IEnqPMiO?tYnd{K5;-w>CBJuc(-)8J6BpeB(eS|G7{NPty~MeIWWxGlQ)CmqV0 zhJ*CUb?81(`jmcY*2;`8B1!*fAy9jj>F_me>yN8`zfq|SY-U+g79 zb!kRDvgAdqzYD?K7ZjJg0HPxA3Y=M01R!kZCdU7SB8Ky?Ysu$dG)M11v2&$E+rl}F z(C&GJb`}zaCB~S6E1V<-Z^g477sq}_(JV1G9|XHHfSclP z->B)Twr+8p@0A7OA-f}2+XuClyLY$UN6ZwSi@K*!GL9-u%0|iV-gPG63vIP({LaLQ zx_a*bx=&Zli_>oe@j=2*!u@9 zZ_YOk<)rL>YqphQZ_o1F~^#$0B`aZvKbf7M-^VZew)Y#YUaP!^g8mnsr+sh(6D6 zsrVk7Ikb`4PWp8+(@T0(kkCgB<)GdqLoJ7-?wfn}Y$Uf>>-$$kCSNqpge2Yu?y$T0 zdPa&&sP15J-V?4%uJ2nHZlGcBMv(ZOIDX+%eadeWHM}wQ*2HWlpWtKqBM^57Bo>|5 zzewy6y5FJKkxwUgXpF>qSO2JRCOmQ1>+>LE{O!21&J)8^Q~xN0C^VTO>E*fk2wk6R zdw%>JDN+Bqc`43USXgxsBONs7bwi0NxfI=obJ|DOB55@x^wrfu-?x=p*LyNPD4v(L zgR(d$zXs^!I*Zh z+yjvwq8*o(dZ9|HZgMOPXgT2=Pi*P1{sU6-CKj%^gCMD}rt@y1W1{e64XC-6*snjI zy%F1{%Sodr331_KGri3s_7QYLVj{b}+_WWh4z2XlbO{kuZ(gDl(|D}D(m}HE_mCCd z+JEI!I9BsVS4simJY1aRwaV*pXTZ?n+d zh$d^eFt1P0`ikG_YvouX{yd_y_j=V1kdoB4L?p<}4`OxGL?onK>=jH|M2|6kDq5haitcPf2k>UJId)PyCpuA~?*h^0uF(dDF<$gyFjP;lRo|k_t{LIU> z=-GnAS_i-v$F-@{z7}mllRY&4#-aJq7bEVIL2!H-l+c_4BgrWj`rM#+1Dj~zM$wtO z&4>c7wzM(Z3%6FZ^e(RF1KxqKfs4w5O3kOGTzb`Xe>KlDa8*1ZjFRrkJY#_t2@wEa z@k-A=MJK|vZX_x-Qunr+9u`YIo^I?i{c71g83qE7We1EiJ#YEzu>wD2%Vvn{y8U#M zqjc6}L{q8VA>B3c)NxT3o~IyB!rR zyikeF8-U~$QrOxA$c$q`BK>~C1AJO$DzGHM8(Z`m*0uuaEX5qPc?RhXY2cX|+SLfa z>m|cOWDca`6Lvbiyj#iTp6a-3Zj1XcD$>3b16P(84-a-)lv9NU*?1U#nL)NisY3V*T1U#k@AFJ3Y$NZ%~+r3cr)-T9o$7~?Lw=4B{4>R%CR zF}#*Yt$Yhrts*MNC32Hn%=NYj>C;|_@7#O$u^N4Ks4CajYC=Hz82%B2;}qQUi@?tZ zmPs4;!UKET2Di8A@-vrRDw)q$3N?BZksg+vEa z=B@D-&jAhhHj`(5%NB}|6l(1BVq)@z8Pjj5R2AZ{!D zb?YUHcuUTwktvf$NLicI*bZZxD{g73LBdm{W+mtOhJX#eGg_jg9JUVI=ovRr@~=^R zA@+p=J*A?SC%3l1)ovX4|Z0?3zctRZq0Bu0w)-n9Gq_#N*Oj>>fx;t3X2Uh6#Zs zqG3>SM5@sqfJRck;$7^nD`L0)_UzCskE|>!51lszX?8GqKzz1n8z5g^Z6IY?uUCRw zjl3?sh&RykFmS<_opKY2{wc_cSK6{IeLXMLu*5tjT0W7w^$*1`c|>QQkq?JO2usji zO3bTJxY8D_WG8#JmWjh%y+_k>Qi5ff3JXWz+*LLc_0|n%xMAxP-?BP^? z6GY#m%MZTtDtFoxf8_)i`lR==vyR_)kr>b+2hn|kZpJYMg_W_N#Saqmc1$?2(m?oCkxL!GwBs(RL#L=+7op}c9~L@hh8 zMJms7h{E3Y%9g@`*>e&w91>T9@VoDfHX_2oOkdacWyp@vMjd9SQOv6K4$0z}-GXUZ zgXYOR1cdcOFZQbL?pz9lC5%MHS1LB~z^zp-n?GnkeBEfQZ?fus|yq8QE2K5fk8bUjjLn$R9&;m=h|##$Fd1N zRRkM4*XMN*T@8%&eSyG09hgf4h3ZS0fePQT(-p)rR==Tt-vxbN1f`o*`7uk}sjQi_ z^HGNkb0zFaZNmRVbc#F0k>l46XRGgnctmg@$z+FuT7=c|(eo{tN=_b$Qq#uTP4p0D zk_(oWWROzJv;V15LOx>%Wz=hNti_iQL4DhfZ#rs{HwiJ#UnG!%T)FG%pc?cM&MfCjkwInO@obU>*7U zY>O8r--p1Q+*-78lO#^0vhs8TLk3Y@L1fuSPgLx!dT&^W`TAAw?7Aw7eD{U+nH zs36mAwQrd4%Npi^wXhO2pAZaxA6r|n>vpy#L`MgMlzfs%@G|!ln0ecTtze14ttT~X zXmtre2hq;OMhA>yul$Bf<-`yk{@`Ws{6ZYWCi#+>qImgCwIaqoOt8rKSb3P;kl(f0x+K?8B+o9}{GKj0n*{em}Zn>b0l39@pA94lgFVlSWtz8iV*xWbHqK%lIO7$(z(ipqaB&SX+)FS= zW-Q}&;|9)g*n2ga(XFS0b2l~Zv5`(>u^pR0{Q}Uf$=ieC zz6k!UKU$J*{;ilowc9=eGHMWlRGqvF))_BD!DaYPjBK|rjsos-ufpqPaGNn{L2d7d zrW}oW9O>$&Nt4aFs!@g;YQ~`d&yW@#HT7K6K(jL5Qht*^id~M5yX3Rz5ce7yckfzx ziw@$KDts>*kms^mn;rtl@jf`#(A_$aURoYmBwBU%2rd+sUv<->YaO{}9xv%dM68Z! z>-UNGf7S0JhswBadQFdrgQS=RgD^vi;nX|MA-K;2wOL0zocf}xWeM$Lq`?Yl4-RX7 zShtCaMGWJE-}*qGmHymy%<(Y=a=n2xe0mRwX|*qS_>uLkTmm`ZFe79xWxF226j9dL z888y7x?-uHd#}~r{|(+irFt3_i{c!*8( zn&uIHE$Ai^tFQfu%o&qO7vK0z>k@mp>moaBAIUPf= z3|hUML`S(GbP1UHxK`d)sHPIWs${F$ZW_+oie6gCdcwWmZ?({G&rDb(p&{b5%QFyY zX10(pzfiqAELf$gT99Oyq?<{RB_RazI4%Kuhq(e^2R zOuzH13m>|pKL8m`e~UGAYkbNtEQrhNObE0TLfLS`?*K;0`sFig7_PD4e)KA850UFI zkOcBWNuLep#vypHaDOttN>e=xH8BiSbkGGvQvkOK#}(-gVN zq=CE9abW^^47Q{~$s5<4R^8yL#>k`K9Q~Yn8h|N`ig_g`7O@L@bOS ze*He79`rbS=h^`sn=dho z#8%R;t~G-Mc3WEdR)9RYwdojhBZFwa0lWD&3B7R|#QqF3<00ubnvTrUG|!@6B!*}l zL1Ce`p|)vF2(s#Fq!#0ws7$@VyeD;GhGBDkx+$-c8*Mce73F(#;M00=36f2WzsQz8 zfHV7BhWCZq&B|SuN#eru_5)a9UG=$jNZqdl%d7IaX7WCUvryZkev)nji!WmuWDMzW zH9$>7 zj_K)q6}w&Ez#_av_>@EIvCdmYCC7LNEV#0xY!4uvZVlZ~9r^5|a~bxbtC^YmM~wSyU0v}Eu3|n-1qF8Etk&@dB&YqYd+WOyG@fFp zjE2ij<0f$c90d+$z+Fm24^Pq2F&;~ivKy=D3-qwh^o;p+^=EZBqmhUp-)-`SW1|=Z zSV#eXJ_IOoO1?qSY8}y@7^%>_0LFe=9^1mTuU_hsZj_%Z;S+tZP&xL9iGt&nNLU=h z85#hkM-2*}T4DjH0*9P$EK?xTz-Z-HR|yIiJd+UhyY$M26IKf=>Ky{WVWkT{>DS*!TABHlUi&eNt8Y?*M*XCNRtbX@R^1>Af5 zS?^6<3b{?nxfGPa!Q>0C1Ba$vZiPr5ejICBL!D}YJVOh;yP64`75(*{;jSNw3XGM5 z)%OvLyjQ0}G1YigCbzp0u`C-!Jh`PpM@ivgKa)<@3Zv2Q+$I)8OEaO ztvZ0U;V^|UnjhpVA%QBgST$^{JlI}!>ucKr5+?UF1z5>l<~JuO^(+xwvFcxz4BFF~ z2cv&M^AZ$GG>Glu&p19nT{t`M2&4$%IVIkB|0K)9Uh_h=>XQX;N%ucX1YwM0*RSG^ zqM)!Ngv{3EY-fpeY#`UoEP}e99q8l7(Iz~jO%8}%9AviHJ)H=Lz6{}6Y$V)wq`k#l zkYRfod%selw4;BK&ZOsU*dqixaDhvpK)T+(C0Kwrn4*7uwT&T5N(Usnym%~$=%{-x zkHvBfvE1R<4Y-j!V+1y2<5WoYx^Qq?b3s86Z)CsV{{e;PFTC#*U7~j zBJb!O9yVT&Nyw5X>m?rV!I6pxbnvzLdM*WS`rE>WS30_Q2WoIdcB-Hox>g%xP)h&A zCvLHtAPC}7%&o}?w$%2W-PJM72}P6WP|>Q=CLk5Q{g0UOitC_g=WKJv2+%-bo!8S2 z|D65@#J`@e>i6_|xt>mrnyh_A4}mD+@87ZohpDePM81xCE=QTS=9|dyI|O98ICmeT z<5Il5KupYd)W`FNu}L#$nn5hMXV>v?W~L{KHL#+3u33UxCk1j1q55rvurFB~8pj@IM1T zpb}69C=MnD2O%0N6$T1Ako_Minvgn>E!CBj4$2o61L-UqLUbU_YqAcVP7b)fKTY^v RQN&L>DX}M1G(cqDe*m!Z4OLQW;Rtal~*bu^s5~4&%aFwunh`xyxPXy6}Rf8Zx^o`!* zMu}dc1d*uGqAm8Bxo_^B_v^hMXU=!NQ)bS5b7l^L0L=vf!3YA>iVh-8yh%J@2GRqw z2~d3+0`%rNjYSZE(f^eK2GJ0J{^zvMxp~k*|I@|92n1z^pCQ622*cqgbrfFM@6SA5y9$$D)cr_KA9)2YZNP{4B3Ysazzeq#4S)?!Y`q7^z>Ry<`hqSTmjWxe^>)L2OpOE{9wkMLgQT|3`(wQSd~MWRp0sDSEJrEc`Ky0D(ukCO6L3 zz(AvOw#5|XbqaYh!RPp}yRsub;p1ykr1+|tGR9IOGRrJNCWy?yv-Tx}=-qaP?+YI2 z)#+H!u#ed&i_-}+cfgo|0K!LjG%xyhF;g%cGyruRC|ZW(h=;^xC(#lwNVbm}_~A3G zi^4b(IzyqkK60xVs(#Me%}L@yzI<_RSU~Cf&yh#1q{H{?*LGg*>n!igaqVRHZH=6g<7=f*R zv6S&L%wV5RJ`;7j?d%={OOd$pbF9RW?+}}7gn4x}8)}#>dF7L$vFrKmRRGDF;B}8d z3;?0!-9LPp!BP<|yQSR#t#x`8xyjw%vi;#>_0JzD^rYNW`Fh4{>qyV{_xucbizaK^ z9m(;(E4gOtX*8$ley6+4Y_ga(voXho^UT6`sk4KA3u-5yDp|fiEr8}?8pPb}UwQW{ zG^EX47T3cjwfond9ltCx7e6s@O&6+e32Uv9$%}rlV!^sCxj3)`HXYg;No5F)C9v}lh?%XtZ(*@=-%iA)p+Y(i9Br@LM=i-dI9bDUQ;ljpLYpF8z7#SG+RxL#` z$Bj?wr18F9Y*xI0d}ChhYirAwv`XvYGZuF1~LlQ zu)iBywd~772XeiAS$yKAOh%g9E@3V^BvM3&sPIq`gMipY6e)2cjO72ou?tWi_eExcEIRr{ef(A(Z$r*tTl*>N zSE$c9CH57`gJq%@Y(ANz1fpwkPNFhzPz3sk^jh&FVKn}9MB04lxH3g&klltnpX#~% zhb?gTc((pH&T#zdE-iF>0`flN#o2|oS)LnvTspqr)0fpNlXgJc2T|?OZ z=rJz{aL^dZB#xP1SKM}Np`>ZVezmfVD<}s>=y%~Ov06`}(3{KgBC%ojy6mGG3nU&$ zqYJ$kH1LG+?#040wFGN@tJh|%p}n(SQ`hevTTq`Y2gu+G#%Tq?YgG(b{it`l{Dw*l zvi@^6Y?sWUtW_syViMk{_C(+sJN6Lr*IByd+)v`PlrizIh+%a07`8B;YQceeS%q)w zsk$>Pp~X{Qqz=WYcldpl@}^(gM9e-OR(vQ`m~Rd0{(VbpBeRT%(@Oje3(mMa{8yB$(}X$ zh{(L8f)Otlgkgc0S_H~8Eoc#pT67#4;*;v{WwVjZ_1q~BKs%2O!?qc-_QO(<9b-@? z0?_y+yoX8CC?~2=-Yvga50FUlFYiKJidLIU-SIG~(5sguaJFlE4N^IJ+`k2mt_b=S z<;&~{D?Xm_W^kW=yaZIG`kNv%ps>QC{%dFZ-WK%I*`6nwr1|bOy^X;mGhqia8@o07 zF=9hP9La-RqU-T>FxI-nMROPt(*wdp+~6>@lV8X_Q%LA6&^X3%i7lE1v@_ILo>e>} z{G6*?g9$v6PwXhFr(!cJ^K@x zH@hOt^cDA~n};9N&8M?2q)r(e$l4x?rmh@El19M27sqnYWNGPlp7>Z=~l z^Bp!Gb*g_uzRe{TD{a(rO{FiwfH^NF?>lipBS@YJ3iOtCe!jx*{O;HYEaOGYcsBJk zR9lUutQ9JM^y%Z4M5Od&RGC?Q5k9i4-^dLC|t^ik}z z*^6ov2kUzE2Hto4Q`gh?e>tBXMTpEpS~b$3LflOUVc-n zJUmxjl;Ii9f2KKWuhfRk!|OC#aqwOhpP#)93EmQ`roF9wA|^U|J%8~djQ9fR@Nmz6 z$bINQlV1Uc;>K+ULA*~EJ_G9`CD5*P-_&JC}WtdF)KU&1GIq@q1K zhd4|?!w#5^kOz~`xGKY&-mIPkBO|phML|c`uW9yFNZ(O{u}|%4TbTZkGb{zn(9RoV zvJ8EVvAb_qX$ggBmV5&IM+eidkJq|PX1U*J^7=F z#eC_@BRJe7IWr9Y-R;?w*bO8gccyU?boT@iPLFtFj=i&*1KUi8{adkDv3`fKE;qPjjA z4+TFF7H_^Kf>)31UGn%Lm3fN3@UPuHA#Z?|4z_5d`6-vw)Fw{?K6hB$HcGDhX?BiD z!s$0^mBga=MNOqr?3?SnPp2i)O60WsOU7%v7vh|M!Pmt~2`vSn-hs01;bBfJO@5a; zqtP3^;khCc{@m{+Cip^r8rwqzWkoG})ICwMhQ~_1=J*am9U+W>(9z1UfI+;pfb$nv y*%f9#Wra&pUMfg^)SGG_*Q`dv>E$LWs-LtH9M}nF;19F-jF|W;4*~;;+5ZNgC0;TB literal 0 HcmV?d00001 diff --git a/application/src/test/resources/lwm2m/credentials/serverKeyStore.jks b/application/src/test/resources/lwm2m/credentials/serverKeyStore.jks deleted file mode 100644 index fc541a3b18111312545da774be682ddb893d72c2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3806 zcmezO_TO6u1_mY|W&_iFnRz9tMY*XdnTaK-$%zaMjFr!(OnJ+|8lh)uUJkHs&Ff*|-7)Tn58;G(ohq5pW^ZBF}XC&q+6y@ia zC^$PBD2VeK85)=wm>U`cNz*6^ej`JmfT^JgRKP&dP~JcmqFD^88QDo7Lm~}?+1SDM zF)>0dW@cn(c4A;T@VS|9lH1dLZ_jKt-`U6Fea+QH`BYo9IL#mhY`ZKUix`Ut^Mt&kdKuN{mQQtB zUi5*fk(H(TpaCDyakBi3jQ?4HzGY-H5CZXqL3|DaHXy~s$Y3B35>sZ8Fc51H(VZk$ zxqXJN`J@Z|H3z%zhgCAvhN1=-vnPXr8zz+tX`Lo>Xmwt;`4ciQ-RUJ$iP{g zT2z)=1Wd({+%)YhP`KWp7MPN_wAmP07`2!L85vm_Sh9C4soL}0b=E$Mg=v$soQ%&E ze-UkGh(7!8*z23GK6Ux3JW7}-CdGRxUb*$s#*H}>yS#3hWc=9SwbZeCr>?;9ccr&_ zYgxQJ(!V{(HT$V`QR>5%H$^P@+qt&id0fzvyI|3@t`COBEccC7VLd&rY7L^CxjgF5%vL_s_{?G7bKy zS)IAVKm?R*1X*|uxSKe0@{<#DGV+T{3^>7wiG|4kHQ!?`JcL1NRatlpxY#&=anH)i z&di7!(9G@(1};nrn!6{v?BTePd(kMjNR;uU^|$ugrFS$Q9umsi${YQ5&H^TdsFeM= z90yik-kWEXGgr=O`{J&$y|KM7LUoxI7hXH8CBN4cMod%AvKSoW?FOfOlv-vGHtp+D%Gs zJc(4Dp!3?mQ2w@XY2U+&4qJBf-UtR0M-HvV>(w&A3ge>joNbbJ!JgUYuAZ6QqPAQv zQ)->$ngpAsU+XTp#rL^@s{=MjR72u+>TO(gfe)|D(-6Qb2h|ya67LSJ{;U~ z=j!t7JN^Y!?~YK;neEaj@$>WA(44J%&My4R*S)D{)LMO8UGTuGsl5-*N3oSb>J z)Lvu@%h8K!V%cK1e3=wtzAp%{I$kwDp0DD0XHV~}Lt>(a&rX)h7S2h_+IP<}98nI8 zw&b9#Z#uT*m>7g0Sr@a2o|KM=nlfPCDFR|qSR>n^QvogiV9x@mC9xTLNo*EnfL$LA zvreFaKLuHbh@1iK`FxVtS7y-vtNrrlO-x^(POD+NzGweDCVTEHScmMzZ From 4450df292f73ce0b58ac52b95bd7a1366f47cbea Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Sat, 8 Jan 2022 18:24:10 +0200 Subject: [PATCH 062/798] lwm2m tests aad logs with NoSec, PSK, X509-trust. RPK, X509_NoTrust - ignore --- .../server/transport/lwm2m/AbstractLwM2MIntegrationTest.java | 3 +++ .../lwm2m/security/AbstractSecurityLwM2MIntegrationTest.java | 1 + .../transport/lwm2m/security/sql/PskLwm2mIntegrationTest.java | 4 ++-- application/src/test/resources/application-test.properties | 2 +- application/src/test/resources/logback.xml | 1 + 5 files changed, 8 insertions(+), 3 deletions(-) diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java index 5d4c32641d..13e0858199 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java @@ -16,6 +16,7 @@ package org.thingsboard.server.transport.lwm2m; import com.fasterxml.jackson.core.type.TypeReference; +import lombok.extern.slf4j.Slf4j; import org.apache.commons.io.IOUtils; import org.eclipse.californium.core.network.config.NetworkConfig; import org.eclipse.leshan.client.object.Security; @@ -66,6 +67,7 @@ import java.util.concurrent.ScheduledExecutorService; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +@Slf4j @DaoSqlTest public abstract class AbstractLwM2MIntegrationTest extends AbstractWebsocketTest { @@ -199,6 +201,7 @@ public abstract class AbstractLwM2MIntegrationTest extends AbstractWebsocketTest createNewClient(security, coapConfig, false); String msg = wsClient.waitForUpdate(); + log.info("msg5555: [{}]", msg); EntityDataUpdate update = mapper.readValue(msg, EntityDataUpdate.class); Assert.assertEquals(1, update.getCmdId()); List eData = update.getUpdate(); diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/AbstractSecurityLwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/AbstractSecurityLwM2MIntegrationTest.java index 0c983f9dcf..d537062def 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/AbstractSecurityLwM2MIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/AbstractSecurityLwM2MIntegrationTest.java @@ -68,6 +68,7 @@ public abstract class AbstractSecurityLwM2MIntegrationTest extends AbstractLwM2M // Client protected LwM2MTestClient client; protected static final String CLIENT_ENDPOINT_NO_SEC = "deviceNoSec"; + protected static final String CLIENT_ENDPOINT_PSK = "devicePSK"; protected static final String CLIENT_ENDPOINT_RPK = "deviceRPK"; protected static final String CLIENT_ENDPOINT_NO_TRUST = "deviceAEndpoint"; protected static final String CLIENT_ENDPOINT_TRUST = "LwX50900000000"; diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/PskLwm2mIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/PskLwm2mIntegrationTest.java index 5850e52015..d7296fd47c 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/PskLwm2mIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/PskLwm2mIntegrationTest.java @@ -34,13 +34,13 @@ public class PskLwm2mIntegrationTest extends AbstractSecurityLwM2MIntegrationTes @Test public void testConnectWithPSKAndObserveTelemetry() throws Exception { PSKClientCredential clientCredentials = new PSKClientCredential(); - clientCredentials.setEndpoint(CLIENT_ENDPOINT_NO_TRUST); + clientCredentials.setEndpoint(CLIENT_ENDPOINT_PSK); clientCredentials.setKey(pskKey); clientCredentials.setIdentity(pskIdentity); Security security = psk(SECURE_URI, SHORT_SERVER_ID, pskIdentity.getBytes(StandardCharsets.UTF_8), Hex.decodeHex(pskKey.toCharArray())); - super.basicTestConnectionObserveTelemetry(security, clientCredentials, SECURE_COAP_CONFIG, CLIENT_ENDPOINT_NO_TRUST); + super.basicTestConnectionObserveTelemetry(security, clientCredentials, SECURE_COAP_CONFIG, CLIENT_ENDPOINT_PSK); } } diff --git a/application/src/test/resources/application-test.properties b/application/src/test/resources/application-test.properties index dd1cd2807c..7f7cb6e8b9 100644 --- a/application/src/test/resources/application-test.properties +++ b/application/src/test/resources/application-test.properties @@ -4,7 +4,7 @@ transport.lwm2m.server.security.credentials.keystore.store_file=lwm2m/credential #transport.lwm2m.server.security.credentials.keystore.store_password=server #transport.lwm2m.server.security.credentials.keystore.key_alias=server #transport.lwm2m.server.security.credentials.keystore.key_password=server -#transport.lwm2m.bootstrap.enabled=false +transport.lwm2m.bootstrap.enabled=false transport.lwm2m.bootstrap.security.credentials.enabled=true transport.lwm2m.bootstrap.security.credentials.type=KEYSTORE transport.lwm2m.bootstrap.security.credentials.keystore.store_file=lwm2m/credentials/lwm2mserver.jks diff --git a/application/src/test/resources/logback.xml b/application/src/test/resources/logback.xml index d3301bf660..91ca9c2b6c 100644 --- a/application/src/test/resources/logback.xml +++ b/application/src/test/resources/logback.xml @@ -10,6 +10,7 @@ + From fe9b61586f3f907289d59a9ecca839965f26c07c Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Sat, 8 Jan 2022 20:11:39 +0200 Subject: [PATCH 063/798] lwm2m tests ignore test del 3/0/9 --- .../sql/RpcLwm2mIntegrationDeleteTest.java | 24 +++++++++---------- application/src/test/resources/logback.xml | 2 +- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationDeleteTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationDeleteTest.java index 6f24d7176c..90a9dd7a62 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationDeleteTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationDeleteTest.java @@ -74,18 +74,18 @@ public class RpcLwm2mIntegrationDeleteTest extends AbstractRpcLwM2MIntegrationTe } - /** - * delete resource - * Delete {"id":"/3/0/9"} - * {"result":"METHOD_NOT_ALLOWED"} - */ - @Test - public void testDeleteResourceByIdKey_Result_METHOD_NOT_ALLOWED() throws Exception { - String expectedPath = objectIdVer_3 + "/" + objectInstanceId_0 + resourceId_9; - String actualResult = sendRPCDeleteById(expectedPath); - ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); - assertEquals(ResponseCode.METHOD_NOT_ALLOWED.getName(), rpcActualResult.get("result").asText()); - } +// /** +// * delete resource +// * Delete {"id":"/3/0/9"} +// * {"result":"METHOD_NOT_ALLOWED"} +// */ +// @Test +// public void testDeleteResourceByIdKey_Result_METHOD_NOT_ALLOWED() throws Exception { +// String expectedPath = objectIdVer_3 + "/" + objectInstanceId_0 + resourceId_9; +// String actualResult = sendRPCDeleteById(expectedPath); +// ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); +// assertEquals(ResponseCode.METHOD_NOT_ALLOWED.getName(), rpcActualResult.get("result").asText()); +// } private String sendRPCDeleteById(String path) throws Exception { diff --git a/application/src/test/resources/logback.xml b/application/src/test/resources/logback.xml index 91ca9c2b6c..4cca303e8f 100644 --- a/application/src/test/resources/logback.xml +++ b/application/src/test/resources/logback.xml @@ -10,7 +10,7 @@ - + From 98331c79a0ccb70a4c8459e8bdc56eb92e50c07a Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Sat, 8 Jan 2022 23:48:24 +0200 Subject: [PATCH 064/798] lwm2m tests add RPK --- .../transport/lwm2m/Lwm2mTestHelper.java | 1 + .../sql/RpcLwm2mIntegrationDeleteTest.java | 25 ++++++++++--------- .../AbstractSecurityLwM2MIntegrationTest.java | 3 --- .../sql/NoSecLwM2MIntegrationTest.java | 4 +-- .../security/sql/PskLwm2mIntegrationTest.java | 4 +-- .../security/sql/RpkLwM2MIntegrationTest.java | 7 +++--- application/src/test/resources/logback.xml | 1 - 7 files changed, 21 insertions(+), 24 deletions(-) diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/Lwm2mTestHelper.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/Lwm2mTestHelper.java index 232332e554..0ea700d46c 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/Lwm2mTestHelper.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/Lwm2mTestHelper.java @@ -55,6 +55,7 @@ public class Lwm2mTestHelper { public static final int resourceId_2 = 2; public static final int resourceId_3 = 3; public static final int resourceId_4 = 4; + public static final int resourceId_7 = 7; public static final int resourceId_8 = 8; public static final int resourceId_9 = 9; public static final int resourceId_11 = 11; diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationDeleteTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationDeleteTest.java index 90a9dd7a62..ebc0f6d783 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationDeleteTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationDeleteTest.java @@ -26,6 +26,7 @@ import static org.junit.Assert.assertTrue; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.objectInstanceId_0; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.objectInstanceId_12; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceId_7; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceId_9; @@ -74,18 +75,18 @@ public class RpcLwm2mIntegrationDeleteTest extends AbstractRpcLwM2MIntegrationTe } -// /** -// * delete resource -// * Delete {"id":"/3/0/9"} -// * {"result":"METHOD_NOT_ALLOWED"} -// */ -// @Test -// public void testDeleteResourceByIdKey_Result_METHOD_NOT_ALLOWED() throws Exception { -// String expectedPath = objectIdVer_3 + "/" + objectInstanceId_0 + resourceId_9; -// String actualResult = sendRPCDeleteById(expectedPath); -// ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); -// assertEquals(ResponseCode.METHOD_NOT_ALLOWED.getName(), rpcActualResult.get("result").asText()); -// } + /** + * delete resource + * Delete {"id":"/3/0/7"} + * {"result":"METHOD_NOT_ALLOWED"} + */ + @Test + public void testDeleteResourceByIdKey_Result_METHOD_NOT_ALLOWED() throws Exception { + String expectedPath = objectIdVer_3 + "/" + objectInstanceId_0 + resourceId_7; + String actualResult = sendRPCDeleteById(expectedPath); + ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); + assertEquals(ResponseCode.METHOD_NOT_ALLOWED.getName(), rpcActualResult.get("result").asText()); + } private String sendRPCDeleteById(String path) throws Exception { diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/AbstractSecurityLwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/AbstractSecurityLwM2MIntegrationTest.java index d537062def..b0fadf1d23 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/AbstractSecurityLwM2MIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/AbstractSecurityLwM2MIntegrationTest.java @@ -67,9 +67,6 @@ public abstract class AbstractSecurityLwM2MIntegrationTest extends AbstractLwM2M // Client protected LwM2MTestClient client; - protected static final String CLIENT_ENDPOINT_NO_SEC = "deviceNoSec"; - protected static final String CLIENT_ENDPOINT_PSK = "devicePSK"; - protected static final String CLIENT_ENDPOINT_RPK = "deviceRPK"; protected static final String CLIENT_ENDPOINT_NO_TRUST = "deviceAEndpoint"; protected static final String CLIENT_ENDPOINT_TRUST = "LwX50900000000"; protected static final String CLIENT_JKS_FOR_TEST = "lwm2mclient"; diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/NoSecLwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/NoSecLwM2MIntegrationTest.java index 8331b99fff..4daac68f9a 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/NoSecLwM2MIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/NoSecLwM2MIntegrationTest.java @@ -29,8 +29,8 @@ public class NoSecLwM2MIntegrationTest extends AbstractSecurityLwM2MIntegrationT @Test public void testConnectAndObserveTelemetry() throws Exception { - NoSecClientCredential clientCredentials = createNoSecClientCredentials(CLIENT_ENDPOINT_NO_SEC); - super.basicTestConnectionObserveTelemetry(SECURITY, clientCredentials, COAP_CONFIG, CLIENT_ENDPOINT_NO_SEC); + NoSecClientCredential clientCredentials = createNoSecClientCredentials(CLIENT_ENDPOINT_TRUST); + super.basicTestConnectionObserveTelemetry(SECURITY, clientCredentials, COAP_CONFIG, CLIENT_ENDPOINT_TRUST); } } diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/PskLwm2mIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/PskLwm2mIntegrationTest.java index d7296fd47c..11b6f3f6e7 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/PskLwm2mIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/PskLwm2mIntegrationTest.java @@ -34,13 +34,13 @@ public class PskLwm2mIntegrationTest extends AbstractSecurityLwM2MIntegrationTes @Test public void testConnectWithPSKAndObserveTelemetry() throws Exception { PSKClientCredential clientCredentials = new PSKClientCredential(); - clientCredentials.setEndpoint(CLIENT_ENDPOINT_PSK); + clientCredentials.setEndpoint(CLIENT_ENDPOINT_TRUST); clientCredentials.setKey(pskKey); clientCredentials.setIdentity(pskIdentity); Security security = psk(SECURE_URI, SHORT_SERVER_ID, pskIdentity.getBytes(StandardCharsets.UTF_8), Hex.decodeHex(pskKey.toCharArray())); - super.basicTestConnectionObserveTelemetry(security, clientCredentials, SECURE_COAP_CONFIG, CLIENT_ENDPOINT_PSK); + super.basicTestConnectionObserveTelemetry(security, clientCredentials, SECURE_COAP_CONFIG, CLIENT_ENDPOINT_TRUST); } } diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/RpkLwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/RpkLwM2MIntegrationTest.java index e6c26a05d0..1f73a2a739 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/RpkLwM2MIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/RpkLwM2MIntegrationTest.java @@ -30,17 +30,16 @@ import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.SHORT_SERVE public class RpkLwM2MIntegrationTest extends AbstractSecurityLwM2MIntegrationTest { - @Ignore @Test public void testConnectWithRPKAndObserveTelemetry() throws Exception { RPKClientCredential rpkClientCredentials = new RPKClientCredential(); - rpkClientCredentials.setEndpoint(CLIENT_ENDPOINT_RPK); - rpkClientCredentials.setKey(new String(Base64.encodeBase64(clientPrivateKeyFromCert.getEncoded()))); + rpkClientCredentials.setEndpoint(CLIENT_ENDPOINT_TRUST); + rpkClientCredentials.setKey(new String(Base64.encodeBase64(clientPublicKeyFromCert.getEncoded()))); Security security = rpk(SECURE_URI, SHORT_SERVER_ID, clientPublicKeyFromCert.getEncoded(), clientPrivateKeyFromCert.getEncoded(), serverPublicKeyFromCert.getEncoded()); - super.basicTestConnectionObserveTelemetry(security, rpkClientCredentials, SECURE_COAP_CONFIG, CLIENT_ENDPOINT_RPK); + super.basicTestConnectionObserveTelemetry(security, rpkClientCredentials, SECURE_COAP_CONFIG, CLIENT_ENDPOINT_TRUST); } } diff --git a/application/src/test/resources/logback.xml b/application/src/test/resources/logback.xml index 4cca303e8f..d3301bf660 100644 --- a/application/src/test/resources/logback.xml +++ b/application/src/test/resources/logback.xml @@ -10,7 +10,6 @@ - From c981ff4c55016e4a8c153ed4121236c381dfa26c Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Sun, 9 Jan 2022 15:22:43 +0200 Subject: [PATCH 065/798] lwm2m tests add no trust --- .../controller/TbTestWebSocketClient.java | 2 +- .../lwm2m/AbstractLwM2MIntegrationTest.java | 16 +-- .../ota/AbstractOtaLwM2MIntegrationTest.java | 3 + .../ota/sql/OtaLwM2MIntegrationTest.java | 24 ++-- .../rpc/AbstractRpcLwM2MIntegrationTest.java | 6 +- .../AbstractSecurityLwM2MIntegrationTest.java | 38 +++--- .../sql/NoSecLwM2MIntegrationTest.java | 4 +- .../security/sql/PskLwm2mIntegrationTest.java | 4 +- .../security/sql/RpkLwM2MIntegrationTest.java | 10 +- .../sql/X509_NoTrustLwM2MIntegrationTest.java | 21 +-- .../sql/X509_TrustLwM2MIntegrationTest.java | 8 +- application/src/test/resources/logback.xml | 1 + .../lwm2m/credentials/lwm2mclient.jks | Bin 17660 -> 20462 bytes .../lwm2m/credentials/lwm2mserver.jks | Bin 6432 -> 6448 bytes .../credentials/lwm2mtruststorechain.jks | Bin 2982 -> 2982 bytes ... => lwM2M_cfssl_chain_clients_for_test.sh} | 120 +++++++++++++++++- ...l.sh => lwm2m_cfssl_chain_all_for_test.sh} | 10 +- 17 files changed, 194 insertions(+), 73 deletions(-) rename application/src/test/resources/lwm2m/credentials/shell/{lwM2M_cfssl_chain_trusts_and_clients_for_test.sh => lwM2M_cfssl_chain_clients_for_test.sh} (63%) rename application/src/test/resources/lwm2m/credentials/shell/{lwm2m_cfssl_chain_for_test_All.sh => lwm2m_cfssl_chain_all_for_test.sh} (78%) diff --git a/application/src/test/java/org/thingsboard/server/controller/TbTestWebSocketClient.java b/application/src/test/java/org/thingsboard/server/controller/TbTestWebSocketClient.java index ff6b004405..2bb68737ac 100644 --- a/application/src/test/java/org/thingsboard/server/controller/TbTestWebSocketClient.java +++ b/application/src/test/java/org/thingsboard/server/controller/TbTestWebSocketClient.java @@ -74,7 +74,7 @@ public class TbTestWebSocketClient extends WebSocketClient { } public String waitForUpdate() { - return waitForUpdate(TimeUnit.SECONDS.toMillis(3)); + return waitForUpdate(TimeUnit.SECONDS.toMillis(8)); } public String waitForUpdate(long ms) { diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java index 13e0858199..0a95dfdbc9 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java @@ -135,7 +135,7 @@ public abstract class AbstractLwM2MIntegrationTest extends AbstractWebsocketTest protected LwM2MTestClient client; private final LwM2MBootstrapClientCredentials defaultBootstrapCredentials; private String[] resources; - protected String endpoint; +// protected String endpoint; public AbstractLwM2MIntegrationTest() { this.defaultBootstrapCredentials = new LwM2MBootstrapClientCredentials(); @@ -197,8 +197,8 @@ public abstract class AbstractLwM2MIntegrationTest extends AbstractWebsocketTest wsClient.waitForReply(); wsClient.registerWaitForUpdate(); - this.endpoint = endpoint; - createNewClient(security, coapConfig, false); +// this.endpoint = endpoint; + createNewClient(security, coapConfig, false, endpoint); String msg = wsClient.waitForUpdate(); log.info("msg5555: [{}]", msg); @@ -264,13 +264,13 @@ public abstract class AbstractLwM2MIntegrationTest extends AbstractWebsocketTest this.resources = resources; } - public void setEndpoint(String endpoint) { - this.endpoint = endpoint; - } +// public void setEndpoint(String endpoint) { +// this.endpoint = endpoint; +// } - public void createNewClient(Security security, NetworkConfig coapConfig, boolean isRpc) throws Exception { + public void createNewClient(Security security, NetworkConfig coapConfig, boolean isRpc, String endpoint) throws Exception { clientDestroy(); - client = new LwM2MTestClient(this.executor, this.endpoint); + client = new LwM2MTestClient(this.executor, endpoint); int clientPort = SocketUtils.findAvailableTcpPort(); client.init(security, coapConfig, clientPort, isRpc); } diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/ota/AbstractOtaLwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/ota/AbstractOtaLwM2MIntegrationTest.java index e78d74bdcd..6cf35aeb94 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/ota/AbstractOtaLwM2MIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/ota/AbstractOtaLwM2MIntegrationTest.java @@ -30,6 +30,9 @@ import static org.thingsboard.server.common.data.ota.OtaPackageType.SOFTWARE; public abstract class AbstractOtaLwM2MIntegrationTest extends AbstractLwM2MIntegrationTest { private final String[] resources = new String[]{"3.xml", "5.xml", "9.xml"}; + protected static final String CLIENT_ENDPOINT_WITHOUT_FW_INFO = "WithoutFirmwareInfoDevice"; + protected static final String CLIENT_ENDPOINT_OTA5 = "Ota5_Device"; + protected static final String CLIENT_ENDPOINT_OTA9 = "Ota9_Device"; public AbstractOtaLwM2MIntegrationTest() { setResources(this.resources); diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/ota/sql/OtaLwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/ota/sql/OtaLwM2MIntegrationTest.java index 903750663a..95a0a774a2 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/ota/sql/OtaLwM2MIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/ota/sql/OtaLwM2MIntegrationTest.java @@ -138,12 +138,12 @@ public class OtaLwM2MIntegrationTest extends AbstractOtaLwM2MIntegrationTest { @Test public void testFirmwareUpdateWithClientWithoutFirmwareOtaInfoFromProfile() throws Exception { - String endpoint = "WithoutFirmwareInfoDevice"; - setEndpoint(endpoint); +// String endpoint = "WithoutFirmwareInfoDevice"; +// setEndpoint(endpoint); createDeviceProfile(transportConfiguration); - NoSecClientCredential credentials = createNoSecClientCredentials(endpoint); + NoSecClientCredential credentials = createNoSecClientCredentials(this.CLIENT_ENDPOINT_WITHOUT_FW_INFO); final Device device = createDevice(credentials); - createNewClient(SECURITY, COAP_CONFIG, false); + createNewClient(SECURITY, COAP_CONFIG, false, this.CLIENT_ENDPOINT_WITHOUT_FW_INFO); Thread.sleep(1000); @@ -165,12 +165,12 @@ public class OtaLwM2MIntegrationTest extends AbstractOtaLwM2MIntegrationTest { @Test public void testFirmwareUpdateByObject5() throws Exception { - String endpoint = "Ota5_Device"; - setEndpoint(endpoint); +// String endpoint = "Ota5_Device"; +// setEndpoint(endpoint); createDeviceProfile(OTA_TRANSPORT_CONFIGURATION); - NoSecClientCredential credentials = createNoSecClientCredentials(endpoint); + NoSecClientCredential credentials = createNoSecClientCredentials(this.CLIENT_ENDPOINT_OTA5); final Device device = createDevice(credentials); - createNewClient(SECURITY, COAP_CONFIG, false); + createNewClient(SECURITY, COAP_CONFIG, false, this.CLIENT_ENDPOINT_OTA5); Thread.sleep(1000); @@ -204,12 +204,12 @@ public class OtaLwM2MIntegrationTest extends AbstractOtaLwM2MIntegrationTest { * */ @Test public void testSoftwareUpdateByObject9() throws Exception { - String endpoint = "Ota9_Device"; - setEndpoint(endpoint); +// String endpoint = "Ota9_Device"; +// setEndpoint(endpoint); createDeviceProfile(OTA_TRANSPORT_CONFIGURATION); - NoSecClientCredential credentials = createNoSecClientCredentials(endpoint); + NoSecClientCredential credentials = createNoSecClientCredentials(this.CLIENT_ENDPOINT_OTA9); final Device device = createDevice(credentials); - createNewClient(SECURITY, COAP_CONFIG, false); + createNewClient(SECURITY, COAP_CONFIG, false, this.CLIENT_ENDPOINT_OTA9); Thread.sleep(1000); diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/AbstractRpcLwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/AbstractRpcLwM2MIntegrationTest.java index 95878b74d1..2310fe1659 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/AbstractRpcLwM2MIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/AbstractRpcLwM2MIntegrationTest.java @@ -73,6 +73,7 @@ public abstract class AbstractRpcLwM2MIntegrationTest extends AbstractLwM2MInteg protected String objectIdVer_50 = "/50"; protected String objectIdVer_3303; protected static AtomicInteger endpointSequence = new AtomicInteger(); + protected static String endpointRpcPref = "deviceEndpointRpc"; public AbstractRpcLwM2MIntegrationTest(){ setResources(resources); @@ -80,9 +81,10 @@ public abstract class AbstractRpcLwM2MIntegrationTest extends AbstractLwM2MInteg @Before public void beforeTest() throws Exception { - setEndpoint("deviceEndpointRpc" + endpointSequence.incrementAndGet()); + String endpoint = endpointRpcPref + endpointSequence.incrementAndGet(); +// setEndpoint(endpoint); init(); - createNewClient (SECURITY, COAP_CONFIG, true); + createNewClient (SECURITY, COAP_CONFIG, true, endpoint); expectedObjects = ConcurrentHashMap.newKeySet(); expectedObjectIdVers = ConcurrentHashMap.newKeySet(); diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/AbstractSecurityLwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/AbstractSecurityLwM2MIntegrationTest.java index b0fadf1d23..c10eb46620 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/AbstractSecurityLwM2MIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/AbstractSecurityLwM2MIntegrationTest.java @@ -27,7 +27,6 @@ import java.security.GeneralSecurityException; import java.security.KeyStore; import java.security.PrivateKey; import java.security.PublicKey; -import java.security.cert.Certificate; import java.security.cert.X509Certificate; @DaoSqlTest @@ -67,16 +66,22 @@ public abstract class AbstractSecurityLwM2MIntegrationTest extends AbstractLwM2M // Client protected LwM2MTestClient client; - protected static final String CLIENT_ENDPOINT_NO_TRUST = "deviceAEndpoint"; - protected static final String CLIENT_ENDPOINT_TRUST = "LwX50900000000"; + protected static final String CLIENT_ENDPOINT_NO_SEC = "LwNoSec00000000"; + protected static final String CLIENT_ENDPOINT_PSK = "LwPsk00000000"; + protected static final String CLIENT_ENDPOINT_RPK = "LwRpk00000000"; + protected static final String CLIENT_ENDPOINT_X509_TRUST = "LwX50900000000"; + protected static final String CLIENT_ENDPOINT_X509_TRUST_NO = "LwX509TrustNo"; protected static final String CLIENT_JKS_FOR_TEST = "lwm2mclient"; protected static final String CLIENT_STORE_PWD = "client_ks_password"; - - protected static final String CLIENT_CERT_ALIAS = "client_alias_00000000"; - - protected final X509Certificate clientX509Cert; // client certificate signed by intermediate, rootCA with a good CN ("host name") - protected final PrivateKey clientPrivateKeyFromCert; // client private key used for X509 and RPK - protected final PublicKey clientPublicKeyFromCert; // client public key used for RPK + protected static final String CLIENT_ALIAS_CERT_TRUST = "client_alias_00000000"; + protected static final String CLIENT_ALIAS_CERT_TRUST_NO = "client_alias_trust_no"; + + protected final X509Certificate clientX509CertTrust; // client certificate signed by intermediate, rootCA with a good CN ("host name") + protected final PrivateKey clientPrivateKeyFromCertTrust; // client private key used for X509 and RPK + protected final PublicKey clientPublicKeyFromCertTrust; // client public key used for RPK + protected final X509Certificate clientX509CertTrustNo; // client certificate signed by intermediate, rootCA with a good CN ("host name") + protected final PrivateKey clientPrivateKeyFromCertTrustNo; // client private key used for X509 and RPK + protected final PublicKey clientPublicKeyFromCertTrustNo; // client public key used for RPK private final String[] resources = new String[]{"1.xml", "2.xml", "3.xml", "5.xml", "9.xml"}; @@ -87,7 +92,7 @@ public abstract class AbstractSecurityLwM2MIntegrationTest extends AbstractLwM2M public AbstractSecurityLwM2MIntegrationTest() { // create client credentials setResources(this.resources); - setEndpoint(CLIENT_ENDPOINT_NO_TRUST); +// setEndpoint(CLIENT_ENDPOINT_NO_TRUST); try { // Get keys PSK this.pskIdentity = "SOME_PSK_ID"; @@ -122,13 +127,14 @@ public abstract class AbstractSecurityLwM2MIntegrationTest extends AbstractLwM2M clientKeyStore.load(clientKeyStoreFile, clientKeyStorePwd); } - clientPrivateKeyFromCert = (PrivateKey) clientKeyStore.getKey(CLIENT_CERT_ALIAS, clientKeyStorePwd); - clientX509Cert = (X509Certificate) clientKeyStore.getCertificate(CLIENT_CERT_ALIAS); - clientPublicKeyFromCert = clientX509Cert.getPublicKey(); + clientPrivateKeyFromCertTrust = (PrivateKey) clientKeyStore.getKey(CLIENT_ALIAS_CERT_TRUST, clientKeyStorePwd); + clientX509CertTrust = (X509Certificate) clientKeyStore.getCertificate(CLIENT_ALIAS_CERT_TRUST); + clientPublicKeyFromCertTrust = clientX509CertTrust != null ? clientX509CertTrust.getPublicKey() : null; + + clientPrivateKeyFromCertTrustNo = (PrivateKey) clientKeyStore.getKey(CLIENT_ALIAS_CERT_TRUST_NO, clientKeyStorePwd); + clientX509CertTrustNo = (X509Certificate) clientKeyStore.getCertificate(CLIENT_ALIAS_CERT_TRUST_NO); + clientPublicKeyFromCertTrustNo = clientX509CertTrustNo != null ? clientX509CertTrustNo.getPublicKey() : null; -// clientX509CertWithBadCN = (X509Certificate) clientKeyStore.getCertificate("client_bad_cn"); -// clientX509CertSelfSigned = (X509Certificate) clientKeyStore.getCertificate("client_self_signed"); -// clientX509CertNotTrusted = (X509Certificate) clientKeyStore.getCertificate("client_not_trusted"); } catch (GeneralSecurityException | IOException e) { throw new RuntimeException(e); } diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/NoSecLwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/NoSecLwM2MIntegrationTest.java index 4daac68f9a..8331b99fff 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/NoSecLwM2MIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/NoSecLwM2MIntegrationTest.java @@ -29,8 +29,8 @@ public class NoSecLwM2MIntegrationTest extends AbstractSecurityLwM2MIntegrationT @Test public void testConnectAndObserveTelemetry() throws Exception { - NoSecClientCredential clientCredentials = createNoSecClientCredentials(CLIENT_ENDPOINT_TRUST); - super.basicTestConnectionObserveTelemetry(SECURITY, clientCredentials, COAP_CONFIG, CLIENT_ENDPOINT_TRUST); + NoSecClientCredential clientCredentials = createNoSecClientCredentials(CLIENT_ENDPOINT_NO_SEC); + super.basicTestConnectionObserveTelemetry(SECURITY, clientCredentials, COAP_CONFIG, CLIENT_ENDPOINT_NO_SEC); } } diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/PskLwm2mIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/PskLwm2mIntegrationTest.java index 11b6f3f6e7..d7296fd47c 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/PskLwm2mIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/PskLwm2mIntegrationTest.java @@ -34,13 +34,13 @@ public class PskLwm2mIntegrationTest extends AbstractSecurityLwM2MIntegrationTes @Test public void testConnectWithPSKAndObserveTelemetry() throws Exception { PSKClientCredential clientCredentials = new PSKClientCredential(); - clientCredentials.setEndpoint(CLIENT_ENDPOINT_TRUST); + clientCredentials.setEndpoint(CLIENT_ENDPOINT_PSK); clientCredentials.setKey(pskKey); clientCredentials.setIdentity(pskIdentity); Security security = psk(SECURE_URI, SHORT_SERVER_ID, pskIdentity.getBytes(StandardCharsets.UTF_8), Hex.decodeHex(pskKey.toCharArray())); - super.basicTestConnectionObserveTelemetry(security, clientCredentials, SECURE_COAP_CONFIG, CLIENT_ENDPOINT_TRUST); + super.basicTestConnectionObserveTelemetry(security, clientCredentials, SECURE_COAP_CONFIG, CLIENT_ENDPOINT_PSK); } } diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/RpkLwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/RpkLwM2MIntegrationTest.java index 1f73a2a739..0066014a9b 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/RpkLwM2MIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/RpkLwM2MIntegrationTest.java @@ -33,13 +33,13 @@ public class RpkLwM2MIntegrationTest extends AbstractSecurityLwM2MIntegrationTes @Test public void testConnectWithRPKAndObserveTelemetry() throws Exception { RPKClientCredential rpkClientCredentials = new RPKClientCredential(); - rpkClientCredentials.setEndpoint(CLIENT_ENDPOINT_TRUST); - rpkClientCredentials.setKey(new String(Base64.encodeBase64(clientPublicKeyFromCert.getEncoded()))); + rpkClientCredentials.setEndpoint(CLIENT_ENDPOINT_RPK); + rpkClientCredentials.setKey(new String(Base64.encodeBase64(clientPublicKeyFromCertTrust.getEncoded()))); Security security = rpk(SECURE_URI, SHORT_SERVER_ID, - clientPublicKeyFromCert.getEncoded(), - clientPrivateKeyFromCert.getEncoded(), + clientPublicKeyFromCertTrust.getEncoded(), + clientPrivateKeyFromCertTrust.getEncoded(), serverPublicKeyFromCert.getEncoded()); - super.basicTestConnectionObserveTelemetry(security, rpkClientCredentials, SECURE_COAP_CONFIG, CLIENT_ENDPOINT_TRUST); + super.basicTestConnectionObserveTelemetry(security, rpkClientCredentials, SECURE_COAP_CONFIG, CLIENT_ENDPOINT_RPK); } } diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/X509_NoTrustLwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/X509_NoTrustLwM2MIntegrationTest.java index 6ca430327d..b2ce6c470d 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/X509_NoTrustLwM2MIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/X509_NoTrustLwM2MIntegrationTest.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.transport.lwm2m.security.sql; +import org.apache.commons.codec.binary.Base64; import org.eclipse.leshan.client.object.Security; import org.junit.Ignore; import org.junit.Test; @@ -29,18 +30,18 @@ import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.SHORT_SERVE public class X509_NoTrustLwM2MIntegrationTest extends AbstractSecurityLwM2MIntegrationTest { - @Ignore @Test public void testConnectWithCertAndObserveTelemetry() throws Exception { -// X509ClientCredential credentials = new X509ClientCredential(); -// credentials.setEndpoint(CLIENT_ENDPOINT_NO_TRUST); -// credentials.setCert(SslUtil.getCertificateString(clientX509CertNotTrusted)); -// Security security = x509(SECURE_URI, -// SHORT_SERVER_ID, -// clientX509CertNotTrusted.getEncoded(), -// clientPrivateKeyNotTrustedFromCert.getEncoded(), -// serverX509Cert.getEncoded()); -// super.basicTestConnectionObserveTelemetry(security, credentials, SECURE_COAP_CONFIG, CLIENT_ENDPOINT_NO_TRUST); + X509ClientCredential credentials = new X509ClientCredential(); + credentials.setEndpoint(CLIENT_ENDPOINT_X509_TRUST_NO); +// rpkClientCredentials.setKey(new String(Base64.encodeBase64(clientPublicKeyFromCertTrust.getEncoded()))); + credentials.setCert(SslUtil.getCertificateString(clientX509CertTrustNo)); + Security security = x509(SECURE_URI, + SHORT_SERVER_ID, + clientX509CertTrustNo.getEncoded(), + clientPrivateKeyFromCertTrustNo.getEncoded(), + serverX509Cert.getEncoded()); + super.basicTestConnectionObserveTelemetry(security, credentials, SECURE_COAP_CONFIG, CLIENT_ENDPOINT_X509_TRUST_NO); } } diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/X509_TrustLwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/X509_TrustLwM2MIntegrationTest.java index 28a10fd278..a51ea98be6 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/X509_TrustLwM2MIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/X509_TrustLwM2MIntegrationTest.java @@ -30,13 +30,13 @@ public class X509_TrustLwM2MIntegrationTest extends AbstractSecurityLwM2MIntegra @Test public void testConnectAndObserveTelemetry() throws Exception { X509ClientCredential credentials = new X509ClientCredential(); - credentials.setEndpoint(CLIENT_ENDPOINT_TRUST); + credentials.setEndpoint(CLIENT_ENDPOINT_X509_TRUST); Security security = x509(SECURE_URI, SHORT_SERVER_ID, - clientX509Cert.getEncoded(), - clientPrivateKeyFromCert.getEncoded(), + clientX509CertTrust.getEncoded(), + clientPrivateKeyFromCertTrust.getEncoded(), serverX509Cert.getEncoded()); - super.basicTestConnectionObserveTelemetry(security, credentials, SECURE_COAP_CONFIG, CLIENT_ENDPOINT_TRUST); + super.basicTestConnectionObserveTelemetry(security, credentials, SECURE_COAP_CONFIG, CLIENT_ENDPOINT_X509_TRUST); } } diff --git a/application/src/test/resources/logback.xml b/application/src/test/resources/logback.xml index d3301bf660..175eda993c 100644 --- a/application/src/test/resources/logback.xml +++ b/application/src/test/resources/logback.xml @@ -10,6 +10,7 @@ + diff --git a/application/src/test/resources/lwm2m/credentials/lwm2mclient.jks b/application/src/test/resources/lwm2m/credentials/lwm2mclient.jks index 490812c01ab483d3462a09cdd18bc405fa23abe5..ca8c8ed1d77f31bab0711a31eeb63ae952c933ef 100644 GIT binary patch literal 20462 zcmbr@Q;;alwkYbhZQHhO+qP}nwr$(CZFkRh_iWpJ{&Vk*y< z^R6g<+ruL7Dv=RT~Yo{Q>KFbfYCr zLPYiG?f?QJM*spOV1NOG_Y8-7oWb`&^5VkYFEd( zz8^saYzfmK+kCwq)I1+Ax|H4}ujyDbEcLsvKozwqmbDx&gkG_+V(kpPDAW%#jPB4_ zFclg+cY+|X7#apGG_1#Wdn1&Fa zYIc=_6FA|hpDiEhu8UKYiD=Qo8 ze<}XXE7f55=dmFfF>@q27VVlrRsFm8O%Y za@qeKag*^6teqx7o-IyjeGND2fq%qhiV2aG9CmI)xi+U;jyqYyvI`9-XACk)1-|!Y z?UC?|g_hIdG{_a@ba1PI*X0BparAAV&hA25hjLrA&{C9;Spr?gG^;m3jUFIK;;Ri3 zA~}ve|CWMkX1yPtParH;>dG6qOM`&h+0R?`k1;KbZbW!N=+8{_4>R;hKa=r zV(=9SZZ$XpumNTKZ^T*tg*ZDC(|@|FC~PE^QXKfOsMkl2lp>xdzjD6^{5#^1E$*`k z-&M*&xGsj&@!k>th=<#uZCVkA;=WR4U9;j5=8zXh`1!{GWI#rs1%CV z8=7A)*rU!dq+u6%(KVLbx=>gCG!b$X_(0>6i@UW=mbEdss8xK1tFBC9)J`SphBbUU z<5*v*M{r1YgvCmx`xCHFrgSvLp)t3%R|woa&**42>{;T3upj~DVb-8!LiLw_gE>ls zx8eVvxC?+2fa^aw^#5j5I{^ECCC<+I&#odi_+RN29IPNntm&Un9S|VEKTY|6j{yFw z>V<~lbP7GtdzxM7CGYv*0!8qTmCwKP4VqAD%u9RpTq|{_7S%K;V*`T37?LnZG!0r+ zIGX*AjgFSKz@|nkeRHrwlTS%Y^b5m&01k*6u-wKH=4dhh0R3qyI-F<7k@m`!tLYT{ zE1L$g(c#S)TS?vKZgf_HFigSgsft%_Qz0u8FLU@K>}nErdGj#2aek_nPqkcUC6Ybb z?Q*lyvv8qV*H?v=FP`iz7{$tx#6hq#oAKdooD7PHZNrA9GT_XrKuma8;r#p!FHYzz z-|ACL5pdJ{YV??yHIhZ@gBIHe{8v@2s9l~=pL6-)3BBZN0Mx$0qz6aj=^7j#eHFASvO_h$X}9X#8pc4wVLCC`7tl)6i)UC%g* z!_AzPPz5q0H($UP@e3gZxg)xjWnjIhRi);L%eH_5H9oYGX@pD(l^v~xv zC|a{?*n5bTzK#h~>$E;V(xrr~dKusHxv@uc78j_6+WnB{3#I8c4$HoV#cyI8KDBl)4}y7(7{`^F(<6>UlD6Y*)Mec zCEwQ|(=CaPzm_m2jRj)_*$a-H-@*o|f0ZgCTLHccYfBdXY+mJAj>p!q%~O}4R(7Z% zb!Z=4>RR`Z&+oUB%H#a)`fb*|sAGBC+%z>twrO8p$^Ytlq{50PTrIS^<+X5^Y zy6Zvm{|m5~1I*}Dq;DAfQd-D(b>xjBsNg*6L%=1g$8C@^nU8%y`Q1t%F&n_hAie~u z0WSrPNIP%oJXJC$0-Zlx0o=F_D?a3D6GLq4j;0LoJt#A#f_9EDi^J*9jyr$m{zF-x z`KC6n!1t3;|DlG%G^S_J$oFMRh8_KB%d{YwxfvhgoCq-DsD{KrYrbHET2&AwN;nuD z>(V;I&}oa7{{uxWEVLeTW5I_mxXM1jK;}LuKM$9U!r{{tP*(NT%{pFxU^B)Oed+9dujiQ7D^4hcU4; ztI7$Z-*5=wcF+rBiNFW%&5a-w9XePwhrgQ@$-?tZhi-S;+TbwD;Lb+f7at$hc-jug zO-%V6q?f-G2s4oHh+28|$meJCgea@P{%Ct-x*&Aq(*-THfd`ev#Kig5Z}rLdbTcx0 zpO9f$^)O%fc5Wwu_F&}a`gS_!q!0=DH1#rD9sKvQgHfsG0g-8MY(QTqS{ajl9?l7U zaOn->xAda69`%x4D3wbW`D;pZlD(hv0H8einK;Q+dDn`6curtP1Cs1*Mx&I0pdOEy?b@vMjjo)^V zs@P5}Z2pbX*5hC#9gXqD#0Jqz6QGclyZL2=>|7yv|A8_Br=H=j^qO+5QVKs90`*~H zi(r#q3|L!?dt5(GAjR-G_cw*2D{`Bm5x-9pP0UfgGJQgkLF5U9aC*$c`qPG29~3@;?|yQ5*RN2#;z_4z+9cRY z=?P7wA~Jh?p3Z1~SdR#*cIagK2t<_*OA#J>7ew~a{8G@urMh*%OYYuKn;$OUGBfU& zj(o-dhFQ8Rl=W)3)5Em|zZ9A-7)C?R4E7H!JE3=f7Efl_? zG>rxZ>-MZUB$h5R00&&30z=#UuYFd~OAvNF!S^%#_ID##mh3&KsL-(X?=f}--Twv{Xf?XwAaY*8e$J?z05kiZ*(PbviF^^Sp@;$@ok|` z)oen%U`Wr@^*VgwqDUA!Jy@zqK|P-W;1a=_#amFvFZMMkX*o+dq=;MgIln@RjN=z_ zyCptFuSb9A4}5+(v1hlC3Z0C0)Hy1fR`16bNa}aw7_K+Rm4_pbSQ{4OO{?gB($5rP zuz(~8>8EZ6Q=YPOL?PiBngca3Y5Ezw8{nHl_PIqSJ{4f!tJ9KR!9^MLX}Vb6pwp5F zA!A$sG$LN~`a}f)Yz~ag#|~hAaEOD^a54%&Q0O(H^p*7)vR-f8;ban=I&H~(!a#6T z=hxF&n_?(f8lK$i?=tu702;S~+<6Nc9pmH&_JjB{mqGoYRAhYk=O!BJ^Rs&Snc7(4 z#<_Sm3}j{Nu7O}7ym~X5fq>tJo5)R0Od6bIkC5|7YQ~cVJze3)>d$6xvEF_aRL7kM z38L3#tmr)HKsRL=VNssT*r$gs*iOgl71+OlP+s1n`%59ql3*S)<&mIbJNvZ9P$+yK zsIsksZvS|;vWbhQ!kc;lS%u;wsM}!>5YLh+_cuV)T8Q=lqHJ#@DTXWWk(Xnz^1r!d zdZ3WvS{21Pu|}cm_3#kaG+6`qD-N}cqvoJx7FZ3?LG|+0^fsPSW7#q=QeW+)w9b20 z_K9uLw-5`TUg*tjib=_=TMr$tmo@#B-vE1>zOGX%ncmTfh;-P?Y%#U4UAcfBZ9JH`VbN^8@-m5o;7l!GkBJ~s(0#V+S-2uw9 zh8{kmkb%in6OI!PN67dgmfzxR^YDOdOZP$%7h;@(jyC!7Lc z@Oag=%GfK_f@DD!8M$!-_H(mdX^*i3IJ?7+akbBs8#*^_1zdJ*ZEX!H>M|JkZH#T! zBkH5cGLJFo*w$^mg_LoV#!9$`U|B6e@(Kq7Q_#)FiGvD4l2q(5SclpcgVcdlz@01(q9va*ZqwIMbi5~o4{Zsl z>_{gOjW)01YYheN`mp~Edui*!au`L9&A`B}Z54nCkF32l1KczB-yB$+NN)|f#c)}< z`dgTatZ?5oAgbx;r0hl{h}gcWnu$&O(_m!F^G~H?rQzC@soX}2i22MzsAWx7mIY00 znJlA~WEkzWu@4aao~Dy2DTJ1o4H3)4-eoWYy^9#@jBJ^1Pd;1Hlg0n--VzW8?NU_z zhXwdGT~#KU3_ii?+oI7qC^*g)EPrVdt5a^vh2ii}tp2zor0WDM)7nD}?4-2hS!S{- z^!c+fF7Os98hSRO$YhAqgb|_a!%HU2%1W9kJKdtC@?uLcOX1WrM)dx1W@_q z_3}M&nCSWWC4aZ}OF}%{MT7Wv5mNx43`Gzq&fEsj4>_}h=sQ`;T+@b>$VXe=wSo;U zIt0~0tGK9f`4BntK)Ltg&j1J3DiuWZtb*$5tE!__`nmd6yfmEZW)0h6Sc*ovY(&W? zvfow%1k}TyQSn2#=Tc1cel%GCZonjNtFs)&vXQfxp_F;)!Irt(2YMPK^5wLrNeNaS zCXo!+-16UdC5nys!`5DcKD@WOH>2j=CVHcLiE#{Cqe1#@fYE-z7(a%9!iaQ}OazE7 z6Jc_^bi0oPmON9Zp6^3w=tAw1Y2^>PHY~NTXs1ZRzT-9`znqUyx*(lz%Ts`fQu=B%TD7<$OtO!LmC2kt!&a(z#~EZoW*~%=Bb89%WSz=#1iaB+|HpH8}O zau&$cF+7Z3O4PhUt|;S6r=jMut$UBF0~wP1X{v4#my`vGWQ7?j5Z%sP%)L&L29)7F zwq#%>v~j*@NDDGV8UpatRW*h`SL?4lGX(V6OR-%u%O=g<0#C|#s9q5V2%Bfd@iTaf zWL((PBoTK&77=*8Bf#+E^L;q(cK$@DH_jw+JBBubWq~(VgmvSuRSNg|=@v#sMCE;&vkk=V02@hoFk>J4fe2fOG@p z2IuHOxRY6Cprs2(G0NI!xjXGLsz!{u$NM6tqv#fENN2qJci}6^kwx)fVS9SyFQLUI zOkQurr`vk3Xq;afEh!c!H?-c$>gCo%lWF+8(8k(P>68^PnEFynfO>hW!;pP|cgh)W z3yeA6MWwUWg0Ac*4PnnLo$0mkgg@2hSU}9KVAnOSK-ujW{V7zurd-89+K}SnN>wfa zYGcyODhP$XVyifE%YDwNHhnO(8)C_*f|-<(3f?|>5z^xv4|iyjMfvjP4HgFtRH6$o zlS$^H_nEN|B4Cd{K^g0sbp>$NEFAV{*-%;cNJIBvIg`~GEc%OKJDD5~1*}PW-Y^>( z4{L>!pFI1q(tmzN3pH-(i|>skalRWj@aR<9_s7S#yeP+i5XAZxoY0epsK71kj%{s` z1B?#h5M}ebO2{xMjU3_Vi{x-=cA2Txkd)1P`o})=d?l1BiRtgBhJaoX4>4YrH~L8+ z#sj_dn(9o&_yjU^h%>nF8UTqKyg4uaFvrbF!5MYZ#0njPUptiZlS!QpQL$E4PED@7 zg6FJmmyh9OMkc?13^VlC)KwnlfZ}OrzL{htP?3!$=dd|EiOjWv)G9}-zH(eECTpc1 zDRMvjz2zDxA=rG_^5gL zXM?mL;P?t(Q0Qp7&+3Cp!H{d8q&6Z4r=!8rP0{_^_#_i8S0vxWD{k*w3;Juw!2&&c zB$)E!4AlM#Z9D^~wnaw3?X0nnP}@^JVU%=-=Ig1;3y+)&`c^>C1Cl~)PqMt1M){}4 z z8Ycse&LxPB6tj(~84FM=Ymk`uyhaOc1nPhjqtTVUuEPve(TnAH*8{gmfzk(e9|^nC zO&8dv0X@^?xRbtoX#HlQM3P%yl%fT-|Xshij+iQ&3{+cYb}op*0%%%FFVG`WXAnuzfs3vuZVyiwg+qhQ&#YfQmnOev3YR&Fs|`_w zvd6=6<22})n8JA-Pc`K5Xu|ITWkSh0{loqpH9TxHe5Q8#ZkA@M7}rRDd97vcr{(aK(_NO*AuoQXip%VO3*IND{R*i`{< z)R%+f)}YwwP^@Jsa4xv|@u4q5q%|6rsl5{(;mplDe5J}dYhn8@VG>ix_j5M!h~27f zp~PsH79uu#tr|%pj?%+;SmeA&?Y8eB7>_Z$TMI+WpRz@Sk*W}x4H_${CqBv#a zf>W*e-W}7yPptkfa~ov-1>zP|_UoGnmkUKZ6ujCTAd-1Z3pzcPP$`|&2h8Ag@9<+&Z*!iX)(*Q`Mjx{K\hylOan*;kqm$M}?1Cu_<6QnGg~ z@4c)8L2ihDiI_Dg$~_}ZmMSM00R;O!q{(3L%5{QgccZ$WDT;eQUC6u4{&3s}TUiEF zDrF2{xe`cVzW-F7uYXZn+l~UqERv{-?DEDn_AOJW4GR;dMS!L}$fTmxIWln{vk)y+ z7r3UsHv`E%lcDeG``N$e<0`V|w221HENbMn$@*t-x}hA-S^BSkB>p%MCZ%1`h4V>y zblRom4^)+FB)rd4I{2_W-w;z*8VM(Mi;U+eo`lwhf_`Q7A6R^O#x}fk8GQB(sH&A~ zEiKk$tEi`oO?J_Ql}l@bm@y@bmtPIMXoJd7QIb@dyE~*FQpu4&^+c;ATk~q=PIUgf z$od`Pc+{$dx1w5M3<~j0LuP;NZaqx7vPLbh!xl_mI_eam#moFkAu<)8z817p`OP8O zm0QGjeYZFPFc%_2Xz=}4Ct=!*$6wlJqp5;v`;oJepMYT4eWgx%S%>$Z(H*uA?R(^) z|2!Uc`-UoI&wnIeR9UrXBDT1(wi26onmeL_CWaT-o0}qd0-_MxN$ggSZc9T8`T(Mk zqCojr40so%!-p#sU`EO0U$$4Bj0Ci>Ju4wTlogY`ua?b1^DzQ|ujbn6$F2wQugwQ> z7I>#(yEWU<$BJZ)<17J;TknXDXPI)vv=l zbg+&Y{Hf?LwXa^^!R>Vo89r^bhvh|7^jTh#LYvCftw32^Lu&6iY}w3}#K@Z_Ah*Cu zQ9Ihg_~Z1xovZA$=IPp75ufYDyK4>9>E_K_@s}FwcL@!;EvPkhrGE`m8IY0d+fl%y zbLo?H&Y)S_Q2~hQRpq19JHu(NICAxh)^JUVBk>_*Uf=gf0A}ULJ&r1{bc8M(Y+Q ziG+RGIWZKV?moiL4(7|Fd^Hc{O@0nzy`)iQrHJVaIy^ z(h{Jd6>{%A>9KGAxH4D#*T}I$S2lT|+BAtH3&+ZYaPvKLxX&>~{T}FKQ%h;+yfbyaXK8QX^pJ91bu|6drLu%c$u zOxxhk<-wRgzn>1!#}xzy8z`QKnS@kLLgTk!FZyB(0dOn0;6*12*Lqe)E8Os9jPUN` zN~m1)@>U+j8nGmzSiz4CTJr*Vu2J9pxR7 z6Y)vlAH(~I5@1gq)armBGXBpjQg|qX?eTah=7lt7%IRu(<3tYv+^<09I#o_29s-Be z7SEx`*7B(JVX&h!g2_bsIA+rx#IQUw8suw?TD*O~hZ3evi?h;HXs#oy+&>7t1~y%_&;2@!R5S*NyJ7Wl_-`qXeo6rW zQ1?7+v$DdQY4Y|E{4^v6ro9-@M%{e|+>u&k6Ps`fbd-dTLw;KNbr+-DhH&YZz1kKUB(;J(# z1Y-&8uoP+tC-_^I$=l1T6m`G^&{Cq0VV1ZVtyoS5F0xQi75Pedf2H5nFnZzS$Ga7> zf=v^h^i_E3RenJ3U4-_w_9$6m&I)CfUiw8Kf53^J3`WqmZS=0~V3dx$`@}P84z?hc z;>=+wRk53=JtC9pvaMG@T@?)8YC)h09&BS%(}2)qpm@H0#NSR)iX9lV31|(iVqf8d z&|7y+GI34;l?XD%xxUHV(`cjlFrji$xvz-MV7_#onZgQ{gZ!=!AT&*6+0InW_Xq^I zLF#SbdO4{6jyq&+`65s%)o^6yJ-y&^aV1QWaT@if&zxgKKWqNRoguqrEGKqFSHr2g z9&+xva6Hia*BBO$fHtqUWO)%-x-}_1&Zx?Iby@rz^=h9moT7+*UKSE;xz3PG_atBf z&BzGIbkxf&N)58rI7^I{p1Fa1x)QO58KbR4r~;#$o2mOFH8#tz)))-Bv$Km7wQR`l zG5IT2&`8JBNj-}~ZOonS_QgbM;5%i;JeJpyC}q~rVx0-gnq&wIH;8Z+8Om}|DlPGE z7a$$#v+wA((KHqT6lq6yu)Pu^92i+V)mH zZ8n(1jcw3te^J3EtVn{+LrTlqzxU6}12bf)a7KAL^4ZMM@$k%hmuhlG2?p7P9x6yE zV9ylB5GGK2SdA`_cSwAg^wzI(nz@X#6`PJLBUDwg*Xeb2!0|CWV~IjRqY?MTOS)g` z&`-@DA)_?|Uk{Qmp$cHbK=bc9h%%Wqd~g?n0J(4dCdO85Fr>x4${Di^{2 zNn|GQIm!4&H-%AAI134^ee`Bf24z>jkM(kc3t){iu_q^8)d*`1;`PPlcD~xjznFrk zcN%s!77sjUJvGc2Ie!`EOB1X&xqne&j~oK&fe>3o-ajnWv|1zDbd6Iwm0{Gt1RebH zNeSdRxY*GCk*Lv98ES2mJHkb%&wuOV8Z;lth|TatK#Cth6}~(RTibZ5J$#;cVl;ky za4>xU?qrEfmTpl)c(I&xLk#PMd)uC5)+u&maNG@gnO?dYgx+STTHL8aS-Ll*5wW5) z#)9*J_=1?z*PLOCNE2wSwlBY$O|_O^lJcctZ?ysDHGtOs#l)694LSEHAUO3Eu0?n}HHOiy6jhL~aa~$YH16C$iB@OyYGs z&*1K`>)NvGYL+M^@kS1>qW=1+L&a<|k>^mw4QampfZSs?=4&@T(nMjFtk(oO_YtX2 za25VLcxr6r9_Y*(oV=D!7n z%y(Mxi&&Z)*+r_C`s5W_w#uR?n=hU%*vx`5TJki}#r_n6 ze8Ae}i*Hs(Q1D{$ciIW-m?sphWYu+Dg(!VZ?VVRg7Ol#xDK4|>9%SmYhlk%+CbDS# zZj1v?;i&+R`=@fz?>wfvjiz!PXr$t2jCb`?Pi}jkVam5kQfoaNV+Ea@L3Y;`nHq4s zm#1g>7zKcQ57pi&-AFUU3_}@m!|W$7Cod3ai#KMgyg3K+-Akxd_U%J^@M7SQP^oA9 zKJ=3v*Kn2Xv{^IqdMGu*6g=~kI;I{L1zHcw1Op%8`q_x(EB2&0gL;@V1*oJJO)u*MPJXELw$>e&U=!0rO>?MrK?rM>&p*-r?pg25)KgUoi}?0+DDiu z$%r_+`s!WT+*-UiJ`?hBMtQ-&uPJR}h*fG&~u#PG(PnbrQOn&P7GT zKzn;Uy-;$}+e3R`n>=tDz%U34O{+$nV`xDG&_}UdUUmw!8#8v6#STLfXT+tsJ}FNw zBq!32j-Jx?)>7d&O$o-cpgo8leHcMynggwZ20*t*O zo@dP7-J(z`Q)mUYo5%sQ1g_#LR9?=dO0me4+Ns+y3M?9uptc7B zP*Rj1D4=&Gbq2PkgM>NI1G%seJ`=XKQfUHCfMS^e`Bi1Y(090dL6DuOKWYo(mb#LF6vUrB z<)GAroE@S-3A6i-JTMCrqQ46*YF>BgfrY=D4|TV~?h`(bfo&h-caR4dw|%3KW@?}Z zc6#bB^6Sr68!+5R+s;l8%zdjEEIj7-!C5_^Uzmk8o9rI4Tn=Aw=q&^_l*bLt zGe7{})J&?Y)g^I>0N*gi`}uQjd15xx?MtqSEktxC1BX~OUZ;wVI1Lh;QcF!YNi|Rb zuR4$PyvZ=tt`jca@u_gmLL4V@WZabYnyP8Cjz1qIVKoZpEd!!MOu(U7KR^gW@v`o~ zX)k!CZAv*o{`Cv9ibcIpu}eax2rBGjO6$TH@=gNg@|1jlvDE_qPMwvjALX%<2{k-I z;S!31x^C;Pftn5`O$)UoM>_pH7X-x)7Zv2uxJ{+z3@a~DY0F(^YRWV5He^2X;`a@@ z_r9z0jYB#vdXM}(u9M;yJoiRfO~m{uZ2JT2<9ta35Mid^Q~8xTfDAkT?=-(46as{! zHH#5R`1gpIUYn)`PW3!d)jW>Z zkel1#j5Y7jy4;DNAnHOfHq4P9hI&{C_{Aj@eE?k^p5x!%X_O>i{SRJ`xJ|sh@NNf` zHC-TK*r1BkhKzQ;)wRRjO-qoKzJ*2hI4zimNETO255P6Ikxd9mzSR$`KL5RTFr!?` zA_D1BECr?*WE5?Z5%!X6Y~PAJT2w*7*&Upki~8w8@IThLvZbz1l!Q%$vqD+MP5t)i zw8HHcVVd!5Jk(Og>CT%JhUj2e=_jgqQFH)$V7w<;FAqrKf?uW8JbgQCh?gE_`fQ$f zh#%fix>(+*HPv4;GmO5J3f#-7qkws=Db_5>T zQxTEvhZA|LSyvdEAx}4a3X9Z?AS+?f3Xh%~+J$6@(CDT*UtN3t7JpHa`l3$5aBP9= zZgN)xJlhetpXh>6_DMTjayj5)2NWWYg6kE2%60cHuZ8V|uOOE|G*sW~jQ_qM%YiFG z0%?BTDNwBWz_?koE6IWq*Hg3aYqG*x2US}?l37*72!uy)FxdRc%ck)zYd?2LO|l%P zZIC>0pC|})s&RYGIvNH{qR9?{*A$wJdJJ0mM9I;Wo(G=t+yy~d_MkkiXtkF+xKT7D zO{*o$NEO=`H;rNtnw**pqF_EmUNHmi8=26x;fX7jHz8C425xG2BY+>>%Bh@y-q-D8>w-dVZS7*zOxibYS` zEl!UShaxF1xB3lF%)C@m)8y9?OP}dpu%>2CuoNpcjhGq6LqPXWnQu|2kJdGAazia- zKU|C0r>+aGDW@E=&CulGhb@LAb&awoBU`W3JvwC(0sgM$@BT9K^^5LfG@mr zEMVV%cFIi*4*p83hhD0|n#5F)%sXGTUV`PTL}h>M?)4ea>aOju+BuCm961wlkgpom zjE2FEnbmC|!pk4Jij8=~LoifSh_D8Yh<3C)2jLp-^CK;Ly6(!K9*|9{I`Ub#^FdKI zI_GTZ>G5C+$T#xkwC!Yyy6{PN*ImBp;K<8&QG9qOT0F_**Oz#`3nYy#TS1@wrS9f# zuqUtxzz*s#=SV*hGK~8XD6!xkvRc2Yfxb0mzjX6XSQsCEkVz1}?;D@8&Cza!b7nCj>pK#Hm zax>v?#bC+fzGs*Z9VghC_!v%8g>gSxz9W0E^24z^ZZF#ml|xpvJ1Fv%+a~1|sw{iL zND-yd9iCPDZrFEM+ms^q!%EdV8(8fkv*HCOBrh)>?@2F-CpER5dVp6wc)g#bE$L3; zy8F?D;krXs!ZVd0FkPUXj)MdXkC>;neT+)>cYh{g1IQZ~?Ub~~FSQp=2k|*%#PT8Y z6EZiEiC)vfj{^Rkg^M^`toFx)pW+RSq##cCd#rZvu=u^~T%YOG|E!eC==lpzH3l~m z<->zQ5KW4YX%+q_?tJ(On*ZF(0o7=I6#1iP0P-unotfZoP-4ve7mZ8Fg>o+S$me6< z^5x$9R$?)Ae&4l6HzbdAP}Fn+uQSFJ+N|q&2N#p=&)TP6$qMCdPQ&pldINmuIS`6895e3&;a(Ph$DE~gXjsO>HZqm8Lbic+&@3&9^~BmQ!AjYu$d*EvdzmJ zyGBS!Qp2-;s3OHCD1@>m24x4z$1KFGoEq1?u2>?_{Ae|Cbs>BRiT*i!#u$LAukj8? z6?+N!A39Cbe~7u=5;X~_Lzf_H$h?HStJ>2mU=k|`-1PEVz6Tm{V|$(Qu0Q%4vRdPE zHYSNP>LdARS_w&&woR)*ajvd19-tH(Js2D*QtI$@)=QZJ5b>DYQ<2Z`*ES>)#ZX}j z*il6em$c;AUTYj1;gTv_MX~`VuoSJb*Q<*vcOsr`Qrqah*2^r87u2LG7g=Fe67Fq~ z?jqik>A3UF#qCr$!4B2AiX#V~fiIsDm~x1?*&BI5vp7$fi@8{X@?M+udp zL@J^sH-7fmUIgZju)H0QZI>g<#fmg!t5h#0I9+kb8Cy~q-#!sQIGS7JO2O2xsfL{LNlei7H4K?YL~Qs{KHUV0h)&-0xGG9qv~l^z~!?9%A5!IM7D@6_^e z!9@XFOF@^Xl27X7z@ye^(C&s1`bs;X&VtIOgHRt7TB@g6imLG z)P~d#bG`1}Ztd^X{?!*#+~=hYL$NiNGf|LhacpZHS^F<&EF!+gVB zZaU*wf!iJg5KChVD$nTxs~q0k4<{{gm70D)K$jxd$kz$-yTju0;O{hyjEL z!!MIi1!cAi1ZLQ7KGbJWPlqv@PO_XFgeMVq+9L%QWaidpe0`5GrodQM9hcr9 z7(n!ePr9b($(rc0>kZm(b}mP*-1uA}XkYOiyu`5m>6ka?zPayG4tGuA$1qOx4r^X$_Ljvr zL-}pbp*mezfC%a^D_;*+-l-WG!-OiaT{v&gWpv{fGTW6RO8P%$l^DJzwONmOAYmGO zJtAz*YplpLkZlGESU7``jmdMbA$Fl#q}=!bRo{n8GR!@TkK3L2{j*S&o_3{Cz(Py{dVzjZ81GxIko zc_6J;MzwV@($MTIWT(mFTDL+zH`9s%WQSLH;Swlm==x^vQ8P@~Kg{cd-2_wU-SWl* z<(ve8w#&>7;fuIY6$>-7fiEdVWl?q)VHq^rhXqK4Ul|}SSy4Igpd`OCg+O56=bTdn zsBkfBhi^-F8QYeh*A}WU(1u(@v8Bf)>i|CjLm9?#z>|kYIOH2bqu_D%=LzAK5@M#x za{X@}RUbHvlKpC{5A)&c#`CTa{XL6#tUHY}xL+t_C+@shf@U4?K^;V_LDjuq<%5(H z3@x47<;;o*h3){%*RGtkK5GG@lfgwof?yW zYx^j5(-PKzhl#@katK|A{LrFBcovNjwKv8F;G`eBqUOHU6Hrd^4YcyF+eG=XQKgZ^ zWj<6^fBqhze@9mMPC&zscdIt-cXwFw z#kMzYG~v$+yGLJ2^IhWZ*+^6+)Arn(5+nj~)t} zv46>nF>KP}%lDoE@d+S5$~X7LQEk~wU} z8xRCVOWc7@N+oMi-l-%sTHP36`~W|OpL@cG z!|)E@?MD`Os(WOMt2sUz^@JlMkpQMGh+b*805@nT<~^S%Wr#ysdeAGo%}y-4UgCF2 zDc{u~-+&3YSOO^k*rLwIiy7Re0aZqjx`}Ak>yd_}YpS>LDDy4}b4!Uw>V+((7J;vk z#fo1yC6GV=MM4ASl4lC3uMIm~DAbRQ<+XKU<40doIGCRFVc z347rKduxiw3Ms7?5QmT=k|l8yI|{x!luib7&!g5HxJy8~{>(fgV2+Ndm>fMDB%nQf zN;vZXJT6cbyRrSMQ7tD@5i}DK!qGK+(fA{FhCCWn+=><_QW70H?ZZco6y2>-$eMPC zn?6d~eJts3X^yAY55Xx~H4m2o+n zmI$fg?&JIB!K23*XL!sz#nScB!Q#tuVjO`Vc^NW$S9>&*`)9Ezp1V|4?)6_GiA+t}ZYFxk> zGMsr^URa*Fg#rB?_VMBd{8HClk>J`8M*|Nv-l}mH9BW{9Z=_i2`cx^ft6b@X78O|b z$H7-n3YC{lDeB3<1BjV8PUQzCZ{sxXS23Y}^gfvvkgAg&&ewwu(98Th2Je-N>>0xI zgcD;v5*?|n+TCk3Y~_IPUUOD|cI8QJGTUe-&+PalN+7c> z>L^c0(W=eMbguz|EG}f7%wGpSfMQGRe3lB!d^aQinS=b68(kT!4Z5&1 zbys`%>rK;V{#Ul4dgh+zz=68>ScKF^iV2|Net~(G3U)!fdALq-4FJ8%I*LWby7eCg zPoEn@;uS%zEU;VnbQ)e3CxNHn6;hpaZj^oTu~o1Zb#5)jy00A)wSnDw84mbL^yh=^ zeG`%;1eOWM-|b*55-YXN2A!=Uf+VoEgvfSd?fT_2U0&effWaYiWnUB+m z%qn-GddA5Gmux$db{!0PgrT%#BOrZe=x~~kGKps*AP$wpUK)5@KmtS`KZ_1q`v%SI z$z*@Rf!x?5+!nZsxfY@TEVZ_|FUHD6Dc@JLc+M9Cj-m5ABajU-?!|o+hJ2eTfkqVJ zM`B*!S`-DoAtUoeXq-_7u>z(R9-RT#V+0!w`8pD!&>$_&#t@2cV9x{Ld@fwbmk*&M zAVlHLPl91F!?a|1I{P42ZA+3UIec7+hb=LA8`$GjY{$-wlq&Kl%rYWZ=@#OF`G7I1 zo`RV-2F$xNmov;?T{IvFc$J$4EbcovTjj5ZVq7+x1Ny_jUU{z>7$K@R2uhQP zkTK-D{}a3mL-gBU#CQJJ?s}c-YYOcf+AA!@j;;Y(qmi7|S&r6Q;lPSe$xuZJdd0ex zg1HCCuEb*vU~&l+F+pk7<*IGTbV@X>z0W~lTPU9iJv8(DOfzsjzfI%{a<$C8DQ3#r z!8!`wO{Qm%GAzuGhy8=Z$HKtMh0b8{axE$we4Ss8MDISN1)}FJ{?VDi%^aE6UQ=On zeK4Nb)`3x=tc3DhU7AZ5#N6u0Bei?zDcuTWflhKTgGkbVaAqFR)p)5ap^5B~6GxPN zhE%cG@8vaH!M`^TMU0W7q>_-o5|a|LwcGw_r4$1g|G8T7ALu<2M3atf$+^3WOM zs%!1j-40?C3V9UR$r+tywaO^;Ua;XgVm9o_R{r)uPH0V3|1gNr*d5_0GMo)}9VE{< zQNr#3psk?z(}f)z<4WAXWhbSJouDdcY9H^s&$8Y)KLN%-FcL2An&Qg1#qpU~Aps$k zSfb9z!*4U8;!A~eFq9mP9@Ng|{A9?;Ym+k5rHg0M7oODhht=1OJXf2z(#LhNCUD+$3!*S$bW3VY%O+?C9M+jp z3uwYSt2z9FRpPkDYdLpkci}suevG!f7`Y7X)G@Cw{-*bzvu-x7-{RQD)ruq=W z{jTNjy)jXN;d^H(4oJ|z;V!(A8;@O_{P(KKzUQdfI?&f0*68XY9GMVuJUn1Iz!`(l zHL{TlT*aHqvQ<&g0%|mit3Pc545k{a91O@C*atH{w6AmY9y|-RLNu6<*gbNF2BeBs zzCLp*8Ntnn(k}_lj8h9`>Cxk_w2G#DQxU!@Z`{?HRF#)9x~0b`_%FZdiYHmUQ+Huh zD+I*qgj{QNapz!tjYLi7BN#|>KJvZe*ke**=jeI+SHgq%ZiKDi?kAVNf~?vgZqN2;s(@_Y8;&{$ zFSC-tp`s-9dQ>ZEJYJBm{GqkMeyg28f=W$#Geqm`^OGeY3k1Q4ZlvGXhkDuejHNNA zC&UZq;J64ESnUt8R!EQCYi6Xgykf1QbYh=;F`P zfucvLv&|8IS8DR3h@_@U=m0)c+c_Bo=Rz^n@Uhv=v{295^D7IbO=&Rx02>)6i0)8p zgRvxP2#%@n?Khe7X3O;(Yf5Q9Qj01UcP`mssj~3yc=rGQBR6s3R%}RecVQpb4`_#V z$*bnZmX)N4k^+oM9K>6eMPq7AcHqdTN!;F1t(3$H{S~2_Zz1i6QafM++)$91;t-?g zQxBGS3lt*0B;!o3)n=7c^}s~-$PY}%YfOf$-}RMIhC+9D$d0_L=+LuZA($xbER@Y+ ziz=sqx*C(2Zb^8U_ZXEow}+7+KRSR@6B4cv8kmYp!L1Z-Bm4ukes(icu3hz263lcN z2NUK3n}3b+_dkK%-;okFRkg%*Nn$J48$BNplQKxu683(QYPO0LqnOeUJkRA@bTU(> z3(&`i_BbikI=*3i^@@-fI5^*O8sz zsNE|k@iqa7O#qnVgZ(}Ofg%X~y9igK`|+JT`x208c~%6V-Qw)oLdiU~<>NIq>*3au zKg5;70$K$x7q2}ZGHjRLd+88?Zc|$~qnm9U+3@AXq5=rnjrMNZ1*2g`24N7!UDd45z~**kcFX z$_mTTf`qZ|lnJ`(RI;f`-7q)%Yg@U&E7O0sgTyLu6jLeL3aX*gM`ryso=du;zM{b} zQ^`)sIYH?kkS*_1ii#4c#rG5L+-t% z*C$CnSqAkB#^eP01O%Uq-DlH(2!>KVop1L;_>6ya!a{(;&bK_iWQ_kBsESz8&++4LS5GK34w*E^TMvHx4WSlMM<>`+1Z5B{{oY0!3 z()I?ga>RB2H|*A)jOGZaI(qp0wLr`GQvTFzz7TCukd+-(%2spiR0EnupJvzle+wPj z5YTIA>D-1zSABH)oz=k`2}eU`Eu{TVtZsG+>4O89C|eb<{n||6f+30iJtkRUu1j&` z(oY6-=67(z)J0Z(u0=5xZCB4!Cd0s1zl;HP_J6w44fx~(4*UH+;<&Skk@Mn!-Cq*O zR&Jb;$Sq7&s);W1BH4YipUVb>D(>?Vh23-0=cfrO0l}}du6O{)epvEBZ>awTvMP;E z8!XWmRpamc#7iYU6|ZAKt(2dQ0jd(4Pm_=FVoZO4?DF=5JY>|}1S<1-zhqTHOYduo zfm$AD7b`+KPWkXP4TT7eC>dM{B)LDDMSZtvRcF{=03mh7tv&a}6ZLlr*Xpz}Kz-I0 zXt}P91y0f=Xih9DG;IdAcwO*w5Y#La$|95P`Ky(Gc%8ZL0lkZe|E!)!Ok`(BSn6~{ z_`KG!~ zq7GTrol(R*mvZqP%)~rdBil8!@$1wl*}r_2W7-HG~i!Y3#x!OaAs$OtOX9q2UwoMOLI+ zkKoeh^rsRp*Kj4}ue*cBS1|{ZSe2KKb>g6AhPXGimuCm+r45rh))g4T5()-zly~=m zocg(6AVR#~;eu{n!!%*(hfxkpl6dm@<_UcmADpr?#SzxySV+;Ag~Q)D#bNn(0At}c zUy-b%9oa^Wtef8*0E-6>B3Q);=g;up0DLlAz^hV50xr#S7~`ZV(=EKF5^Zk7N!sua z;@*m?;Em4+Ah|keG$oAL9UdFx2`V7q5fqrOI$sMIzO+%%%skWi>M{3xbdNSXdpPmH zc-*aBz@xwbZt?t(0KbF~@&oZ&sai!ZLyQej4F@KB!&SqQIA#a5 zuIYNCcNujy>6lcRqEf8xTcDnH^Llf8;3}PKz2fP6jejkJhAAItG^WVIqV!=-QheZN zU-=eV=^VtuZ@J2&Dab-OYXX|D(ABCT>zb9H)6v*Xkqcz2w>3Oy>gA158{O9P6R)uy zXz9Bb612*3Y;s1k#unsYTl)Y?t5_a|Ne&!Nacr+lO6}9@V4L91akpcq&hM+@a BIWPbK literal 17660 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 KeSQK10fwM$Q%GO{ diff --git a/application/src/test/resources/lwm2m/credentials/lwm2mserver.jks b/application/src/test/resources/lwm2m/credentials/lwm2mserver.jks index a1923e942f923b8d84a989055ec50c2441f79259..d16967343fe4d56689c12353dc34808625155eec 100644 GIT binary patch delta 6129 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 DGMenJ delta 6113 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 diff --git a/application/src/test/resources/lwm2m/credentials/lwm2mtruststorechain.jks b/application/src/test/resources/lwm2m/credentials/lwm2mtruststorechain.jks index 2e46c718898093e907b646f2aa3051d7309d117c..b97f3629cda96a9c428c2556c4728310d0fbf431 100644 GIT binary patch delta 2678 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 delta 2678 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 /dev/null <<-CONFIG CONFIG echo "====================================================" -echo -e "Generate the root of certificates: \n-${CA_ROOT_KEY}-key.pem (certificate key)\n-${CA_ROOT_KEY}.pem (certificate)\n-${CA_ROOT_KEY}.csr (sign request)" +echo -e "Generate the root of certificates: \n-${CA_ROOT_CERT_KEY}-key.pem (certificate key)\n-${CA_ROOT_CERT_KEY}.pem (certificate)\n-${CA_ROOT_CERT_KEY}.csr (sign request)" echo "====================================================" cfssl genkey \ -initca \ @@ -286,14 +294,114 @@ keytool -importkeystore -deststorepass ${CLIENT_STORE_PWD} -destkeypass ${CLIENT done +#keytool -list -v -keystore ./${CLIENT_PATH}/lwm2mclient.jks -storepass client_ks_password -storetype PKCS12 + +echo "====================================================" +echo -e "Generate the root no trust in ${TRUST_NO_PATH} of certificates: \n-${CA_ROOT_CERT_KEY}-key.pem (certificate key)\n-${CA_ROOT_CERT_KEY}.pem (certificate)\n-${CA_ROOT_CERT_KEY}.csr (sign request)" +echo "====================================================" +cfssl genkey \ + -initca \ + - \ + <<-CONFIG | cfssljson -bare ./${TRUST_NO_PATH}/${CA_ROOT_CERT_KEY} +{ + "CN": "ROOT CA NO TRUST", + "key": { + "algo": "ecdsa", + "size": 256 + }, + "names": [ + { + "C": "UK", + "ST": "Kyiv city", + "L": "Kyiv", + "O": "Thingsboard", + "OU": "DEVELOPER_TEST" + } + ], + "ca": { + "expiry": "131400h" + } +} +CONFIG + +CA_LIST_CERT_FOR_CAT=$(set_list_sert_for_cat ./${TRUST_NO_PATH}/${CA_ROOT_CERT_KEY}.pem) + +echo "====================================================" +echo -e "Generate and Signed the intermediates of our no trust in ${TRUST_NO_PATH} certificate: \n-${CA_INTERMEDIATE_CERT_KEY_PREF}?-key.pem (certificate key)\n-${CA_INTERMEDIATE_CERT_KEY_PREF}?.pem (certificate)\n-${CA_INTERMEDIATE_CERT_KEY_PREF}?.csr (sign request)" +echo "====================================================" + +CA_INTERMEDIATE_CERT_SIGN=${CA_ROOT_CERT_KEY} +CA_LIST_CERT_FOR_CAT="" +CA_INTERMEDIATE_NUMBER=0 +while [[ ${CA_INTERMEDIATE_NUMBER} -lt ${CA_INTERMEDIATE_FINISH} ]]; +do + CA_INTERMEDIATE_CERT_KEY=$(intermediate_common_name) + CA_INTERMEDIATE_NUMBER=$((${CA_INTERMEDIATE_NUMBER} + 1)) + + cfssl gencert \ + -ca ./${TRUST_NO_PATH}/${CA_INTERMEDIATE_CERT_SIGN}.pem \ + -ca-key ./${TRUST_NO_PATH}/${CA_INTERMEDIATE_CERT_SIGN}-key.pem \ + -config ./${TRUST_PATH}/ca-root-to-intermediate-config.json \ + -hostname "${SERVER_HOST_NAME},${SERVER_LOCAL_HOST_NAME}${SERVER_PUBLIC_HOST_NAMES:+, }${SERVER_PUBLIC_HOST_NAMES}" \ + - \ + <<-CONFIG | cfssljson -bare ./${TRUST_NO_PATH}/${CA_INTERMEDIATE_CERT_KEY} + { + "CN": "${CA_INTERMEDIATE_CERT_KEY}_TRUST_NO", + "names": [ + { + "C": "UK", + "ST": "Kyiv city", + "L": "Kyiv", + "O": "Thingsboard", + "OU": "DEVELOPER_TEST" + } + ] + } +CONFIG + #openssl x509 -in ${CA_INTERMEDIATE_CERT_KEY}.pem -text -noout + CA_LIST_CERT_FOR_CAT=$(set_list_sert_for_cat ./${TRUST_NO_PATH}/${CA_INTERMEDIATE_CERT_KEY}.pem) + CA_INTERMEDIATE_CERT_SIGN=${CA_INTERMEDIATE_CERT_KEY} +done + +echo "====================================================" +echo -e "Generate and Signed the client no trust of our certificate: \n-${CLIENT_CERT_TRUST_NO_KEY}-key.pem (certificate key)\n-${CLIENT_CERT_TRUST_NO_KEY}.pem (certificate)\n-${CLIENT_CERT_TRUST_NO_KEY}.csr (sign request)" +echo "====================================================" + + CLIENT_CERT_ALIAS=$(client_alias_name) + CLIENT_NUMBER=$((${CLIENT_NUMBER} + 1)) + + cfssl gencert \ + -ca ./${TRUST_NO_PATH}/${CA_INTERMEDIATE_CERT_KEY}.pem \ + -ca-key ./${TRUST_NO_PATH}/${CA_INTERMEDIATE_CERT_KEY}-key.pem \ + -config ./${TRUST_PATH}/ca-config.json \ + -profile client \ + -hostname "${CLIENT_HOST_NAME}" \ + - \ + <<-CONFIG | cfssljson -bare ./${CLIENT_PATH}/${CLIENT_CERT_TRUST_NO_KEY} +{ + "CN": "${CLIENT_CERT_TRUST_NO_KEY}" +} +CONFIG + +echo "====================================================" +echo -e "Add the client certificate no trust (${CLIENT_CERT_TRUST_NO_KEY}.pem) to keystore: ${CLIENT_JKS_FOR_TEST}.jks" +echo "====================================================" +cat ./${CLIENT_PATH}/${CLIENT_CERT_TRUST_NO_KEY}.pem ${CA_LIST_CERT_FOR_CAT} > ./${CLIENT_PATH}/${CLIENT_CERT_TRUST_NO_KEY}_chain.pem +openssl pkcs12 -export -in ./${CLIENT_PATH}/${CLIENT_CERT_TRUST_NO_KEY}_chain.pem -inkey ./${CLIENT_PATH}/${CLIENT_CERT_TRUST_NO_KEY}-key.pem -out ./${CLIENT_PATH}/${CLIENT_CERT_TRUST_NO_KEY}.p12 -name ${CLIENT_CERT_ALIAS_TRUST_NO} -CAfile ./${TRUST_NO_PATH}/${CA_INTERMEDIATE_CERT_KEY}.pem -caname ${CA_ROOT_NO_ALIAS} -passin pass:${CLIENT_STORE_PWD} -passout pass:${CLIENT_STORE_PWD} +keytool -importkeystore -deststorepass ${CLIENT_STORE_PWD} -destkeypass ${CLIENT_STORE_PWD} -destkeystore ./${CLIENT_PATH}/${CLIENT_JKS_FOR_TEST}.jks -srckeystore ./${CLIENT_PATH}/${CLIENT_CERT_TRUST_NO_KEY}.p12 -srcstoretype PKCS12 -srcstorepass ${CLIENT_STORE_PWD} -alias ${CLIENT_CERT_ALIAS_TRUST_NO} + + + keytool -list -v -keystore ./${CLIENT_PATH}/lwm2mclient.jks -storepass client_ks_password -storetype PKCS12 -rm ./${TRUST_PATH}/*.p12 -rm ./${TRUST_PATH}/*.csr -rm ./${TRUST_PATH}/*.json -rm ./${TRUST_PATH}/${CA_ROOT_CERT_KEY}* -rm ./${TRUST_PATH}/${CA_INTERMEDIATE_CERT_KEY_PREF}* +rm ./${TRUST_PATH}/*.p12 2> /dev/null +rm ./${TRUST_PATH}/*.csr 2> /dev/null +rm ./${TRUST_PATH}/*.json 2> /dev/null +rm ./${TRUST_PATH}/${CA_ROOT_CERT_KEY}* 2> /dev/null +rm ./${TRUST_PATH}/${CA_INTERMEDIATE_CERT_KEY_PREF}* 2> /dev/null + +rm -rf ${TRUST_NO_PATH} 2> /dev/null rm ./${CLIENT_PATH}/*.p12 2> /dev/null rm ./${CLIENT_PATH}/*.csr 2> /dev/null diff --git a/application/src/test/resources/lwm2m/credentials/shell/lwm2m_cfssl_chain_for_test_All.sh b/application/src/test/resources/lwm2m/credentials/shell/lwm2m_cfssl_chain_all_for_test.sh similarity index 78% rename from application/src/test/resources/lwm2m/credentials/shell/lwm2m_cfssl_chain_for_test_All.sh rename to application/src/test/resources/lwm2m/credentials/shell/lwm2m_cfssl_chain_all_for_test.sh index b3b114cb28..c869366ac2 100755 --- a/application/src/test/resources/lwm2m/credentials/shell/lwm2m_cfssl_chain_for_test_All.sh +++ b/application/src/test/resources/lwm2m/credentials/shell/lwm2m_cfssl_chain_all_for_test.sh @@ -27,11 +27,11 @@ Help() } if [ "$1" == "-h" ] ; then - echo -e "Usage 2: ./`basename $0` \"Information is not displayed\" : \"Keys for the server are generated\" : \"Keys for the clients and trusts are generated\"" - echo -e "Usage 1: ./`basename $0` true \"Information is displayed\" : \"Keys for the server are generated\" : \"Keys for the clients and trusts are generated\"" + echo -e "Usage 1: ./`basename $0` \"Information is not displayed\" : \"Keys for the server are generated\" : \"Keys for the clients and trusts are generated\"" + echo -e "Usage 2: ./`basename $0` true \"Information is displayed\" : \"Keys for the server are generated\" : \"Keys for the clients and trusts are generated\"" echo -e "Usage 3: ./`basename $0` true false \"Information is displayed\" : \"Keys for the server are not generated\" : \"Keys for the clients and trusts are generated\"" echo -e "Usage 4: ./`basename $0` true false false \"Information is displayed\" : \"Keys for the server are not generated\" : \"Keys for the clients and trusts are not generated\"" - echo -e "Usage 4: ./`basename $0` true true false \"Information is displayed\" : \"Keys for the server are generated\" : \"Keys for the clients and trusts are not generated\"" + echo -e "Usage 5: ./`basename $0` true true false \"Information is displayed\" : \"Keys for the server are generated\" : \"Keys for the clients and trusts are not generated\"" echo "This Help File: ./`basename $0` -h" exit 0 fi @@ -53,13 +53,13 @@ if [ "$IS_IHFO" = false ] ; then ./lwm2m_cfssl_chain_server_for_test.sh > /dev/null 2>&1 & fi if [ "$IS_TRUST_CLIENT_CREATED_KEY" = true ] ; then - ./lwM2M_cfssl_chain_trusts_and_clients_for_test.sh ${INTERMEDIATE_START} ${INTERMEDIATE_FINISH} ${CLIENT_START} ${CLIENT_FINISH} > /dev/null 2>&1 & + ./lwM2M_cfssl_chain_clients_for_test.sh ${INTERMEDIATE_START} ${INTERMEDIATE_FINISH} ${CLIENT_START} ${CLIENT_FINISH} > /dev/null 2>&1 & fi else if [ "$IS_SERVER_CREATED_KEY" = true ] ; then ./lwm2m_cfssl_chain_server_for_test.sh fi if [ "$IS_TRUST_CLIENT_CREATED_KEY" = true ] ; then - ./lwM2M_cfssl_chain_trusts_and_clients_for_test.sh ${INTERMEDIATE_START} ${INTERMEDIATE_FINISH} ${CLIENT_START} ${CLIENT_FINISH} + ./lwM2M_cfssl_chain_clients_for_test.sh ${INTERMEDIATE_START} ${INTERMEDIATE_FINISH} ${CLIENT_START} ${CLIENT_FINISH} fi fi \ No newline at end of file From be23dd2f7cfa43c1de39730d7a371208da06d454 Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Sun, 9 Jan 2022 20:04:42 +0200 Subject: [PATCH 066/798] lwm2m tests add no trust and clear comments --- .../lwm2m/AbstractLwM2MIntegrationTest.java | 8 -- .../transport/lwm2m/Lwm2mTestHelper.java | 13 ++- .../ota/sql/OtaLwM2MIntegrationTest.java | 4 - .../rpc/AbstractRpcLwM2MIntegrationTest.java | 9 +- .../AbstractSecurityLwM2MIntegrationTest.java | 89 ++----------------- .../sql/NoSecLwM2MIntegrationTest.java | 1 - .../security/sql/PskLwm2mIntegrationTest.java | 9 +- .../security/sql/RpkLwM2MIntegrationTest.java | 1 - .../sql/X509_NoTrustLwM2MIntegrationTest.java | 4 - application/src/test/resources/logback.xml | 1 - 10 files changed, 18 insertions(+), 121 deletions(-) diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java index 0a95dfdbc9..09495402f3 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java @@ -67,7 +67,6 @@ import java.util.concurrent.ScheduledExecutorService; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; -@Slf4j @DaoSqlTest public abstract class AbstractLwM2MIntegrationTest extends AbstractWebsocketTest { @@ -135,7 +134,6 @@ public abstract class AbstractLwM2MIntegrationTest extends AbstractWebsocketTest protected LwM2MTestClient client; private final LwM2MBootstrapClientCredentials defaultBootstrapCredentials; private String[] resources; -// protected String endpoint; public AbstractLwM2MIntegrationTest() { this.defaultBootstrapCredentials = new LwM2MBootstrapClientCredentials(); @@ -197,11 +195,9 @@ public abstract class AbstractLwM2MIntegrationTest extends AbstractWebsocketTest wsClient.waitForReply(); wsClient.registerWaitForUpdate(); -// this.endpoint = endpoint; createNewClient(security, coapConfig, false, endpoint); String msg = wsClient.waitForUpdate(); - log.info("msg5555: [{}]", msg); EntityDataUpdate update = mapper.readValue(msg, EntityDataUpdate.class); Assert.assertEquals(1, update.getCmdId()); List eData = update.getUpdate(); @@ -264,10 +260,6 @@ public abstract class AbstractLwM2MIntegrationTest extends AbstractWebsocketTest this.resources = resources; } -// public void setEndpoint(String endpoint) { -// this.endpoint = endpoint; -// } - public void createNewClient(Security security, NetworkConfig coapConfig, boolean isRpc, String endpoint) throws Exception { clientDestroy(); client = new LwM2MTestClient(this.executor, endpoint); diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/Lwm2mTestHelper.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/Lwm2mTestHelper.java index 0ea700d46c..8dd44d25ae 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/Lwm2mTestHelper.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/Lwm2mTestHelper.java @@ -25,18 +25,16 @@ public class Lwm2mTestHelper { // Server public static final int SECURE_PORT = 5686; public static final int SECURE_PORT_BS = 5688; - public static final String HOST = "localhost"; - public static final String HOST_BS = "localhost"; - public static final NetworkConfig SECURE_COAP_CONFIG = new NetworkConfig().setString("COAP_SECURE_PORT", Integer.toString(SECURE_PORT)); - public static final String ENDPOINT_SECURITY = "deviceAEndpoint"; - public static final String SECURE_URI = "coaps://localhost:" + SECURE_PORT; - public static final int PORT = 5685; public static final int PORT_BS = 5687; + public static final String HOST = "localhost"; + public static final String HOST_BS = "localhost"; public static final int SHORT_SERVER_ID = 123; public static final int SHORT_SERVER_ID_BS = 111; - public static final Security SECURITY = noSec("coap://localhost:" + PORT, SHORT_SERVER_ID); + public static final NetworkConfig SECURE_COAP_CONFIG = new NetworkConfig().setString("COAP_SECURE_PORT", Integer.toString(SECURE_PORT)); + public static final String SECURE_URI = "coaps://" + HOST + ":" + SECURE_PORT; + public static final Security SECURITY = noSec("coap://"+ HOST +":" + PORT, SHORT_SERVER_ID); public static final NetworkConfig COAP_CONFIG = new NetworkConfig().setString("COAP_PORT", Integer.toString(PORT)); // Models @@ -67,5 +65,4 @@ public class Lwm2mTestHelper { public static final String resourceIdName_3_14 = "UtfOffset"; public static final String resourceIdName_19_0_0 = "dataRead"; public static final String resourceIdName_19_1_0 = "dataWrite"; - } diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/ota/sql/OtaLwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/ota/sql/OtaLwM2MIntegrationTest.java index 95a0a774a2..e3c538c928 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/ota/sql/OtaLwM2MIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/ota/sql/OtaLwM2MIntegrationTest.java @@ -138,8 +138,6 @@ public class OtaLwM2MIntegrationTest extends AbstractOtaLwM2MIntegrationTest { @Test public void testFirmwareUpdateWithClientWithoutFirmwareOtaInfoFromProfile() throws Exception { -// String endpoint = "WithoutFirmwareInfoDevice"; -// setEndpoint(endpoint); createDeviceProfile(transportConfiguration); NoSecClientCredential credentials = createNoSecClientCredentials(this.CLIENT_ENDPOINT_WITHOUT_FW_INFO); final Device device = createDevice(credentials); @@ -165,8 +163,6 @@ public class OtaLwM2MIntegrationTest extends AbstractOtaLwM2MIntegrationTest { @Test public void testFirmwareUpdateByObject5() throws Exception { -// String endpoint = "Ota5_Device"; -// setEndpoint(endpoint); createDeviceProfile(OTA_TRANSPORT_CONFIGURATION); NoSecClientCredential credentials = createNoSecClientCredentials(this.CLIENT_ENDPOINT_OTA5); final Device device = createDevice(credentials); diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/AbstractRpcLwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/AbstractRpcLwM2MIntegrationTest.java index 2310fe1659..4303c5e6eb 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/AbstractRpcLwM2MIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/AbstractRpcLwM2MIntegrationTest.java @@ -18,12 +18,10 @@ package org.thingsboard.server.transport.lwm2m.rpc; import org.junit.Before; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.device.credentials.lwm2m.NoSecClientCredential; -import org.thingsboard.server.controller.TbTestWebSocketClient; import org.thingsboard.server.dao.service.DaoSqlTest; import org.thingsboard.server.transport.lwm2m.AbstractLwM2MIntegrationTest; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Predicate; @@ -53,8 +51,6 @@ public abstract class AbstractRpcLwM2MIntegrationTest extends AbstractLwM2MInteg protected String RPC_TRANSPORT_CONFIGURATION; - protected ScheduledExecutorService executor; - protected TbTestWebSocketClient wsClient; protected String deviceId; public Set expectedObjects; public Set expectedObjectIdVers; @@ -73,7 +69,7 @@ public abstract class AbstractRpcLwM2MIntegrationTest extends AbstractLwM2MInteg protected String objectIdVer_50 = "/50"; protected String objectIdVer_3303; protected static AtomicInteger endpointSequence = new AtomicInteger(); - protected static String endpointRpcPref = "deviceEndpointRpc"; + protected static String DEVICE_ENDPOINT_RPC_PREF = "deviceEndpointRpc"; public AbstractRpcLwM2MIntegrationTest(){ setResources(resources); @@ -81,8 +77,7 @@ public abstract class AbstractRpcLwM2MIntegrationTest extends AbstractLwM2MInteg @Before public void beforeTest() throws Exception { - String endpoint = endpointRpcPref + endpointSequence.incrementAndGet(); -// setEndpoint(endpoint); + String endpoint = DEVICE_ENDPOINT_RPC_PREF + endpointSequence.incrementAndGet(); init(); createNewClient (SECURITY, COAP_CONFIG, true, endpoint); diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/AbstractSecurityLwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/AbstractSecurityLwM2MIntegrationTest.java index c10eb46620..c9d2b556af 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/AbstractSecurityLwM2MIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/AbstractSecurityLwM2MIntegrationTest.java @@ -33,36 +33,16 @@ import java.security.cert.X509Certificate; public abstract class AbstractSecurityLwM2MIntegrationTest extends AbstractLwM2MIntegrationTest { protected final String CREDENTIALS_PATH = "lwm2m/credentials/"; // client public key or id used for PSK - protected final String pskIdentity; // client public key or id used for PSK - protected final String pskKey; // client private/secret key used for PSK -// protected final PublicKey clientPublicKey; // client public key used for RPK -// protected final PrivateKey clientPrivateKey; // client private key used for RPK - - - -// // client certificate signed by rootCA but with bad CN (CN does not start by leshan_integration_test) -// protected final X509Certificate clientX509CertWithBadCN; -// // client certificate self-signed with a good CN (CN start by leshan_integration_test) -// protected final X509Certificate clientX509CertSelfSigned; -// // client certificate signed by another CA (not rootCA) with a good CN (CN start by leshan_integration_test) -// protected final X509Certificate clientX509CertNotTrusted; - - // self-signed server certificate -// protected final X509Certificate serverX509CertSelfSigned; -// // rootCA used by the server -// protected final X509Certificate rootCAX509Cert; - // certificates trustedby the server (should contain rootCA) + // Get keys PSK + protected final String CLIENT_PSK_IDENTITY = "SOME_PSK_ID"; // client public key or id used for PSK + protected final String CLIENT_PSK_KEY = "73656372657450534b73656372657450"; // client private/secret key used for PSK // Server protected static final String SERVER_JKS_FOR_TEST = "lwm2mserver"; protected static final String SERVER_STORE_PWD = "server_ks_password"; protected static final String SERVER_CERT_ALIAS = "server"; - protected final X509Certificate serverX509Cert; // server certificate signed by rootCA -// protected final PrivateKey serverPrivateKeyFromCert; // server private key used for RPK and X509 - protected final PublicKey serverPublicKeyFromCert; // server public key used for RPK - -// // Server Trust -// protected final Certificate[] trustedCertificates = new Certificate[1]; +protected final X509Certificate serverX509Cert; // server certificate signed by rootCA + protected final PublicKey serverPublicKeyFromCert; // server public key used for RPK // Client protected LwM2MTestClient client; @@ -92,45 +72,18 @@ public abstract class AbstractSecurityLwM2MIntegrationTest extends AbstractLwM2M public AbstractSecurityLwM2MIntegrationTest() { // create client credentials setResources(this.resources); -// setEndpoint(CLIENT_ENDPOINT_NO_TRUST); try { -// Get keys PSK - this.pskIdentity = "SOME_PSK_ID"; - this.pskKey = "73656372657450534b73656372657450"; - -// // Get point values -// byte[] publicX = Hex -// .decodeHex("89c048261979208666f2bfb188be1968fc9021c416ce12828c06f4e314c167b5".toCharArray()); -// byte[] publicY = Hex -// .decodeHex("cbf1eb7587f08e01688d9ada4be859137ca49f79394bad9179326b3090967b68".toCharArray()); -// byte[] privateS = Hex -// .decodeHex("e67b68d2aaeb6550f19d98cade3ad62b39532e02e6b422e1f7ea189dabaea5d2".toCharArray()); -// -// // Get Elliptic Curve Parameter spec for secp256r1 -// AlgorithmParameters algoParameters = AlgorithmParameters.getInstance("EC"); -// algoParameters.init(new ECGenParameterSpec("secp256r1")); -// ECParameterSpec parameterSpec = algoParameters.getParameterSpec(ECParameterSpec.class); -// -// // Create key specs -// KeySpec publicKeySpec = new ECPublicKeySpec(new ECPoint(new BigInteger(publicX), new BigInteger(publicY)), -// parameterSpec); -// KeySpec privateKeySpec = new ECPrivateKeySpec(new BigInteger(privateS), parameterSpec); -// -// // Get keys RPK -// clientPublicKey = KeyFactory.getInstance("EC").generatePublic(publicKeySpec); -// clientPrivateKey = KeyFactory.getInstance("EC").generatePrivate(privateKeySpec); - // Get certificates from key store char[] clientKeyStorePwd = CLIENT_STORE_PWD.toCharArray(); KeyStore clientKeyStore = KeyStore.getInstance(KeyStore.getDefaultType()); try (InputStream clientKeyStoreFile = this.getClass().getClassLoader().getResourceAsStream(CREDENTIALS_PATH + CLIENT_JKS_FOR_TEST + ".jks")) { clientKeyStore.load(clientKeyStoreFile, clientKeyStorePwd); } - + // Trust clientPrivateKeyFromCertTrust = (PrivateKey) clientKeyStore.getKey(CLIENT_ALIAS_CERT_TRUST, clientKeyStorePwd); clientX509CertTrust = (X509Certificate) clientKeyStore.getCertificate(CLIENT_ALIAS_CERT_TRUST); clientPublicKeyFromCertTrust = clientX509CertTrust != null ? clientX509CertTrust.getPublicKey() : null; - + // No trust clientPrivateKeyFromCertTrustNo = (PrivateKey) clientKeyStore.getKey(CLIENT_ALIAS_CERT_TRUST_NO, clientKeyStorePwd); clientX509CertTrustNo = (X509Certificate) clientKeyStore.getCertificate(CLIENT_ALIAS_CERT_TRUST_NO); clientPublicKeyFromCertTrustNo = clientX509CertTrustNo != null ? clientX509CertTrustNo.getPublicKey() : null; @@ -141,29 +94,6 @@ public abstract class AbstractSecurityLwM2MIntegrationTest extends AbstractLwM2M // create server credentials try { -// // Get point values -// byte[] publicX = Hex -// .decodeHex("fcc28728c123b155be410fc1c0651da374fc6ebe7f96606e90d927d188894a73".toCharArray()); -// byte[] publicY = Hex -// .decodeHex("d2ffaa73957d76984633fc1cc54d0b763ca0559a9dff9706e9f4557dacc3f52a".toCharArray()); -// byte[] privateS = Hex -// .decodeHex("1dae121ba406802ef07c193c1ee4df91115aabd79c1ed7f4c0ef7ef6a5449400".toCharArray()); -// -// // Get Elliptic Curve Parameter spec for secp256r1 -// AlgorithmParameters algoParameters = AlgorithmParameters.getInstance("EC"); -// algoParameters.init(new ECGenParameterSpec("secp256r1")); -// ECParameterSpec parameterSpec = algoParameters.getParameterSpec(ECParameterSpec.class); -// -// // Create key specs -// KeySpec publicKeySpec = new ECPublicKeySpec(new ECPoint(new BigInteger(publicX), new BigInteger(publicY)), -// parameterSpec); -// KeySpec privateKeySpec = new ECPrivateKeySpec(new BigInteger(privateS), parameterSpec); -// -// // Get keys -// serverPublicKey = KeyFactory.getInstance("EC").generatePublic(publicKeySpec); -// serverPrivateKey = KeyFactory.getInstance("EC").generatePrivate(privateKeySpec); - - // Get certificates from key store char[] serverKeyStorePwd = SERVER_STORE_PWD.toCharArray(); KeyStore serverKeyStore = KeyStore.getInstance(KeyStore.getDefaultType()); @@ -171,13 +101,8 @@ public abstract class AbstractSecurityLwM2MIntegrationTest extends AbstractLwM2M serverKeyStore.load(serverKeyStoreFile, serverKeyStorePwd); } -// serverPrivateKeyFromCert = (PrivateKey) serverKeyStore.getKey("server", serverKeyStorePwd); serverX509Cert = (X509Certificate) serverKeyStore.getCertificate(SERVER_CERT_ALIAS); serverPublicKeyFromCert = serverX509Cert.getPublicKey(); -// rootCAX509Cert = (X509Certificate) serverKeyStore.getCertificate("rootCA"); - -// serverX509CertSelfSigned = (X509Certificate) serverKeyStore.getCertificate("server_self_signed"); -// trustedCertificates[0] = serverX509Cert; } catch (GeneralSecurityException | IOException e) { throw new RuntimeException(e); } diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/NoSecLwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/NoSecLwM2MIntegrationTest.java index 8331b99fff..c3af57ae71 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/NoSecLwM2MIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/NoSecLwM2MIntegrationTest.java @@ -16,7 +16,6 @@ package org.thingsboard.server.transport.lwm2m.security.sql; import lombok.extern.slf4j.Slf4j; -import org.junit.Ignore; import org.junit.Test; import org.thingsboard.server.common.data.device.credentials.lwm2m.NoSecClientCredential; import org.thingsboard.server.transport.lwm2m.security.AbstractSecurityLwM2MIntegrationTest; diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/PskLwm2mIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/PskLwm2mIntegrationTest.java index d7296fd47c..52a85ecf2a 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/PskLwm2mIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/PskLwm2mIntegrationTest.java @@ -17,7 +17,6 @@ package org.thingsboard.server.transport.lwm2m.security.sql; import org.eclipse.leshan.client.object.Security; import org.eclipse.leshan.core.util.Hex; -import org.junit.Ignore; import org.junit.Test; import org.thingsboard.server.common.data.device.credentials.lwm2m.PSKClientCredential; import org.thingsboard.server.transport.lwm2m.security.AbstractSecurityLwM2MIntegrationTest; @@ -35,12 +34,12 @@ public class PskLwm2mIntegrationTest extends AbstractSecurityLwM2MIntegrationTes public void testConnectWithPSKAndObserveTelemetry() throws Exception { PSKClientCredential clientCredentials = new PSKClientCredential(); clientCredentials.setEndpoint(CLIENT_ENDPOINT_PSK); - clientCredentials.setKey(pskKey); - clientCredentials.setIdentity(pskIdentity); + clientCredentials.setKey(CLIENT_PSK_KEY); + clientCredentials.setIdentity(CLIENT_PSK_IDENTITY); Security security = psk(SECURE_URI, SHORT_SERVER_ID, - pskIdentity.getBytes(StandardCharsets.UTF_8), - Hex.decodeHex(pskKey.toCharArray())); + CLIENT_PSK_IDENTITY.getBytes(StandardCharsets.UTF_8), + Hex.decodeHex(CLIENT_PSK_KEY.toCharArray())); super.basicTestConnectionObserveTelemetry(security, clientCredentials, SECURE_COAP_CONFIG, CLIENT_ENDPOINT_PSK); } } diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/RpkLwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/RpkLwM2MIntegrationTest.java index 0066014a9b..05933019bf 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/RpkLwM2MIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/RpkLwM2MIntegrationTest.java @@ -16,7 +16,6 @@ package org.thingsboard.server.transport.lwm2m.security.sql; import org.eclipse.leshan.client.object.Security; -import org.junit.Ignore; import org.junit.Test; import org.thingsboard.server.common.data.device.credentials.lwm2m.RPKClientCredential; import org.thingsboard.server.transport.lwm2m.security.AbstractSecurityLwM2MIntegrationTest; diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/X509_NoTrustLwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/X509_NoTrustLwM2MIntegrationTest.java index b2ce6c470d..af7282bdb7 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/X509_NoTrustLwM2MIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/X509_NoTrustLwM2MIntegrationTest.java @@ -15,9 +15,7 @@ */ package org.thingsboard.server.transport.lwm2m.security.sql; -import org.apache.commons.codec.binary.Base64; import org.eclipse.leshan.client.object.Security; -import org.junit.Ignore; import org.junit.Test; import org.thingsboard.server.common.data.device.credentials.lwm2m.X509ClientCredential; import org.thingsboard.server.common.transport.util.SslUtil; @@ -34,7 +32,6 @@ public class X509_NoTrustLwM2MIntegrationTest extends AbstractSecurityLwM2MInteg public void testConnectWithCertAndObserveTelemetry() throws Exception { X509ClientCredential credentials = new X509ClientCredential(); credentials.setEndpoint(CLIENT_ENDPOINT_X509_TRUST_NO); -// rpkClientCredentials.setKey(new String(Base64.encodeBase64(clientPublicKeyFromCertTrust.getEncoded()))); credentials.setCert(SslUtil.getCertificateString(clientX509CertTrustNo)); Security security = x509(SECURE_URI, SHORT_SERVER_ID, @@ -43,5 +40,4 @@ public class X509_NoTrustLwM2MIntegrationTest extends AbstractSecurityLwM2MInteg serverX509Cert.getEncoded()); super.basicTestConnectionObserveTelemetry(security, credentials, SECURE_COAP_CONFIG, CLIENT_ENDPOINT_X509_TRUST_NO); } - } diff --git a/application/src/test/resources/logback.xml b/application/src/test/resources/logback.xml index 175eda993c..d3301bf660 100644 --- a/application/src/test/resources/logback.xml +++ b/application/src/test/resources/logback.xml @@ -10,7 +10,6 @@ - From d3987d1c670350d68cdf8578ad54051c897b0869 Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Mon, 10 Jan 2022 12:58:32 +0200 Subject: [PATCH 067/798] device actor checkSessionsTimeout refactored to improve performance and reduce memory pressure --- .../device/DeviceActorMessageProcessor.java | 49 ++++++++++++++----- 1 file changed, 37 insertions(+), 12 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java b/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java index 369d371286..d8f0c38975 100644 --- a/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java +++ b/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java @@ -97,6 +97,7 @@ import javax.annotation.Nullable; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; +import java.util.ConcurrentModificationException; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; @@ -116,6 +117,7 @@ import java.util.stream.Collectors; @Slf4j class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { + static final String SESSION_TIMEOUT_MESSAGE = "session timeout!"; final TenantId tenantId; final DeviceId deviceId; final LinkedHashMapRemoveEldest sessions; @@ -961,19 +963,42 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { } void checkSessionsTimeout() { - log.debug("[{}] checkSessionsTimeout started. Size before check {}", deviceId, sessions.size()); - long expTime = System.currentTimeMillis() - systemContext.getSessionInactivityTimeout(); - Map sessionsToRemove = sessions.entrySet().stream().filter(kv -> kv.getValue().getLastActivityTime() < expTime).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); - sessionsToRemove.forEach((sessionId, sessionMD) -> { - sessions.remove(sessionId); - rpcSubscriptions.remove(sessionId); - attributeSubscriptions.remove(sessionId); - notifyTransportAboutClosedSession(sessionId, sessionMD, "session timeout!"); - }); - if (!sessionsToRemove.isEmpty()) { - dumpSessions(); + final long expTime = System.currentTimeMillis() - systemContext.getSessionInactivityTimeout(); + List expiredIds = null; + + try { + for (Map.Entry kv : sessions.entrySet()) { //entry set are cached for stable sessions + if (kv.getValue().getLastActivityTime() < expTime) { + final UUID id = kv.getKey(); + if (expiredIds == null) { + expiredIds = new ArrayList<>(1); //most of the expired sessions is a single event + } + expiredIds.add(id); + + } + } + } catch (ConcurrentModificationException ignored) { + //Sessions are not thread safe and possible exceptions + //It is an extremely rare event + //Complete session check will perform on the next check + } + + if (expiredIds != null) { + int removed = 0; + for (UUID id : expiredIds) { + final SessionInfoMetaData session = sessions.remove(id); + rpcSubscriptions.remove(id); + attributeSubscriptions.remove(id); + if (session != null) { + removed++; + notifyTransportAboutClosedSession(id, session, SESSION_TIMEOUT_MESSAGE); + } + } + if (removed != 0) { + dumpSessions(); + } } - log.debug("[{}] checkSessionsTimeout finished. Size after check {}", deviceId, sessions.size()); + } } From 6f19efd5397279e44ea76d5c73edb1136f22fc2d Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Mon, 10 Jan 2022 13:07:07 +0200 Subject: [PATCH 068/798] TenantId for SYS_TENANT_ID refactored (new instances replaced with constant) --- .../java/org/thingsboard/server/actors/app/AppActor.java | 2 +- .../thingsboard/server/service/install/DatabaseHelper.java | 2 +- .../thingsboard/server/service/install/InstallScripts.java | 4 ++-- .../thingsboard/server/service/mail/DefaultMailService.java | 2 +- .../auth/jwt/RefreshTokenAuthenticationProvider.java | 4 ++-- .../thingsboard/server/service/sms/DefaultSmsService.java | 2 +- .../server/queue/discovery/HashPartitionService.java | 2 +- .../dao/component/BaseComponentDescriptorService.java | 2 +- .../server/dao/device/DeviceCredentialsServiceImpl.java | 2 +- .../thingsboard/server/dao/tenant/TenantServiceImpl.java | 6 +++--- .../thingsboard/server/dao/service/AbstractServiceTest.java | 2 +- .../server/dao/sql/widget/JpaWidgetsBundleDaoTest.java | 2 +- 12 files changed, 16 insertions(+), 16 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/actors/app/AppActor.java b/application/src/main/java/org/thingsboard/server/actors/app/AppActor.java index 223fb94937..aac0220d07 100644 --- a/application/src/main/java/org/thingsboard/server/actors/app/AppActor.java +++ b/application/src/main/java/org/thingsboard/server/actors/app/AppActor.java @@ -222,7 +222,7 @@ public class AppActor extends ContextAwareActor { @Override public TbActorId createActorId() { - return new TbEntityActorId(new TenantId(EntityId.NULL_UUID)); + return new TbEntityActorId(TenantId.SYS_TENANT_ID); } @Override diff --git a/application/src/main/java/org/thingsboard/server/service/install/DatabaseHelper.java b/application/src/main/java/org/thingsboard/server/service/install/DatabaseHelper.java index f9ea9afa67..44828c3630 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/DatabaseHelper.java +++ b/application/src/main/java/org/thingsboard/server/service/install/DatabaseHelper.java @@ -99,7 +99,7 @@ public class DatabaseHelper { } } for (CustomerId customerId : customerIds) { - dashboardService.assignDashboardToCustomer(new TenantId(EntityId.NULL_UUID), dashboardId, customerId); + dashboardService.assignDashboardToCustomer(TenantId.SYS_TENANT_ID, dashboardId, customerId); } }); } diff --git a/application/src/main/java/org/thingsboard/server/service/install/InstallScripts.java b/application/src/main/java/org/thingsboard/server/service/install/InstallScripts.java index d5a4365963..f4e046bcd5 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/InstallScripts.java +++ b/application/src/main/java/org/thingsboard/server/service/install/InstallScripts.java @@ -169,7 +169,7 @@ public class InstallScripts { ruleChain = ruleChainService.saveRuleChain(ruleChain); ruleChainMetaData.setRuleChainId(ruleChain.getId()); - ruleChainService.saveRuleChainMetaData(new TenantId(EntityId.NULL_UUID), ruleChainMetaData); + ruleChainService.saveRuleChainMetaData(TenantId.SYS_TENANT_ID, ruleChainMetaData); return ruleChain; } @@ -217,7 +217,7 @@ public class InstallScripts { dashboard.setTenantId(tenantId); Dashboard savedDashboard = dashboardService.saveDashboard(dashboard); if (customerId != null && !customerId.isNullUid()) { - dashboardService.assignDashboardToCustomer(new TenantId(EntityId.NULL_UUID), savedDashboard.getId(), customerId); + dashboardService.assignDashboardToCustomer(TenantId.SYS_TENANT_ID, savedDashboard.getId(), customerId); } } catch (Exception e) { log.error("Unable to load dashboard from json: [{}]", path.toString()); diff --git a/application/src/main/java/org/thingsboard/server/service/mail/DefaultMailService.java b/application/src/main/java/org/thingsboard/server/service/mail/DefaultMailService.java index 0b261436b4..f18fae4ef2 100644 --- a/application/src/main/java/org/thingsboard/server/service/mail/DefaultMailService.java +++ b/application/src/main/java/org/thingsboard/server/service/mail/DefaultMailService.java @@ -95,7 +95,7 @@ public class DefaultMailService implements MailService { @Override public void updateMailConfiguration() { - AdminSettings settings = adminSettingsService.findAdminSettingsByKey(new TenantId(EntityId.NULL_UUID), "mail"); + AdminSettings settings = adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, "mail"); if (settings != null) { JsonNode jsonConfig = settings.getJsonValue(); mailSender = createMailSender(jsonConfig); diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/jwt/RefreshTokenAuthenticationProvider.java b/application/src/main/java/org/thingsboard/server/service/security/auth/jwt/RefreshTokenAuthenticationProvider.java index 7899c15b70..428fe8adc8 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/jwt/RefreshTokenAuthenticationProvider.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/jwt/RefreshTokenAuthenticationProvider.java @@ -75,7 +75,7 @@ public class RefreshTokenAuthenticationProvider implements AuthenticationProvide } private SecurityUser authenticateByUserId(UserId userId) { - TenantId systemId = new TenantId(EntityId.NULL_UUID); + TenantId systemId = TenantId.SYS_TENANT_ID; User user = userService.findUserById(systemId, userId); if (user == null) { throw new UsernameNotFoundException("User not found by refresh token"); @@ -99,7 +99,7 @@ public class RefreshTokenAuthenticationProvider implements AuthenticationProvide } private SecurityUser authenticateByPublicId(String publicId) { - TenantId systemId = new TenantId(EntityId.NULL_UUID); + TenantId systemId = TenantId.SYS_TENANT_ID; CustomerId customerId; try { customerId = new CustomerId(UUID.fromString(publicId)); diff --git a/application/src/main/java/org/thingsboard/server/service/sms/DefaultSmsService.java b/application/src/main/java/org/thingsboard/server/service/sms/DefaultSmsService.java index f0b3eb174e..7fdda75144 100644 --- a/application/src/main/java/org/thingsboard/server/service/sms/DefaultSmsService.java +++ b/application/src/main/java/org/thingsboard/server/service/sms/DefaultSmsService.java @@ -71,7 +71,7 @@ public class DefaultSmsService implements SmsService { @Override public void updateSmsConfiguration() { - AdminSettings settings = adminSettingsService.findAdminSettingsByKey(new TenantId(EntityId.NULL_UUID), "sms"); + AdminSettings settings = adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, "sms"); if (settings != null) { try { JsonNode jsonConfig = settings.getJsonValue(); diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/discovery/HashPartitionService.java b/common/queue/src/main/java/org/thingsboard/server/queue/discovery/HashPartitionService.java index 06c635761c..1e657aac29 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/discovery/HashPartitionService.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/discovery/HashPartitionService.java @@ -279,7 +279,7 @@ public class HashPartitionService implements PartitionService { tpi.tenantId(tenantId); myPartitionsSearchKey = new ServiceQueueKey(serviceQueue, tenantId); } else { - myPartitionsSearchKey = new ServiceQueueKey(serviceQueue, new TenantId(TenantId.NULL_UUID)); + myPartitionsSearchKey = new ServiceQueueKey(serviceQueue, TenantId.SYS_TENANT_ID); } List partitions = myPartitions.get(myPartitionsSearchKey); if (partitions != null) { diff --git a/dao/src/main/java/org/thingsboard/server/dao/component/BaseComponentDescriptorService.java b/dao/src/main/java/org/thingsboard/server/dao/component/BaseComponentDescriptorService.java index c00c5fbe56..ac92aa3f67 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/component/BaseComponentDescriptorService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/component/BaseComponentDescriptorService.java @@ -51,7 +51,7 @@ public class BaseComponentDescriptorService implements ComponentDescriptorServic @Override public ComponentDescriptor saveComponent(TenantId tenantId, ComponentDescriptor component) { - componentValidator.validate(component, data -> new TenantId(EntityId.NULL_UUID)); + componentValidator.validate(component, data -> TenantId.SYS_TENANT_ID); Optional result = componentDescriptorDao.saveIfNotExist(tenantId, component); return result.orElseGet(() -> componentDescriptorDao.findByClazz(tenantId, component.getClazz())); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceCredentialsServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceCredentialsServiceImpl.java index e131dd34a4..28ab143d6a 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceCredentialsServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceCredentialsServiceImpl.java @@ -73,7 +73,7 @@ public class DeviceCredentialsServiceImpl extends AbstractEntityService implemen public DeviceCredentials findDeviceCredentialsByCredentialsId(String credentialsId) { log.trace("Executing findDeviceCredentialsByCredentialsId [{}]", credentialsId); validateString(credentialsId, "Incorrect credentialsId " + credentialsId); - return deviceCredentialsDao.findByCredentialsId(new TenantId(EntityId.NULL_UUID), credentialsId); + return deviceCredentialsDao.findByCredentialsId(TenantId.SYS_TENANT_ID, credentialsId); } @Override diff --git a/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java index 7314f54b39..4ccaa4e5c9 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java @@ -168,20 +168,20 @@ public class TenantServiceImpl extends AbstractEntityService implements TenantSe public PageData findTenants(PageLink pageLink) { log.trace("Executing findTenants pageLink [{}]", pageLink); Validator.validatePageLink(pageLink); - return tenantDao.findTenantsByRegion(new TenantId(EntityId.NULL_UUID), DEFAULT_TENANT_REGION, pageLink); + return tenantDao.findTenantsByRegion(TenantId.SYS_TENANT_ID, DEFAULT_TENANT_REGION, pageLink); } @Override public PageData findTenantInfos(PageLink pageLink) { log.trace("Executing findTenantInfos pageLink [{}]", pageLink); Validator.validatePageLink(pageLink); - return tenantDao.findTenantInfosByRegion(new TenantId(EntityId.NULL_UUID), DEFAULT_TENANT_REGION, pageLink); + return tenantDao.findTenantInfosByRegion(TenantId.SYS_TENANT_ID, DEFAULT_TENANT_REGION, pageLink); } @Override public void deleteTenants() { log.trace("Executing deleteTenants"); - tenantsRemover.removeEntities(new TenantId(EntityId.NULL_UUID), DEFAULT_TENANT_REGION); + tenantsRemover.removeEntities(TenantId.SYS_TENANT_ID, DEFAULT_TENANT_REGION); } private DataValidator tenantValidator = diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/AbstractServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/AbstractServiceTest.java index 2d4fd72179..c99beb9251 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/AbstractServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/AbstractServiceTest.java @@ -86,7 +86,7 @@ public abstract class AbstractServiceTest { protected ObjectMapper mapper = new ObjectMapper(); - public static final TenantId SYSTEM_TENANT_ID = new TenantId(EntityId.NULL_UUID); + public static final TenantId SYSTEM_TENANT_ID = TenantId.SYS_TENANT_ID; @Autowired protected UserService userService; diff --git a/dao/src/test/java/org/thingsboard/server/dao/sql/widget/JpaWidgetsBundleDaoTest.java b/dao/src/test/java/org/thingsboard/server/dao/sql/widget/JpaWidgetsBundleDaoTest.java index 7aa1521978..11e2a47af0 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/sql/widget/JpaWidgetsBundleDaoTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/sql/widget/JpaWidgetsBundleDaoTest.java @@ -158,7 +158,7 @@ public class JpaWidgetsBundleDaoTest extends AbstractJpaDaoTest { WidgetsBundle widgetsBundle = new WidgetsBundle(); widgetsBundle.setAlias(prefix + i); widgetsBundle.setTitle(prefix + i); - widgetsBundle.setTenantId(new TenantId(NULL_UUID)); + widgetsBundle.setTenantId(TenantId.SYS_TENANT_ID); widgetsBundle.setId(new WidgetsBundleId(Uuids.timeBased())); widgetsBundleDao.save(AbstractServiceTest.SYSTEM_TENANT_ID, widgetsBundle); } From 48ac1a256e102bcb9bc62a60b3a2ffbf2a6d05a8 Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Mon, 10 Jan 2022 13:39:54 +0200 Subject: [PATCH 069/798] TenantId factory method fromUUID added to reduce optimize TenantId instances count in heap memory. Factory as default @JsonCreator --- .../server/common/data/id/TenantId.java | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/id/TenantId.java b/common/data/src/main/java/org/thingsboard/server/common/data/id/TenantId.java index a785ac03d5..efba010507 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/id/TenantId.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/id/TenantId.java @@ -15,23 +15,31 @@ */ package org.thingsboard.server.common.data.id; -import java.util.UUID; - import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; import io.swagger.annotations.ApiModelProperty; +import org.springframework.util.ConcurrentReferenceHashMap; +import org.springframework.util.ConcurrentReferenceHashMap.ReferenceType; import org.thingsboard.server.common.data.EntityType; +import java.util.UUID; + public final class TenantId extends UUIDBased implements EntityId { @JsonIgnore public static final TenantId SYS_TENANT_ID = new TenantId(EntityId.NULL_UUID); + static final ConcurrentReferenceHashMap tenants = new ConcurrentReferenceHashMap<>(16, ReferenceType.SOFT); + private static final long serialVersionUID = 1L; @JsonCreator - public TenantId(@JsonProperty("id") UUID id) { + public static TenantId fromUUID(@JsonProperty("id") UUID id) { + return tenants.computeIfAbsent(id, TenantId::new); + } + + public TenantId(UUID id) { super(id); } From 7d78437258ff55f4dc94ea0dfb3f25a1154ca333 Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Mon, 10 Jan 2022 13:55:25 +0200 Subject: [PATCH 070/798] TenantId new instance replaced with factory method call fromUUID --- .../server/actors/app/AppActor.java | 2 +- .../server/controller/BaseController.java | 2 +- .../controller/DashboardController.java | 2 +- .../server/controller/DeviceController.java | 2 +- .../server/controller/EventController.java | 6 +++--- .../server/controller/TenantController.java | 6 +++--- .../server/controller/UserController.java | 2 +- .../controller/WidgetTypeController.java | 2 +- .../DefaultTbApiUsageStateService.java | 2 +- .../edge/DefaultEdgeNotificationService.java | 2 +- .../rpc/processor/TelemetryEdgeProcessor.java | 4 ++-- .../ota/DefaultOtaPackageStateService.java | 2 +- .../queue/DefaultTbClusterService.java | 2 +- .../queue/DefaultTbCoreConsumerService.java | 12 +++++------ .../DefaultTbRuleEngineConsumerService.java | 6 +++--- ...lByTenantIdTbRuleEngineSubmitStrategy.java | 2 +- .../service/security/AccessValidator.java | 2 +- .../security/model/token/JwtTokenFactory.java | 2 +- .../state/DefaultDeviceStateService.java | 2 +- .../DefaultRuleEngineStatisticsService.java | 2 +- .../subscription/TbSubscriptionUtils.java | 6 +++--- .../transport/DefaultTransportApiService.java | 8 ++++---- .../msg/TransportToDeviceActorMsgWrapper.java | 2 +- .../sql/BaseTbResourceServiceTest.java | 2 +- .../common/data/id/EntityIdFactory.java | 2 +- .../server/common/data/id/TenantId.java | 3 ++- .../DefaultTbServiceInfoProvider.java | 2 +- .../queue/discovery/HashPartitionService.java | 2 +- .../lwm2m/server/client/LwM2mClient.java | 2 +- .../uplink/DefaultLwM2mUplinkMsgHandler.java | 4 ++-- .../service/DefaultTransportService.java | 12 +++++------ .../server/dao/entity/BaseEntityService.java | 2 +- .../server/dao/model/ModelConstants.java | 2 +- .../dao/model/sql/AbstractAlarmEntity.java | 2 +- .../dao/model/sql/AbstractAssetEntity.java | 2 +- .../dao/model/sql/AbstractDeviceEntity.java | 2 +- .../dao/model/sql/AbstractEdgeEntity.java | 2 +- .../model/sql/AbstractEntityViewEntity.java | 2 +- .../dao/model/sql/AbstractTenantEntity.java | 2 +- .../model/sql/AbstractWidgetTypeEntity.java | 2 +- .../dao/model/sql/ApiUsageStateEntity.java | 2 +- .../server/dao/model/sql/AuditLogEntity.java | 2 +- .../server/dao/model/sql/CustomerEntity.java | 2 +- .../server/dao/model/sql/DashboardEntity.java | 2 +- .../dao/model/sql/DashboardInfoEntity.java | 2 +- .../dao/model/sql/DeviceProfileEntity.java | 2 +- .../server/dao/model/sql/EdgeEventEntity.java | 2 +- .../dao/model/sql/EntityAlarmEntity.java | 2 +- .../server/dao/model/sql/EventEntity.java | 2 +- .../dao/model/sql/OAuth2ParamsEntity.java | 2 +- .../dao/model/sql/OtaPackageEntity.java | 2 +- .../dao/model/sql/OtaPackageInfoEntity.java | 2 +- .../server/dao/model/sql/RpcEntity.java | 2 +- .../server/dao/model/sql/RuleChainEntity.java | 2 +- .../dao/model/sql/TbResourceEntity.java | 2 +- .../dao/model/sql/TbResourceInfoEntity.java | 2 +- .../server/dao/model/sql/UserEntity.java | 2 +- .../dao/model/sql/WidgetsBundleEntity.java | 2 +- .../dao/resource/BaseResourceService.java | 2 +- .../server/dao/sql/asset/JpaAssetDao.java | 2 +- .../server/dao/sql/device/JpaDeviceDao.java | 2 +- .../server/dao/sql/edge/JpaEdgeDao.java | 2 +- .../dao/sql/entityview/JpaEntityViewDao.java | 2 +- .../dao/sql/query/AlarmDataAdapter.java | 2 +- .../server/dao/sql/tenant/JpaTenantDao.java | 2 +- .../server/dao/tenant/TenantServiceImpl.java | 2 +- .../server/dao/user/UserServiceImpl.java | 2 +- .../dao/widget/WidgetTypeServiceImpl.java | 2 +- .../dao/widget/WidgetsBundleServiceImpl.java | 2 +- .../nosql/CassandraPartitionsCacheTest.java | 2 +- .../dao/service/AbstractServiceTest.java | 2 +- .../dao/service/BaseAssetServiceTest.java | 2 +- .../dao/service/BaseCustomerServiceTest.java | 2 +- .../dao/service/BaseDashboardServiceTest.java | 2 +- .../dao/service/BaseDeviceServiceTest.java | 2 +- .../dao/service/BaseEdgeEventServiceTest.java | 6 +++--- .../dao/service/BaseEdgeServiceTest.java | 2 +- .../service/BaseOtaPackageServiceTest.java | 2 +- .../dao/service/BaseRuleChainServiceTest.java | 2 +- .../service/BaseWidgetTypeServiceTest.java | 4 ++-- .../service/BaseWidgetsBundleServiceTest.java | 12 +++++------ .../service/event/BaseEventServiceTest.java | 4 ++-- .../server/dao/sql/alarm/JpaAlarmDaoTest.java | 8 ++++---- .../server/dao/sql/asset/JpaAssetDaoTest.java | 6 +++--- .../dao/sql/customer/JpaCustomerDaoTest.java | 4 ++-- .../dashboard/JpaDashboardInfoDaoTest.java | 2 +- .../dao/sql/device/JpaDeviceDaoTest.java | 20 +++++++++---------- .../dao/sql/event/JpaBaseEventDaoTest.java | 10 +++++----- .../dao/sql/tenant/JpaTenantDaoTest.java | 2 +- .../server/dao/sql/user/JpaUserDaoTest.java | 4 ++-- .../sql/widget/JpaWidgetsBundleDaoTest.java | 2 +- .../engine/action/TbCreateRelationNode.java | 2 +- .../rule/engine/action/TbAlarmNodeTest.java | 2 +- .../metadata/AbstractAttributeNodeTest.java | 2 +- .../profile/TbDeviceProfileNodeTest.java | 2 +- 95 files changed, 148 insertions(+), 147 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/actors/app/AppActor.java b/application/src/main/java/org/thingsboard/server/actors/app/AppActor.java index aac0220d07..d64456fba8 100644 --- a/application/src/main/java/org/thingsboard/server/actors/app/AppActor.java +++ b/application/src/main/java/org/thingsboard/server/actors/app/AppActor.java @@ -160,7 +160,7 @@ public class AppActor extends ContextAwareActor { } } else { if (EntityType.TENANT.equals(msg.getEntityId().getEntityType())) { - TenantId tenantId = new TenantId(msg.getEntityId().getId()); + TenantId tenantId = TenantId.fromUUID(msg.getEntityId().getId()); if (msg.getEvent() == ComponentLifecycleEvent.DELETED) { log.info("[{}] Handling tenant deleted notification: {}", msg.getTenantId(), msg); deletedTenants.add(tenantId); diff --git a/application/src/main/java/org/thingsboard/server/controller/BaseController.java b/application/src/main/java/org/thingsboard/server/controller/BaseController.java index c077d5aef5..533d9bb9d1 100644 --- a/application/src/main/java/org/thingsboard/server/controller/BaseController.java +++ b/application/src/main/java/org/thingsboard/server/controller/BaseController.java @@ -482,7 +482,7 @@ public abstract class BaseController { checkCustomerId(new CustomerId(entityId.getId()), operation); return; case TENANT: - checkTenantId(new TenantId(entityId.getId()), operation); + checkTenantId(TenantId.fromUUID(entityId.getId()), operation); return; case TENANT_PROFILE: checkTenantProfileId(new TenantProfileId(entityId.getId()), operation); diff --git a/application/src/main/java/org/thingsboard/server/controller/DashboardController.java b/application/src/main/java/org/thingsboard/server/controller/DashboardController.java index 1d3265053d..bc532c183a 100644 --- a/application/src/main/java/org/thingsboard/server/controller/DashboardController.java +++ b/application/src/main/java/org/thingsboard/server/controller/DashboardController.java @@ -585,7 +585,7 @@ public class DashboardController extends BaseController { @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES) @RequestParam(required = false) String sortOrder) throws ThingsboardException { try { - TenantId tenantId = new TenantId(toUUID(strTenantId)); + TenantId tenantId = TenantId.fromUUID(toUUID(strTenantId)); checkTenantId(tenantId, Operation.READ); PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); return checkNotNull(dashboardService.findDashboardsByTenantId(tenantId, pageLink)); diff --git a/application/src/main/java/org/thingsboard/server/controller/DeviceController.java b/application/src/main/java/org/thingsboard/server/controller/DeviceController.java index 6ea6bdd880..38239c9a8b 100644 --- a/application/src/main/java/org/thingsboard/server/controller/DeviceController.java +++ b/application/src/main/java/org/thingsboard/server/controller/DeviceController.java @@ -807,7 +807,7 @@ public class DeviceController extends BaseController { DeviceId deviceId = new DeviceId(toUUID(strDeviceId)); Device device = checkDeviceId(deviceId, Operation.ASSIGN_TO_TENANT); - TenantId newTenantId = new TenantId(toUUID(strTenantId)); + TenantId newTenantId = TenantId.fromUUID(toUUID(strTenantId)); Tenant newTenant = tenantService.findTenantById(newTenantId); if (newTenant == null) { throw new ThingsboardException("Could not find the specified Tenant!", ThingsboardErrorCode.BAD_REQUEST_PARAMS); diff --git a/application/src/main/java/org/thingsboard/server/controller/EventController.java b/application/src/main/java/org/thingsboard/server/controller/EventController.java index ea77fce94c..5819ac3cb7 100644 --- a/application/src/main/java/org/thingsboard/server/controller/EventController.java +++ b/application/src/main/java/org/thingsboard/server/controller/EventController.java @@ -136,7 +136,7 @@ public class EventController extends BaseController { checkParameter("EntityId", strEntityId); checkParameter("EntityType", strEntityType); try { - TenantId tenantId = new TenantId(toUUID(strTenantId)); + TenantId tenantId = TenantId.fromUUID(toUUID(strTenantId)); EntityId entityId = EntityIdFactory.getByTypeAndId(strEntityType, strEntityId); checkEntityId(entityId, Operation.READ); @@ -177,7 +177,7 @@ public class EventController extends BaseController { checkParameter("EntityId", strEntityId); checkParameter("EntityType", strEntityType); try { - TenantId tenantId = new TenantId(toUUID(strTenantId)); + TenantId tenantId = TenantId.fromUUID(toUUID(strTenantId)); EntityId entityId = EntityIdFactory.getByTypeAndId(strEntityType, strEntityId); checkEntityId(entityId, Operation.READ); @@ -224,7 +224,7 @@ public class EventController extends BaseController { checkParameter("EntityId", strEntityId); checkParameter("EntityType", strEntityType); try { - TenantId tenantId = new TenantId(toUUID(strTenantId)); + TenantId tenantId = TenantId.fromUUID(toUUID(strTenantId)); EntityId entityId = EntityIdFactory.getByTypeAndId(strEntityType, strEntityId); checkEntityId(entityId, Operation.READ); diff --git a/application/src/main/java/org/thingsboard/server/controller/TenantController.java b/application/src/main/java/org/thingsboard/server/controller/TenantController.java index 0cfc8f3865..72f5422a0b 100644 --- a/application/src/main/java/org/thingsboard/server/controller/TenantController.java +++ b/application/src/main/java/org/thingsboard/server/controller/TenantController.java @@ -82,7 +82,7 @@ public class TenantController extends BaseController { @PathVariable(TENANT_ID) String strTenantId) throws ThingsboardException { checkParameter(TENANT_ID, strTenantId); try { - TenantId tenantId = new TenantId(toUUID(strTenantId)); + TenantId tenantId = TenantId.fromUUID(toUUID(strTenantId)); Tenant tenant = checkTenantId(tenantId, Operation.READ); if (!tenant.getAdditionalInfo().isNull()) { processDashboardIdFromAdditionalInfo((ObjectNode) tenant.getAdditionalInfo(), HOME_DASHBOARD); @@ -104,7 +104,7 @@ public class TenantController extends BaseController { @PathVariable(TENANT_ID) String strTenantId) throws ThingsboardException { checkParameter(TENANT_ID, strTenantId); try { - TenantId tenantId = new TenantId(toUUID(strTenantId)); + TenantId tenantId = TenantId.fromUUID(toUUID(strTenantId)); return checkTenantInfoId(tenantId, Operation.READ); } catch (Exception e) { throw handleException(e); @@ -154,7 +154,7 @@ public class TenantController extends BaseController { @PathVariable(TENANT_ID) String strTenantId) throws ThingsboardException { checkParameter(TENANT_ID, strTenantId); try { - TenantId tenantId = new TenantId(toUUID(strTenantId)); + TenantId tenantId = TenantId.fromUUID(toUUID(strTenantId)); Tenant tenant = checkTenantId(tenantId, Operation.DELETE); tenantService.deleteTenant(tenantId); tenantProfileCache.evict(tenantId); diff --git a/application/src/main/java/org/thingsboard/server/controller/UserController.java b/application/src/main/java/org/thingsboard/server/controller/UserController.java index e83bc8970c..6007d1831e 100644 --- a/application/src/main/java/org/thingsboard/server/controller/UserController.java +++ b/application/src/main/java/org/thingsboard/server/controller/UserController.java @@ -371,7 +371,7 @@ public class UserController extends BaseController { @RequestParam(required = false) String sortOrder) throws ThingsboardException { checkParameter("tenantId", strTenantId); try { - TenantId tenantId = new TenantId(toUUID(strTenantId)); + TenantId tenantId = TenantId.fromUUID(toUUID(strTenantId)); PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); return checkNotNull(userService.findTenantAdmins(tenantId, pageLink)); } catch (Exception e) { diff --git a/application/src/main/java/org/thingsboard/server/controller/WidgetTypeController.java b/application/src/main/java/org/thingsboard/server/controller/WidgetTypeController.java index b7f8b28b28..5f7c9ec6c9 100644 --- a/application/src/main/java/org/thingsboard/server/controller/WidgetTypeController.java +++ b/application/src/main/java/org/thingsboard/server/controller/WidgetTypeController.java @@ -216,7 +216,7 @@ public class WidgetTypeController extends BaseController { try { TenantId tenantId; if (isSystem) { - tenantId = new TenantId(ModelConstants.NULL_UUID); + tenantId = TenantId.fromUUID(ModelConstants.NULL_UUID); } else { tenantId = getCurrentUser().getTenantId(); } diff --git a/application/src/main/java/org/thingsboard/server/service/apiusage/DefaultTbApiUsageStateService.java b/application/src/main/java/org/thingsboard/server/service/apiusage/DefaultTbApiUsageStateService.java index bb768a15d7..9a9b0ff6a3 100644 --- a/application/src/main/java/org/thingsboard/server/service/apiusage/DefaultTbApiUsageStateService.java +++ b/application/src/main/java/org/thingsboard/server/service/apiusage/DefaultTbApiUsageStateService.java @@ -164,7 +164,7 @@ public class DefaultTbApiUsageStateService extends TbApplicationEventListener msg, TbCallback callback) { ToUsageStatsServiceMsg statsMsg = msg.getValue(); - TenantId tenantId = new TenantId(new UUID(statsMsg.getTenantIdMSB(), statsMsg.getTenantIdLSB())); + TenantId tenantId = TenantId.fromUUID(new UUID(statsMsg.getTenantIdMSB(), statsMsg.getTenantIdLSB())); EntityId entityId; if (statsMsg.getCustomerIdMSB() != 0 && statsMsg.getCustomerIdLSB() != 0) { entityId = new CustomerId(new UUID(statsMsg.getCustomerIdMSB(), statsMsg.getCustomerIdLSB())); diff --git a/application/src/main/java/org/thingsboard/server/service/edge/DefaultEdgeNotificationService.java b/application/src/main/java/org/thingsboard/server/service/edge/DefaultEdgeNotificationService.java index d25e63183c..0ae07631b5 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/DefaultEdgeNotificationService.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/DefaultEdgeNotificationService.java @@ -124,7 +124,7 @@ public class DefaultEdgeNotificationService implements EdgeNotificationService { public void pushNotificationToEdge(TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg, TbCallback callback) { log.trace("Pushing notification to edge {}", edgeNotificationMsg); try { - TenantId tenantId = new TenantId(new UUID(edgeNotificationMsg.getTenantIdMSB(), edgeNotificationMsg.getTenantIdLSB())); + TenantId tenantId = TenantId.fromUUID(new UUID(edgeNotificationMsg.getTenantIdMSB(), edgeNotificationMsg.getTenantIdLSB())); EdgeEventType type = EdgeEventType.valueOf(edgeNotificationMsg.getType()); switch (type) { case EDGE: diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/TelemetryEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/TelemetryEdgeProcessor.java index 7aea556544..16af076b3a 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/TelemetryEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/TelemetryEdgeProcessor.java @@ -276,7 +276,7 @@ public class TelemetryEdgeProcessor extends BaseEdgeProcessor { case DASHBOARD: return new DashboardId(new UUID(entityData.getEntityIdMSB(), entityData.getEntityIdLSB())); case TENANT: - return new TenantId(new UUID(entityData.getEntityIdMSB(), entityData.getEntityIdLSB())); + return TenantId.fromUUID(new UUID(entityData.getEntityIdMSB(), entityData.getEntityIdLSB())); case CUSTOMER: return new CustomerId(new UUID(entityData.getEntityIdMSB(), entityData.getEntityIdLSB())); case USER: @@ -303,7 +303,7 @@ public class TelemetryEdgeProcessor extends BaseEdgeProcessor { entityId = new DashboardId(edgeEvent.getEntityId()); break; case TENANT: - entityId = new TenantId(edgeEvent.getEntityId()); + entityId = TenantId.fromUUID(edgeEvent.getEntityId()); break; case CUSTOMER: entityId = new CustomerId(edgeEvent.getEntityId()); diff --git a/application/src/main/java/org/thingsboard/server/service/ota/DefaultOtaPackageStateService.java b/application/src/main/java/org/thingsboard/server/service/ota/DefaultOtaPackageStateService.java index 883745e643..bfb46ea75d 100644 --- a/application/src/main/java/org/thingsboard/server/service/ota/DefaultOtaPackageStateService.java +++ b/application/src/main/java/org/thingsboard/server/service/ota/DefaultOtaPackageStateService.java @@ -206,7 +206,7 @@ public class DefaultOtaPackageStateService implements OtaPackageStateService { boolean isSuccess = false; OtaPackageId targetOtaPackageId = new OtaPackageId(new UUID(msg.getOtaPackageIdMSB(), msg.getOtaPackageIdLSB())); DeviceId deviceId = new DeviceId(new UUID(msg.getDeviceIdMSB(), msg.getDeviceIdLSB())); - TenantId tenantId = new TenantId(new UUID(msg.getTenantIdMSB(), msg.getTenantIdLSB())); + TenantId tenantId = TenantId.fromUUID(new UUID(msg.getTenantIdMSB(), msg.getTenantIdLSB())); OtaPackageType firmwareType = OtaPackageType.valueOf(msg.getType()); long ts = msg.getTs(); diff --git a/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbClusterService.java b/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbClusterService.java index 0a82994a42..834a59811f 100644 --- a/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbClusterService.java +++ b/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbClusterService.java @@ -140,7 +140,7 @@ public class DefaultTbClusterService implements TbClusterService { public void pushMsgToRuleEngine(TenantId tenantId, EntityId entityId, TbMsg tbMsg, TbQueueCallback callback) { if (tenantId.isNullUid()) { if (entityId.getEntityType().equals(EntityType.TENANT)) { - tenantId = new TenantId(entityId.getId()); + tenantId = TenantId.fromUUID(entityId.getId()); } else { log.warn("[{}][{}] Received invalid message: {}", tenantId, entityId, tbMsg); return; diff --git a/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbCoreConsumerService.java b/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbCoreConsumerService.java index 3602aa804e..88649272be 100644 --- a/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbCoreConsumerService.java +++ b/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbCoreConsumerService.java @@ -453,37 +453,37 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService msg) { log.trace("[{}] Creating callback for topic {} message: {}", id, configuration.getName(), msg.getValue()); ToRuleEngineMsg toRuleEngineMsg = msg.getValue(); - TenantId tenantId = new TenantId(new UUID(toRuleEngineMsg.getTenantIdMSB(), toRuleEngineMsg.getTenantIdLSB())); + TenantId tenantId = TenantId.fromUUID(new UUID(toRuleEngineMsg.getTenantIdMSB(), toRuleEngineMsg.getTenantIdLSB())); TbMsgCallback callback = prometheusStatsEnabled ? new TbMsgPackCallback(id, tenantId, ctx, stats.getTimer(tenantId, SUCCESSFUL_STATUS), stats.getTimer(tenantId, FAILED_STATUS)) : new TbMsgPackCallback(id, tenantId, ctx); @@ -344,9 +344,9 @@ public class DefaultTbRuleEngineConsumerService extends AbstractConsumerService< TbMsg tmpMsg = TbMsg.fromBytes(configuration.getName(), tmp.getTbMsg().toByteArray(), TbMsgCallback.EMPTY); RuleNodeInfo ruleNodeInfo = ctx.getLastVisitedRuleNode(pending.getKey()); if (printAll) { - log.trace("[{}] {} to process message: {}, Last Rule Node: {}", new TenantId(new UUID(tmp.getTenantIdMSB(), tmp.getTenantIdLSB())), prefix, tmpMsg, ruleNodeInfo); + log.trace("[{}] {} to process message: {}, Last Rule Node: {}", TenantId.fromUUID(new UUID(tmp.getTenantIdMSB(), tmp.getTenantIdLSB())), prefix, tmpMsg, ruleNodeInfo); } else { - log.info("[{}] {} to process message: {}, Last Rule Node: {}", new TenantId(new UUID(tmp.getTenantIdMSB(), tmp.getTenantIdLSB())), prefix, tmpMsg, ruleNodeInfo); + log.info("[{}] {} to process message: {}, Last Rule Node: {}", TenantId.fromUUID(new UUID(tmp.getTenantIdMSB(), tmp.getTenantIdLSB())), prefix, tmpMsg, ruleNodeInfo); break; } } diff --git a/application/src/main/java/org/thingsboard/server/service/queue/processing/SequentialByTenantIdTbRuleEngineSubmitStrategy.java b/application/src/main/java/org/thingsboard/server/service/queue/processing/SequentialByTenantIdTbRuleEngineSubmitStrategy.java index 7c7d076d94..fba60ea6fb 100644 --- a/application/src/main/java/org/thingsboard/server/service/queue/processing/SequentialByTenantIdTbRuleEngineSubmitStrategy.java +++ b/application/src/main/java/org/thingsboard/server/service/queue/processing/SequentialByTenantIdTbRuleEngineSubmitStrategy.java @@ -29,6 +29,6 @@ public class SequentialByTenantIdTbRuleEngineSubmitStrategy extends SequentialBy @Override protected EntityId getEntityId(TransportProtos.ToRuleEngineMsg msg) { - return new TenantId(new UUID(msg.getTenantIdMSB(), msg.getTenantIdLSB())); + return TenantId.fromUUID(new UUID(msg.getTenantIdMSB(), msg.getTenantIdLSB())); } } diff --git a/application/src/main/java/org/thingsboard/server/service/security/AccessValidator.java b/application/src/main/java/org/thingsboard/server/service/security/AccessValidator.java index ab9d0de231..829ef08865 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/AccessValidator.java +++ b/application/src/main/java/org/thingsboard/server/service/security/AccessValidator.java @@ -450,7 +450,7 @@ public class AccessValidator { } else if (currentUser.isSystemAdmin()) { callback.onSuccess(ValidationResult.ok(null)); } else { - ListenableFuture tenantFuture = tenantService.findTenantByIdAsync(currentUser.getTenantId(), new TenantId(entityId.getId())); + ListenableFuture tenantFuture = tenantService.findTenantByIdAsync(currentUser.getTenantId(), TenantId.fromUUID(entityId.getId())); Futures.addCallback(tenantFuture, getCallback(callback, tenant -> { if (tenant == null) { return ValidationResult.entityNotFound("Tenant with requested id wasn't found!"); diff --git a/application/src/main/java/org/thingsboard/server/service/security/model/token/JwtTokenFactory.java b/application/src/main/java/org/thingsboard/server/service/security/model/token/JwtTokenFactory.java index 9315b3bcc1..f8afd4932c 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/model/token/JwtTokenFactory.java +++ b/application/src/main/java/org/thingsboard/server/service/security/model/token/JwtTokenFactory.java @@ -126,7 +126,7 @@ public class JwtTokenFactory { securityUser.setUserPrincipal(principal); String tenantId = claims.get(TENANT_ID, String.class); if (tenantId != null) { - securityUser.setTenantId(new TenantId(UUID.fromString(tenantId))); + securityUser.setTenantId(TenantId.fromUUID(UUID.fromString(tenantId))); } String customerId = claims.get(CUSTOMER_ID, String.class); if (customerId != null) { diff --git a/application/src/main/java/org/thingsboard/server/service/state/DefaultDeviceStateService.java b/application/src/main/java/org/thingsboard/server/service/state/DefaultDeviceStateService.java index 97c22d69c9..2c68414369 100644 --- a/application/src/main/java/org/thingsboard/server/service/state/DefaultDeviceStateService.java +++ b/application/src/main/java/org/thingsboard/server/service/state/DefaultDeviceStateService.java @@ -237,7 +237,7 @@ public class DefaultDeviceStateService extends TbApplicationEventListener { - TenantId tenantId = new TenantId(id); + TenantId tenantId = TenantId.fromUUID(id); try { AssetId serviceAssetId = getServiceAssetId(tenantId, queueName); if (stats.getTotalMsgCounter().get() > 0) { diff --git a/application/src/main/java/org/thingsboard/server/service/subscription/TbSubscriptionUtils.java b/application/src/main/java/org/thingsboard/server/service/subscription/TbSubscriptionUtils.java index 82e0b17f67..023e961451 100644 --- a/application/src/main/java/org/thingsboard/server/service/subscription/TbSubscriptionUtils.java +++ b/application/src/main/java/org/thingsboard/server/service/subscription/TbSubscriptionUtils.java @@ -125,7 +125,7 @@ public class TbSubscriptionUtils { .sessionId(subProto.getSessionId()) .subscriptionId(subProto.getSubscriptionId()) .entityId(EntityIdFactory.getByTypeAndUuid(subProto.getEntityType(), new UUID(subProto.getEntityIdMSB(), subProto.getEntityIdLSB()))) - .tenantId(new TenantId(new UUID(subProto.getTenantIdMSB(), subProto.getTenantIdLSB()))); + .tenantId(TenantId.fromUUID(new UUID(subProto.getTenantIdMSB(), subProto.getTenantIdLSB()))); builder.scope(TbAttributeSubscriptionScope.valueOf(attributeSub.getScope())); builder.allKeys(attributeSub.getAllKeys()); @@ -142,7 +142,7 @@ public class TbSubscriptionUtils { .sessionId(subProto.getSessionId()) .subscriptionId(subProto.getSubscriptionId()) .entityId(EntityIdFactory.getByTypeAndUuid(subProto.getEntityType(), new UUID(subProto.getEntityIdMSB(), subProto.getEntityIdLSB()))) - .tenantId(new TenantId(new UUID(subProto.getTenantIdMSB(), subProto.getTenantIdLSB()))); + .tenantId(TenantId.fromUUID(new UUID(subProto.getTenantIdMSB(), subProto.getTenantIdLSB()))); builder.allKeys(telemetrySub.getAllKeys()); Map keyStates = new HashMap<>(); @@ -161,7 +161,7 @@ public class TbSubscriptionUtils { .sessionId(subProto.getSessionId()) .subscriptionId(subProto.getSubscriptionId()) .entityId(EntityIdFactory.getByTypeAndUuid(subProto.getEntityType(), new UUID(subProto.getEntityIdMSB(), subProto.getEntityIdLSB()))) - .tenantId(new TenantId(new UUID(subProto.getTenantIdMSB(), subProto.getTenantIdLSB()))); + .tenantId(TenantId.fromUUID(new UUID(subProto.getTenantIdMSB(), subProto.getTenantIdLSB()))); builder.ts(alarmSub.getTs()); return builder.build(); } diff --git a/application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java b/application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java index 1cefefd5d1..80fb062c90 100644 --- a/application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java +++ b/application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java @@ -380,7 +380,7 @@ public class DefaultTransportApiService implements TransportApiService { DeviceProfile deviceProfile = deviceProfileCache.find(deviceProfileId); builder.setData(ByteString.copyFrom(dataDecodingEncodingService.encode(deviceProfile))); } else if (entityType.equals(EntityType.TENANT)) { - TenantId tenantId = new TenantId(entityUuid); + TenantId tenantId = TenantId.fromUUID(entityUuid); TenantProfile tenantProfile = tenantProfileCache.get(tenantId); ApiUsageState state = apiUsageStateService.getApiUsageState(tenantId); builder.setData(ByteString.copyFrom(dataDecodingEncodingService.encode(tenantProfile))); @@ -425,7 +425,7 @@ public class DefaultTransportApiService implements TransportApiService { private ListenableFuture handle(GetResourceRequestMsg requestMsg) { - TenantId tenantId = new TenantId(new UUID(requestMsg.getTenantIdMSB(), requestMsg.getTenantIdLSB())); + TenantId tenantId = TenantId.fromUUID(new UUID(requestMsg.getTenantIdMSB(), requestMsg.getTenantIdLSB())); ResourceType resourceType = ResourceType.valueOf(requestMsg.getResourceType()); String resourceKey = requestMsg.getResourceKey(); TransportProtos.GetResourceResponseMsg.Builder builder = TransportProtos.GetResourceResponseMsg.newBuilder(); @@ -542,7 +542,7 @@ public class DefaultTransportApiService implements TransportApiService { } private ListenableFuture handle(TransportProtos.GetOtaPackageRequestMsg requestMsg) { - TenantId tenantId = new TenantId(new UUID(requestMsg.getTenantIdMSB(), requestMsg.getTenantIdLSB())); + TenantId tenantId = TenantId.fromUUID(new UUID(requestMsg.getTenantIdMSB(), requestMsg.getTenantIdLSB())); DeviceId deviceId = new DeviceId(new UUID(requestMsg.getDeviceIdMSB(), requestMsg.getDeviceIdLSB())); OtaPackageType otaPackageType = OtaPackageType.valueOf(requestMsg.getType()); Device device = deviceService.findDeviceById(tenantId, deviceId); @@ -592,7 +592,7 @@ public class DefaultTransportApiService implements TransportApiService { } private ListenableFuture handleRegistration(TransportProtos.LwM2MRegistrationRequestMsg msg) { - TenantId tenantId = new TenantId(UUID.fromString(msg.getTenantId())); + TenantId tenantId = TenantId.fromUUID(UUID.fromString(msg.getTenantId())); String deviceName = msg.getEndpoint(); Lock deviceCreationLock = deviceCreationLocks.computeIfAbsent(deviceName, id -> new ReentrantLock()); deviceCreationLock.lock(); diff --git a/application/src/main/java/org/thingsboard/server/service/transport/msg/TransportToDeviceActorMsgWrapper.java b/application/src/main/java/org/thingsboard/server/service/transport/msg/TransportToDeviceActorMsgWrapper.java index 74d81c0307..e2aba53bf1 100644 --- a/application/src/main/java/org/thingsboard/server/service/transport/msg/TransportToDeviceActorMsgWrapper.java +++ b/application/src/main/java/org/thingsboard/server/service/transport/msg/TransportToDeviceActorMsgWrapper.java @@ -44,7 +44,7 @@ public class TransportToDeviceActorMsgWrapper implements TbActorMsg, DeviceAware public TransportToDeviceActorMsgWrapper(TransportToDeviceActorMsg msg, TbCallback callback) { this.msg = msg; this.callback = callback; - this.tenantId = new TenantId(new UUID(msg.getSessionInfo().getTenantIdMSB(), msg.getSessionInfo().getTenantIdLSB())); + this.tenantId = TenantId.fromUUID(new UUID(msg.getSessionInfo().getTenantIdMSB(), msg.getSessionInfo().getTenantIdLSB())); this.deviceId = new DeviceId(new UUID(msg.getSessionInfo().getDeviceIdMSB(), msg.getSessionInfo().getDeviceIdLSB())); } diff --git a/application/src/test/java/org/thingsboard/server/service/resource/sql/BaseTbResourceServiceTest.java b/application/src/test/java/org/thingsboard/server/service/resource/sql/BaseTbResourceServiceTest.java index caf4431e7d..035dc82e75 100644 --- a/application/src/test/java/org/thingsboard/server/service/resource/sql/BaseTbResourceServiceTest.java +++ b/application/src/test/java/org/thingsboard/server/service/resource/sql/BaseTbResourceServiceTest.java @@ -277,7 +277,7 @@ public class BaseTbResourceServiceTest extends AbstractControllerTest { @Test(expected = DataValidationException.class) public void testSaveTbResourceWithInvalidTenant() throws Exception { TbResource resource = new TbResource(); - resource.setTenantId(new TenantId(Uuids.timeBased())); + resource.setTenantId(TenantId.fromUUID(Uuids.timeBased())); resource.setResourceType(ResourceType.JKS); resource.setTitle("My resource"); resource.setFileName(DEFAULT_FILE_NAME); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/id/EntityIdFactory.java b/common/data/src/main/java/org/thingsboard/server/common/data/id/EntityIdFactory.java index 6d8ddfdd18..519ac4d4b8 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/id/EntityIdFactory.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/id/EntityIdFactory.java @@ -40,7 +40,7 @@ public class EntityIdFactory { public static EntityId getByTypeAndUuid(EntityType type, UUID uuid) { switch (type) { case TENANT: - return new TenantId(uuid); + return TenantId.fromUUID(uuid); case CUSTOMER: return new CustomerId(uuid); case USER: diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/id/TenantId.java b/common/data/src/main/java/org/thingsboard/server/common/data/id/TenantId.java index efba010507..665edc0ca9 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/id/TenantId.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/id/TenantId.java @@ -28,7 +28,7 @@ import java.util.UUID; public final class TenantId extends UUIDBased implements EntityId { @JsonIgnore - public static final TenantId SYS_TENANT_ID = new TenantId(EntityId.NULL_UUID); + public static final TenantId SYS_TENANT_ID = TenantId.fromUUID(EntityId.NULL_UUID); static final ConcurrentReferenceHashMap tenants = new ConcurrentReferenceHashMap<>(16, ReferenceType.SOFT); @@ -39,6 +39,7 @@ public final class TenantId extends UUIDBased implements EntityId { return tenants.computeIfAbsent(id, TenantId::new); } + //default constructor is still available due to possible usage in extensions public TenantId(UUID id) { super(id); } diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/discovery/DefaultTbServiceInfoProvider.java b/common/queue/src/main/java/org/thingsboard/server/queue/discovery/DefaultTbServiceInfoProvider.java index 6988bef9e1..b94c009ad7 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/discovery/DefaultTbServiceInfoProvider.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/discovery/DefaultTbServiceInfoProvider.java @@ -88,7 +88,7 @@ public class DefaultTbServiceInfoProvider implements TbServiceInfoProvider { UUID tenantId; if (!StringUtils.isEmpty(tenantIdStr)) { tenantId = UUID.fromString(tenantIdStr); - isolatedTenant = new TenantId(tenantId); + isolatedTenant = TenantId.fromUUID(tenantId); } else { tenantId = TenantId.NULL_UUID; } diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/discovery/HashPartitionService.java b/common/queue/src/main/java/org/thingsboard/server/queue/discovery/HashPartitionService.java index 1e657aac29..59693732c8 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/discovery/HashPartitionService.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/discovery/HashPartitionService.java @@ -327,7 +327,7 @@ public class HashPartitionService implements PartitionService { } private TenantId getSystemOrIsolatedTenantId(TransportProtos.ServiceInfo serviceInfo) { - return new TenantId(new UUID(serviceInfo.getTenantIdMSB(), serviceInfo.getTenantIdLSB())); + return TenantId.fromUUID(new UUID(serviceInfo.getTenantIdMSB(), serviceInfo.getTenantIdLSB())); } private void addNode(Map> queueServiceList, ServiceInfo instance) { diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java index e1565d5be2..83fdf51787 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java @@ -148,7 +148,7 @@ public class LwM2mClient implements Serializable { public void init(ValidateDeviceCredentialsResponse credentials, UUID sessionId) { this.session = createSession(nodeId, sessionId, credentials); - this.tenantId = new TenantId(new UUID(session.getTenantIdMSB(), session.getTenantIdLSB())); + this.tenantId = TenantId.fromUUID(new UUID(session.getTenantIdMSB(), session.getTenantIdLSB())); this.deviceId = new UUID(session.getDeviceIdMSB(), session.getDeviceIdLSB()); this.profileId = new UUID(session.getDeviceProfileIdMSB(), session.getDeviceProfileIdLSB()); this.powerMode = credentials.getDeviceInfo().getPowerMode(); diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2mUplinkMsgHandler.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2mUplinkMsgHandler.java index db1390768e..6a31a650df 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2mUplinkMsgHandler.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2mUplinkMsgHandler.java @@ -428,7 +428,7 @@ public class DefaultLwM2mUplinkMsgHandler extends LwM2MExecutorAwareService impl @Override public void onResourceUpdate(TransportProtos.ResourceUpdateMsg resourceUpdateMsgOpt) { String idVer = resourceUpdateMsgOpt.getResourceKey(); - TenantId tenantId = new TenantId(new UUID(resourceUpdateMsgOpt.getTenantIdMSB(), resourceUpdateMsgOpt.getTenantIdLSB())); + TenantId tenantId = TenantId.fromUUID(new UUID(resourceUpdateMsgOpt.getTenantIdMSB(), resourceUpdateMsgOpt.getTenantIdLSB())); modelProvider.evict(tenantId, idVer); clientContext.getLwM2mClients().forEach(e -> e.updateResourceModel(idVer, modelProvider)); } @@ -436,7 +436,7 @@ public class DefaultLwM2mUplinkMsgHandler extends LwM2MExecutorAwareService impl @Override public void onResourceDelete(TransportProtos.ResourceDeleteMsg resourceDeleteMsgOpt) { String pathIdVer = resourceDeleteMsgOpt.getResourceKey(); - TenantId tenantId = new TenantId(new UUID(resourceDeleteMsgOpt.getTenantIdMSB(), resourceDeleteMsgOpt.getTenantIdLSB())); + TenantId tenantId = TenantId.fromUUID(new UUID(resourceDeleteMsgOpt.getTenantIdMSB(), resourceDeleteMsgOpt.getTenantIdLSB())); modelProvider.evict(tenantId, pathIdVer); clientContext.getLwM2mClients().forEach(e -> e.deleteResources(pathIdVer, modelProvider)); } diff --git a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/DefaultTransportService.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/DefaultTransportService.java index 36e1a68daa..57d1da63b4 100644 --- a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/DefaultTransportService.java +++ b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/DefaultTransportService.java @@ -451,7 +451,7 @@ public class DefaultTransportService implements TransportService { private TransportDeviceInfo getTransportDeviceInfo(TransportProtos.DeviceInfoProto di) { TransportDeviceInfo tdi = new TransportDeviceInfo(); - tdi.setTenantId(new TenantId(new UUID(di.getTenantIdMSB(), di.getTenantIdLSB()))); + tdi.setTenantId(TenantId.fromUUID(new UUID(di.getTenantIdMSB(), di.getTenantIdLSB()))); tdi.setCustomerId(new CustomerId(new UUID(di.getCustomerIdMSB(), di.getCustomerIdLSB()))); tdi.setDeviceId(new DeviceId(new UUID(di.getDeviceIdMSB(), di.getDeviceIdLSB()))); tdi.setDeviceProfileId(new DeviceProfileId(new UUID(di.getDeviceProfileIdMSB(), di.getDeviceProfileIdLSB()))); @@ -805,7 +805,7 @@ public class DefaultTransportService implements TransportService { if (log.isTraceEnabled()) { log.trace("[{}] Processing msg: {}", toSessionId(sessionInfo), msg); } - TenantId tenantId = new TenantId(new UUID(sessionInfo.getTenantIdMSB(), sessionInfo.getTenantIdLSB())); + TenantId tenantId = TenantId.fromUUID(new UUID(sessionInfo.getTenantIdMSB(), sessionInfo.getTenantIdLSB())); DeviceId deviceId = new DeviceId(new UUID(sessionInfo.getDeviceIdMSB(), sessionInfo.getDeviceIdLSB())); EntityType rateLimitedEntityType = rateLimitService.checkLimits(tenantId, deviceId, dataPoints); @@ -895,14 +895,14 @@ public class DefaultTransportService implements TransportService { } else if (EntityType.TENANT_PROFILE.equals(entityType)) { tenantProfileCache.remove(new TenantProfileId(entityUuid)); } else if (EntityType.TENANT.equals(entityType)) { - rateLimitService.remove(new TenantId(entityUuid)); + rateLimitService.remove(TenantId.fromUUID(entityUuid)); } else if (EntityType.DEVICE.equals(entityType)) { rateLimitService.remove(new DeviceId(entityUuid)); onDeviceDeleted(new DeviceId(entityUuid)); } } else if (toSessionMsg.hasResourceUpdateMsg()) { TransportProtos.ResourceUpdateMsg msg = toSessionMsg.getResourceUpdateMsg(); - TenantId tenantId = new TenantId(new UUID(msg.getTenantIdMSB(), msg.getTenantIdLSB())); + TenantId tenantId = TenantId.fromUUID(new UUID(msg.getTenantIdMSB(), msg.getTenantIdLSB())); ResourceType resourceType = ResourceType.valueOf(msg.getResourceType()); String resourceId = msg.getResourceKey(); transportResourceCache.update(tenantId, resourceType, resourceId); @@ -913,7 +913,7 @@ public class DefaultTransportService implements TransportService { } else if (toSessionMsg.hasResourceDeleteMsg()) { TransportProtos.ResourceDeleteMsg msg = toSessionMsg.getResourceDeleteMsg(); - TenantId tenantId = new TenantId(new UUID(msg.getTenantIdMSB(), msg.getTenantIdLSB())); + TenantId tenantId = TenantId.fromUUID(new UUID(msg.getTenantIdMSB(), msg.getTenantIdLSB())); ResourceType resourceType = ResourceType.valueOf(msg.getResourceType()); String resourceId = msg.getResourceKey(); transportResourceCache.evict(tenantId, resourceType, resourceId); @@ -1001,7 +1001,7 @@ public class DefaultTransportService implements TransportService { } protected TenantId getTenantId(TransportProtos.SessionInfoProto sessionInfo) { - return new TenantId(new UUID(sessionInfo.getTenantIdMSB(), sessionInfo.getTenantIdLSB())); + return TenantId.fromUUID(new UUID(sessionInfo.getTenantIdMSB(), sessionInfo.getTenantIdLSB())); } protected CustomerId getCustomerId(TransportProtos.SessionInfoProto sessionInfo) { diff --git a/dao/src/main/java/org/thingsboard/server/dao/entity/BaseEntityService.java b/dao/src/main/java/org/thingsboard/server/dao/entity/BaseEntityService.java index 06e19a7730..948ed549f3 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/entity/BaseEntityService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/entity/BaseEntityService.java @@ -139,7 +139,7 @@ public class BaseEntityService extends AbstractEntityService implements EntitySe hasName = entityViewService.findEntityViewByIdAsync(tenantId, new EntityViewId(entityId.getId())); break; case TENANT: - hasName = tenantService.findTenantByIdAsync(tenantId, new TenantId(entityId.getId())); + hasName = tenantService.findTenantByIdAsync(tenantId, TenantId.fromUUID(entityId.getId())); break; case CUSTOMER: hasName = customerService.findCustomerByIdAsync(tenantId, new CustomerId(entityId.getId())); diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java b/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java index 6f7a3c2567..ec25ed26a7 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java @@ -28,7 +28,7 @@ public class ModelConstants { } public static final UUID NULL_UUID = Uuids.startOf(0); - public static final TenantId SYSTEM_TENANT = new TenantId(ModelConstants.NULL_UUID); + public static final TenantId SYSTEM_TENANT = TenantId.fromUUID(ModelConstants.NULL_UUID); // this is the difference between midnight October 15, 1582 UTC and midnight January 1, 1970 UTC as 100 nanosecond units public static final long EPOCH_DIFF = 122192928000000000L; diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractAlarmEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractAlarmEntity.java index 8ade2b78d5..afa5d06215 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractAlarmEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractAlarmEntity.java @@ -166,7 +166,7 @@ public abstract class AbstractAlarmEntity extends BaseSqlEntity Alarm alarm = new Alarm(new AlarmId(id)); alarm.setCreatedTime(createdTime); if (tenantId != null) { - alarm.setTenantId(new TenantId(tenantId)); + alarm.setTenantId(TenantId.fromUUID(tenantId)); } if (customerId != null) { alarm.setCustomerId(new CustomerId(customerId)); diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractAssetEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractAssetEntity.java index 07d24020ff..1984dea60c 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractAssetEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractAssetEntity.java @@ -119,7 +119,7 @@ public abstract class AbstractAssetEntity extends BaseSqlEntity Asset asset = new Asset(new AssetId(id)); asset.setCreatedTime(createdTime); if (tenantId != null) { - asset.setTenantId(new TenantId(tenantId)); + asset.setTenantId(TenantId.fromUUID(tenantId)); } if (customerId != null) { asset.setCustomerId(new CustomerId(customerId)); diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractDeviceEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractDeviceEntity.java index c9913565e4..14a71cad48 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractDeviceEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractDeviceEntity.java @@ -145,7 +145,7 @@ public abstract class AbstractDeviceEntity extends BaseSqlEnti Device device = new Device(new DeviceId(getUuid())); device.setCreatedTime(createdTime); if (tenantId != null) { - device.setTenantId(new TenantId(tenantId)); + device.setTenantId(TenantId.fromUUID(tenantId)); } if (customerId != null) { device.setCustomerId(new CustomerId(customerId)); diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractEdgeEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractEdgeEntity.java index bb9e9d98ff..4929e6a8ab 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractEdgeEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractEdgeEntity.java @@ -153,7 +153,7 @@ public abstract class AbstractEdgeEntity extends BaseSqlEntity extends Bas entityView.setEntityId(EntityIdFactory.getByTypeAndUuid(entityType.name(), entityId)); } if (tenantId != null) { - entityView.setTenantId(new TenantId(tenantId)); + entityView.setTenantId(TenantId.fromUUID(tenantId)); } if (customerId != null) { entityView.setCustomerId(new CustomerId(customerId)); diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractTenantEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractTenantEntity.java index 7ba0fc0871..8445e6c4bf 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractTenantEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractTenantEntity.java @@ -138,7 +138,7 @@ public abstract class AbstractTenantEntity extends BaseSqlEnti } protected Tenant toTenant() { - Tenant tenant = new Tenant(new TenantId(this.getUuid())); + Tenant tenant = new Tenant(TenantId.fromUUID(this.getUuid())); tenant.setCreatedTime(createdTime); tenant.setTitle(title); tenant.setRegion(region); diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractWidgetTypeEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractWidgetTypeEntity.java index 803d1604f8..52f6f6f486 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractWidgetTypeEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractWidgetTypeEntity.java @@ -75,7 +75,7 @@ public abstract class AbstractWidgetTypeEntity extends BaseWidgetType widgetType = new BaseWidgetType(new WidgetTypeId(getUuid())); widgetType.setCreatedTime(createdTime); if (tenantId != null) { - widgetType.setTenantId(new TenantId(tenantId)); + widgetType.setTenantId(TenantId.fromUUID(tenantId)); } widgetType.setBundleAlias(bundleAlias); widgetType.setAlias(alias); diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/ApiUsageStateEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/ApiUsageStateEntity.java index 40b56c0095..7919d64eec 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/ApiUsageStateEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/ApiUsageStateEntity.java @@ -102,7 +102,7 @@ public class ApiUsageStateEntity extends BaseSqlEntity implements ApiUsageState ur = new ApiUsageState(new ApiUsageStateId(this.getUuid())); ur.setCreatedTime(createdTime); if (tenantId != null) { - ur.setTenantId(new TenantId(tenantId)); + ur.setTenantId(TenantId.fromUUID(tenantId)); } if (entityId != null) { ur.setEntityId(EntityIdFactory.getByTypeAndUuid(entityType, entityId)); diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/AuditLogEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/AuditLogEntity.java index 17e30a817f..b1447467d2 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/AuditLogEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/AuditLogEntity.java @@ -132,7 +132,7 @@ public class AuditLogEntity extends BaseSqlEntity implements BaseEntit AuditLog auditLog = new AuditLog(new AuditLogId(this.getUuid())); auditLog.setCreatedTime(createdTime); if (tenantId != null) { - auditLog.setTenantId(new TenantId(tenantId)); + auditLog.setTenantId(TenantId.fromUUID(tenantId)); } if (customerId != null) { auditLog.setCustomerId(new CustomerId(customerId)); diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/CustomerEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/CustomerEntity.java index 6ed9a3ff51..252cb096a2 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/CustomerEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/CustomerEntity.java @@ -113,7 +113,7 @@ public final class CustomerEntity extends BaseSqlEntity implements Sea public Customer toData() { Customer customer = new Customer(new CustomerId(this.getUuid())); customer.setCreatedTime(createdTime); - customer.setTenantId(new TenantId(tenantId)); + customer.setTenantId(TenantId.fromUUID(tenantId)); customer.setTitle(title); customer.setCountry(country); customer.setState(state); diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/DashboardEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/DashboardEntity.java index 518a45e5fd..5c01f3f3b4 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/DashboardEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/DashboardEntity.java @@ -119,7 +119,7 @@ public final class DashboardEntity extends BaseSqlEntity implements S Dashboard dashboard = new Dashboard(new DashboardId(this.getUuid())); dashboard.setCreatedTime(this.getCreatedTime()); if (tenantId != null) { - dashboard.setTenantId(new TenantId(tenantId)); + dashboard.setTenantId(TenantId.fromUUID(tenantId)); } dashboard.setTitle(title); dashboard.setImage(image); diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/DashboardInfoEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/DashboardInfoEntity.java index 8d202b2dad..a8f9efba4f 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/DashboardInfoEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/DashboardInfoEntity.java @@ -113,7 +113,7 @@ public class DashboardInfoEntity extends BaseSqlEntity implements DashboardInfo dashboardInfo = new DashboardInfo(new DashboardId(this.getUuid())); dashboardInfo.setCreatedTime(createdTime); if (tenantId != null) { - dashboardInfo.setTenantId(new TenantId(tenantId)); + dashboardInfo.setTenantId(TenantId.fromUUID(tenantId)); } dashboardInfo.setTitle(title); dashboardInfo.setImage(image); diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/DeviceProfileEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/DeviceProfileEntity.java index a3d6c491c0..1ae936810e 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/DeviceProfileEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/DeviceProfileEntity.java @@ -158,7 +158,7 @@ public final class DeviceProfileEntity extends BaseSqlEntity impl DeviceProfile deviceProfile = new DeviceProfile(new DeviceProfileId(this.getUuid())); deviceProfile.setCreatedTime(createdTime); if (tenantId != null) { - deviceProfile.setTenantId(new TenantId(tenantId)); + deviceProfile.setTenantId(TenantId.fromUUID(tenantId)); } deviceProfile.setName(name); deviceProfile.setType(type); diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/EdgeEventEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/EdgeEventEntity.java index b30d40470b..90a1f2a137 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/EdgeEventEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/EdgeEventEntity.java @@ -111,7 +111,7 @@ public class EdgeEventEntity extends BaseSqlEntity implements BaseEnt public EdgeEvent toData() { EdgeEvent edgeEvent = new EdgeEvent(new EdgeEventId(this.getUuid())); edgeEvent.setCreatedTime(createdTime); - edgeEvent.setTenantId(new TenantId(tenantId)); + edgeEvent.setTenantId(TenantId.fromUUID(tenantId)); edgeEvent.setEdgeId(new EdgeId(edgeId)); if (entityId != null) { edgeEvent.setEntityId(entityId); diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/EntityAlarmEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/EntityAlarmEntity.java index 50d57cf61c..0a2842ca8e 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/EntityAlarmEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/EntityAlarmEntity.java @@ -85,7 +85,7 @@ public final class EntityAlarmEntity implements ToData { @Override public EntityAlarm toData() { EntityAlarm result = new EntityAlarm(); - result.setTenantId(new TenantId(tenantId)); + result.setTenantId(TenantId.fromUUID(tenantId)); result.setEntityId(EntityIdFactory.getByTypeAndUuid(entityType, entityId)); result.setAlarmId(new AlarmId(alarmId)); result.setAlarmType(alarmType); diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/EventEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/EventEntity.java index 2d1e5b555d..15bb01df6a 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/EventEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/EventEntity.java @@ -103,7 +103,7 @@ public class EventEntity extends BaseSqlEntity implements BaseEntity { OAuth2Params oauth2Params = new OAuth2Params(); oauth2Params.setId(new OAuth2ParamsId(id)); oauth2Params.setCreatedTime(createdTime); - oauth2Params.setTenantId(new TenantId(tenantId)); + oauth2Params.setTenantId(TenantId.fromUUID(tenantId)); oauth2Params.setEnabled(enabled); return oauth2Params; } diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/OtaPackageEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/OtaPackageEntity.java index 7e6f9a2aff..e6d14c588d 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/OtaPackageEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/OtaPackageEntity.java @@ -151,7 +151,7 @@ public class OtaPackageEntity extends BaseSqlEntity implements Searc public OtaPackage toData() { OtaPackage otaPackage = new OtaPackage(new OtaPackageId(id)); otaPackage.setCreatedTime(createdTime); - otaPackage.setTenantId(new TenantId(tenantId)); + otaPackage.setTenantId(TenantId.fromUUID(tenantId)); if (deviceProfileId != null) { otaPackage.setDeviceProfileId(new DeviceProfileId(deviceProfileId)); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/OtaPackageInfoEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/OtaPackageInfoEntity.java index b4909a6d2d..1eb94ddc96 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/OtaPackageInfoEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/OtaPackageInfoEntity.java @@ -170,7 +170,7 @@ public class OtaPackageInfoEntity extends BaseSqlEntity implemen public OtaPackageInfo toData() { OtaPackageInfo otaPackageInfo = new OtaPackageInfo(new OtaPackageId(id)); otaPackageInfo.setCreatedTime(createdTime); - otaPackageInfo.setTenantId(new TenantId(tenantId)); + otaPackageInfo.setTenantId(TenantId.fromUUID(tenantId)); if (deviceProfileId != null) { otaPackageInfo.setDeviceProfileId(new DeviceProfileId(deviceProfileId)); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/RpcEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/RpcEntity.java index d68aa1afb8..aadbf1e686 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/RpcEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/RpcEntity.java @@ -97,7 +97,7 @@ public class RpcEntity extends BaseSqlEntity implements BaseEntity { public Rpc toData() { Rpc rpc = new Rpc(new RpcId(id)); rpc.setCreatedTime(createdTime); - rpc.setTenantId(new TenantId(tenantId)); + rpc.setTenantId(TenantId.fromUUID(tenantId)); rpc.setDeviceId(new DeviceId(deviceId)); rpc.setExpirationTime(expirationTime); rpc.setRequest(request); diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/RuleChainEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/RuleChainEntity.java index be195723bd..6c4f85294c 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/RuleChainEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/RuleChainEntity.java @@ -110,7 +110,7 @@ public class RuleChainEntity extends BaseSqlEntity implements SearchT public RuleChain toData() { RuleChain ruleChain = new RuleChain(new RuleChainId(this.getUuid())); ruleChain.setCreatedTime(createdTime); - ruleChain.setTenantId(new TenantId(tenantId)); + ruleChain.setTenantId(TenantId.fromUUID(tenantId)); ruleChain.setName(name); ruleChain.setType(type); if (firstRuleNodeId != null) { diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/TbResourceEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/TbResourceEntity.java index 5b22b6e012..206da7bcf3 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/TbResourceEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/TbResourceEntity.java @@ -88,7 +88,7 @@ public class TbResourceEntity extends BaseSqlEntity implements Searc public TbResource toData() { TbResource resource = new TbResource(new TbResourceId(id)); resource.setCreatedTime(createdTime); - resource.setTenantId(new TenantId(tenantId)); + resource.setTenantId(TenantId.fromUUID(tenantId)); resource.setTitle(title); resource.setResourceType(ResourceType.valueOf(resourceType)); resource.setResourceKey(resourceKey); diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/TbResourceInfoEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/TbResourceInfoEntity.java index 78c36861c4..d063ececc7 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/TbResourceInfoEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/TbResourceInfoEntity.java @@ -76,7 +76,7 @@ public class TbResourceInfoEntity extends BaseSqlEntity implemen public TbResourceInfo toData() { TbResourceInfo resource = new TbResourceInfo(new TbResourceId(id)); resource.setCreatedTime(createdTime); - resource.setTenantId(new TenantId(tenantId)); + resource.setTenantId(TenantId.fromUUID(tenantId)); resource.setTitle(title); resource.setResourceType(ResourceType.valueOf(resourceType)); resource.setResourceKey(resourceKey); diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/UserEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/UserEntity.java index ba6e7ec042..bbe08b7eac 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/UserEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/UserEntity.java @@ -110,7 +110,7 @@ public class UserEntity extends BaseSqlEntity implements SearchTextEntity< user.setCreatedTime(createdTime); user.setAuthority(authority); if (tenantId != null) { - user.setTenantId(new TenantId(tenantId)); + user.setTenantId(TenantId.fromUUID(tenantId)); } if (customerId != null) { user.setCustomerId(new CustomerId(customerId)); diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/WidgetsBundleEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/WidgetsBundleEntity.java index 205ebf23f9..bd556c9605 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/WidgetsBundleEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/WidgetsBundleEntity.java @@ -87,7 +87,7 @@ public final class WidgetsBundleEntity extends BaseSqlEntity impl WidgetsBundle widgetsBundle = new WidgetsBundle(new WidgetsBundleId(id)); widgetsBundle.setCreatedTime(createdTime); if (tenantId != null) { - widgetsBundle.setTenantId(new TenantId(tenantId)); + widgetsBundle.setTenantId(TenantId.fromUUID(tenantId)); } widgetsBundle.setAlias(alias); widgetsBundle.setTitle(title); diff --git a/dao/src/main/java/org/thingsboard/server/dao/resource/BaseResourceService.java b/dao/src/main/java/org/thingsboard/server/dao/resource/BaseResourceService.java index f442f5acea..d4e5f3b5b7 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/resource/BaseResourceService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/resource/BaseResourceService.java @@ -180,7 +180,7 @@ public class BaseResourceService implements ResourceService { throw new DataValidationException("Resource key should be specified!"); } if (resource.getTenantId() == null) { - resource.setTenantId(new TenantId(ModelConstants.NULL_UUID)); + resource.setTenantId(TenantId.fromUUID(ModelConstants.NULL_UUID)); } if (!resource.getTenantId().getId().equals(ModelConstants.NULL_UUID)) { Tenant tenant = tenantDao.findById(tenantId, resource.getTenantId().getId()); diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/asset/JpaAssetDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/asset/JpaAssetDao.java index 2f8dbe5b02..9a78ac28e4 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/asset/JpaAssetDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/asset/JpaAssetDao.java @@ -182,7 +182,7 @@ public class JpaAssetDao extends JpaAbstractSearchTextDao im if (types != null && !types.isEmpty()) { list = new ArrayList<>(); for (String type : types) { - list.add(new EntitySubtype(new TenantId(tenantId), EntityType.ASSET, type)); + list.add(new EntitySubtype(TenantId.fromUUID(tenantId), EntityType.ASSET, type)); } } return list; diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceDao.java index 163a03857a..76468c4155 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceDao.java @@ -274,7 +274,7 @@ public class JpaDeviceDao extends JpaAbstractSearchTextDao if (types != null && !types.isEmpty()) { list = new ArrayList<>(); for (String type : types) { - list.add(new EntitySubtype(new TenantId(tenantId), EntityType.DEVICE, type)); + list.add(new EntitySubtype(TenantId.fromUUID(tenantId), EntityType.DEVICE, type)); } } return list; diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/edge/JpaEdgeDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/edge/JpaEdgeDao.java index 1049a35775..6365064dde 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/edge/JpaEdgeDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/edge/JpaEdgeDao.java @@ -187,7 +187,7 @@ public class JpaEdgeDao extends JpaAbstractSearchTextDao imple if (types != null && !types.isEmpty()) { list = new ArrayList<>(); for (String type : types) { - list.add(new EntitySubtype(new TenantId(tenantId), EntityType.EDGE, type)); + list.add(new EntitySubtype(TenantId.fromUUID(tenantId), EntityType.EDGE, type)); } } return list; diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/entityview/JpaEntityViewDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/entityview/JpaEntityViewDao.java index 35f81a860c..451a141c8d 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/entityview/JpaEntityViewDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/entityview/JpaEntityViewDao.java @@ -172,7 +172,7 @@ public class JpaEntityViewDao extends JpaAbstractSearchTextDao(); for (String type : types) { - list.add(new EntitySubtype(new TenantId(tenantId), EntityType.ENTITY_VIEW, type)); + list.add(new EntitySubtype(TenantId.fromUUID(tenantId), EntityType.ENTITY_VIEW, type)); } } return list; diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/query/AlarmDataAdapter.java b/dao/src/main/java/org/thingsboard/server/dao/sql/query/AlarmDataAdapter.java index df2d00fdbb..40a2cb972f 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/query/AlarmDataAdapter.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/query/AlarmDataAdapter.java @@ -86,7 +86,7 @@ public class AlarmDataAdapter { alarm.setType(row.get(ModelConstants.ALARM_TYPE_PROPERTY).toString()); alarm.setSeverity(AlarmSeverity.valueOf(row.get(ModelConstants.ALARM_SEVERITY_PROPERTY).toString())); alarm.setStatus(AlarmStatus.valueOf(row.get(ModelConstants.ALARM_STATUS_PROPERTY).toString())); - alarm.setTenantId(new TenantId((UUID) row.get(ModelConstants.TENANT_ID_PROPERTY))); + alarm.setTenantId(TenantId.fromUUID((UUID) row.get(ModelConstants.TENANT_ID_PROPERTY))); Object customerIdObj = row.get(ModelConstants.CUSTOMER_ID_PROPERTY); CustomerId customerId = customerIdObj != null ? new CustomerId((UUID) customerIdObj) : null; alarm.setCustomerId(customerId); diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/tenant/JpaTenantDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/tenant/JpaTenantDao.java index ff9dce6c96..d963e57eab 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/tenant/JpaTenantDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/tenant/JpaTenantDao.java @@ -77,7 +77,7 @@ public class JpaTenantDao extends JpaAbstractSearchTextDao @Override public PageData findTenantsIds(PageLink pageLink) { - return DaoUtil.pageToPageData(tenantRepository.findTenantsIds(DaoUtil.toPageable(pageLink))).mapData(TenantId::new); + return DaoUtil.pageToPageData(tenantRepository.findTenantsIds(DaoUtil.toPageable(pageLink))).mapData(TenantId::fromUUID); } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java index 4ccaa4e5c9..6b0e1bb182 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java @@ -224,7 +224,7 @@ public class TenantServiceImpl extends AbstractEntityService implements TenantSe @Override protected void removeEntity(TenantId tenantId, Tenant entity) { - deleteTenant(new TenantId(entity.getUuidId())); + deleteTenant(TenantId.fromUUID(entity.getUuidId())); } }; } diff --git a/dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java index 1ed5811ffa..3b4eb70e41 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java @@ -409,7 +409,7 @@ public class UserServiceImpl extends AbstractEntityService implements UserServic } TenantId tenantId = user.getTenantId(); if (tenantId == null) { - tenantId = new TenantId(ModelConstants.NULL_UUID); + tenantId = TenantId.fromUUID(ModelConstants.NULL_UUID); user.setTenantId(tenantId); } CustomerId customerId = user.getCustomerId(); diff --git a/dao/src/main/java/org/thingsboard/server/dao/widget/WidgetTypeServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/widget/WidgetTypeServiceImpl.java index d8a8ea3e35..44850ccd34 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/widget/WidgetTypeServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/widget/WidgetTypeServiceImpl.java @@ -135,7 +135,7 @@ public class WidgetTypeServiceImpl implements WidgetTypeService { throw new DataValidationException("Widgets type descriptor can't be empty!"); } if (widgetTypeDetails.getTenantId() == null) { - widgetTypeDetails.setTenantId(new TenantId(ModelConstants.NULL_UUID)); + widgetTypeDetails.setTenantId(TenantId.fromUUID(ModelConstants.NULL_UUID)); } if (!widgetTypeDetails.getTenantId().getId().equals(ModelConstants.NULL_UUID)) { Tenant tenant = tenantDao.findById(tenantId, widgetTypeDetails.getTenantId().getId()); diff --git a/dao/src/main/java/org/thingsboard/server/dao/widget/WidgetsBundleServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/widget/WidgetsBundleServiceImpl.java index 4f7bd93eca..dd6a1e8961 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/widget/WidgetsBundleServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/widget/WidgetsBundleServiceImpl.java @@ -159,7 +159,7 @@ public class WidgetsBundleServiceImpl implements WidgetsBundleService { throw new DataValidationException("Widgets bundle title should be specified!"); } if (widgetsBundle.getTenantId() == null) { - widgetsBundle.setTenantId(new TenantId(ModelConstants.NULL_UUID)); + widgetsBundle.setTenantId(TenantId.fromUUID(ModelConstants.NULL_UUID)); } if (!widgetsBundle.getTenantId().getId().equals(ModelConstants.NULL_UUID)) { Tenant tenant = tenantDao.findById(tenantId, widgetsBundle.getTenantId().getId()); diff --git a/dao/src/test/java/org/thingsboard/server/dao/nosql/CassandraPartitionsCacheTest.java b/dao/src/test/java/org/thingsboard/server/dao/nosql/CassandraPartitionsCacheTest.java index ef371569dd..cf214990b6 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/nosql/CassandraPartitionsCacheTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/nosql/CassandraPartitionsCacheTest.java @@ -96,7 +96,7 @@ public class CassandraPartitionsCacheTest { cassandraBaseTimeseriesDao.init(); UUID id = UUID.randomUUID(); - TenantId tenantId = new TenantId(id); + TenantId tenantId = TenantId.fromUUID(id); long tsKvEntryTs = System.currentTimeMillis(); for (int i = 0; i < 50000; i++) { diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/AbstractServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/AbstractServiceTest.java index c99beb9251..2f524eaa50 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/AbstractServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/AbstractServiceTest.java @@ -173,7 +173,7 @@ public abstract class AbstractServiceTest { protected Event generateEvent(TenantId tenantId, EntityId entityId, String eventType, String eventUid) throws IOException { if (tenantId == null) { - tenantId = new TenantId(Uuids.timeBased()); + tenantId = TenantId.fromUUID(Uuids.timeBased()); } Event event = new Event(); event.setTenantId(tenantId); diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/BaseAssetServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/BaseAssetServiceTest.java index a4a88d24ea..b7c9feb493 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/BaseAssetServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/BaseAssetServiceTest.java @@ -104,7 +104,7 @@ public abstract class BaseAssetServiceTest extends AbstractServiceTest { Asset asset = new Asset(); asset.setName("My asset"); asset.setType("default"); - asset.setTenantId(new TenantId(Uuids.timeBased())); + asset.setTenantId(TenantId.fromUUID(Uuids.timeBased())); assetService.saveAsset(asset); } diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/BaseCustomerServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/BaseCustomerServiceTest.java index 5991f6de2c..3b795fe09f 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/BaseCustomerServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/BaseCustomerServiceTest.java @@ -105,7 +105,7 @@ public abstract class BaseCustomerServiceTest extends AbstractServiceTest { public void testSaveCustomerWithInvalidTenant() { Customer customer = new Customer(); customer.setTitle("My customer"); - customer.setTenantId(new TenantId(Uuids.timeBased())); + customer.setTenantId(TenantId.fromUUID(Uuids.timeBased())); customerService.saveCustomer(customer); } diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/BaseDashboardServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/BaseDashboardServiceTest.java index ce307b74b0..15b39b1aa7 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/BaseDashboardServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/BaseDashboardServiceTest.java @@ -102,7 +102,7 @@ public abstract class BaseDashboardServiceTest extends AbstractServiceTest { public void testSaveDashboardWithInvalidTenant() { Dashboard dashboard = new Dashboard(); dashboard.setTitle("My dashboard"); - dashboard.setTenantId(new TenantId(Uuids.timeBased())); + dashboard.setTenantId(TenantId.fromUUID(Uuids.timeBased())); dashboardService.saveDashboard(dashboard); } diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/BaseDeviceServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/BaseDeviceServiceTest.java index 1a9432419e..dfb0434136 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/BaseDeviceServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/BaseDeviceServiceTest.java @@ -267,7 +267,7 @@ public abstract class BaseDeviceServiceTest extends AbstractServiceTest { Device device = new Device(); device.setName("My device"); device.setType("default"); - device.setTenantId(new TenantId(Uuids.timeBased())); + device.setTenantId(TenantId.fromUUID(Uuids.timeBased())); deviceService.saveDevice(device); } diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/BaseEdgeEventServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/BaseEdgeEventServiceTest.java index 51a4975dcd..e5e62384c0 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/BaseEdgeEventServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/BaseEdgeEventServiceTest.java @@ -53,7 +53,7 @@ public abstract class BaseEdgeEventServiceTest extends AbstractServiceTest { protected EdgeEvent generateEdgeEvent(TenantId tenantId, EdgeId edgeId, EntityId entityId, EdgeEventActionType edgeEventAction) throws IOException { if (tenantId == null) { - tenantId = new TenantId(Uuids.timeBased()); + tenantId = TenantId.fromUUID(Uuids.timeBased()); } EdgeEvent edgeEvent = new EdgeEvent(); edgeEvent.setTenantId(tenantId); @@ -76,7 +76,7 @@ public abstract class BaseEdgeEventServiceTest extends AbstractServiceTest { EdgeId edgeId = new EdgeId(Uuids.timeBased()); DeviceId deviceId = new DeviceId(Uuids.timeBased()); - TenantId tenantId = new TenantId(Uuids.timeBased()); + TenantId tenantId = TenantId.fromUUID(Uuids.timeBased()); saveEdgeEventWithProvidedTime(timeBeforeStartTime, edgeId, deviceId, tenantId); EdgeEvent savedEdgeEvent = saveEdgeEventWithProvidedTime(eventTime, edgeId, deviceId, tenantId); EdgeEvent savedEdgeEvent2 = saveEdgeEventWithProvidedTime(eventTime + 1, edgeId, deviceId, tenantId); @@ -105,7 +105,7 @@ public abstract class BaseEdgeEventServiceTest extends AbstractServiceTest { public void findEdgeEventsWithTsUpdateAndWithout() throws Exception { EdgeId edgeId = new EdgeId(Uuids.timeBased()); DeviceId deviceId = new DeviceId(Uuids.timeBased()); - TenantId tenantId = new TenantId(Uuids.timeBased()); + TenantId tenantId = TenantId.fromUUID(Uuids.timeBased()); TimePageLink pageLink = new TimePageLink(1, 0, null, new SortOrder("createdTime", SortOrder.Direction.ASC)); EdgeEvent edgeEventWithTsUpdate = generateEdgeEvent(tenantId, edgeId, deviceId, EdgeEventActionType.TIMESERIES_UPDATED); diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/BaseEdgeServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/BaseEdgeServiceTest.java index 631fcff8da..340afefc6d 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/BaseEdgeServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/BaseEdgeServiceTest.java @@ -100,7 +100,7 @@ public abstract class BaseEdgeServiceTest extends AbstractServiceTest { Edge edge = new Edge(); edge.setName("My edge"); edge.setType("default"); - edge.setTenantId(new TenantId(Uuids.timeBased())); + edge.setTenantId(TenantId.fromUUID(Uuids.timeBased())); edgeService.saveEdge(edge, true); } diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/BaseOtaPackageServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/BaseOtaPackageServiceTest.java index 8fcadcd812..3dc7130981 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/BaseOtaPackageServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/BaseOtaPackageServiceTest.java @@ -342,7 +342,7 @@ public abstract class BaseOtaPackageServiceTest extends AbstractServiceTest { @Test public void testSaveFirmwareWithInvalidTenant() { OtaPackage firmware = new OtaPackage(); - firmware.setTenantId(new TenantId(Uuids.timeBased())); + firmware.setTenantId(TenantId.fromUUID(Uuids.timeBased())); firmware.setDeviceProfileId(deviceProfileId); firmware.setType(FIRMWARE); firmware.setTitle(TITLE); diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/BaseRuleChainServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/BaseRuleChainServiceTest.java index c1635a6281..deaab55ac2 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/BaseRuleChainServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/BaseRuleChainServiceTest.java @@ -97,7 +97,7 @@ public abstract class BaseRuleChainServiceTest extends AbstractServiceTest { public void testSaveRuleChainWithInvalidTenant() { RuleChain ruleChain = new RuleChain(); ruleChain.setName("My RuleChain"); - ruleChain.setTenantId(new TenantId(Uuids.timeBased())); + ruleChain.setTenantId(TenantId.fromUUID(Uuids.timeBased())); ruleChainService.saveRuleChain(ruleChain); } diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/BaseWidgetTypeServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/BaseWidgetTypeServiceTest.java index ba500f950d..024745dc9e 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/BaseWidgetTypeServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/BaseWidgetTypeServiceTest.java @@ -142,7 +142,7 @@ public abstract class BaseWidgetTypeServiceTest extends AbstractServiceTest { WidgetsBundle savedWidgetsBundle = widgetsBundleService.saveWidgetsBundle(widgetsBundle); WidgetTypeDetails widgetType = new WidgetTypeDetails(); - widgetType.setTenantId(new TenantId(Uuids.timeBased())); + widgetType.setTenantId(TenantId.fromUUID(Uuids.timeBased())); widgetType.setBundleAlias(savedWidgetsBundle.getAlias()); widgetType.setName("Widget Type"); widgetType.setDescriptor(new ObjectMapper().readValue("{ \"someKey\": \"someValue\" }", JsonNode.class)); @@ -176,7 +176,7 @@ public abstract class BaseWidgetTypeServiceTest extends AbstractServiceTest { widgetType.setName("Widget Type"); widgetType.setDescriptor(new ObjectMapper().readValue("{ \"someKey\": \"someValue\" }", JsonNode.class)); WidgetTypeDetails savedWidgetType = widgetTypeService.saveWidgetType(widgetType); - savedWidgetType.setTenantId(new TenantId(ModelConstants.NULL_UUID)); + savedWidgetType.setTenantId(TenantId.fromUUID(ModelConstants.NULL_UUID)); try { widgetTypeService.saveWidgetType(savedWidgetType); } finally { diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/BaseWidgetsBundleServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/BaseWidgetsBundleServiceTest.java index 76f67c3c48..3573fbcaaf 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/BaseWidgetsBundleServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/BaseWidgetsBundleServiceTest.java @@ -88,7 +88,7 @@ public abstract class BaseWidgetsBundleServiceTest extends AbstractServiceTest { public void testSaveWidgetsBundleWithInvalidTenant() { WidgetsBundle widgetsBundle = new WidgetsBundle(); widgetsBundle.setTitle("My widgets bundle"); - widgetsBundle.setTenantId(new TenantId(Uuids.timeBased())); + widgetsBundle.setTenantId(TenantId.fromUUID(Uuids.timeBased())); widgetsBundleService.saveWidgetsBundle(widgetsBundle); } @@ -98,7 +98,7 @@ public abstract class BaseWidgetsBundleServiceTest extends AbstractServiceTest { widgetsBundle.setTitle("My widgets bundle"); widgetsBundle.setTenantId(tenantId); WidgetsBundle savedWidgetsBundle = widgetsBundleService.saveWidgetsBundle(widgetsBundle); - savedWidgetsBundle.setTenantId(new TenantId(ModelConstants.NULL_UUID)); + savedWidgetsBundle.setTenantId(TenantId.fromUUID(ModelConstants.NULL_UUID)); try { widgetsBundleService.saveWidgetsBundle(savedWidgetsBundle); } finally { @@ -160,7 +160,7 @@ public abstract class BaseWidgetsBundleServiceTest extends AbstractServiceTest { @Test public void testFindSystemWidgetsBundlesByPageLink() { - TenantId tenantId = new TenantId(ModelConstants.NULL_UUID); + TenantId tenantId = TenantId.fromUUID(ModelConstants.NULL_UUID); List systemWidgetsBundles = widgetsBundleService.findSystemWidgetsBundles(tenantId); List createdWidgetsBundles = new ArrayList<>(); @@ -204,7 +204,7 @@ public abstract class BaseWidgetsBundleServiceTest extends AbstractServiceTest { @Test public void testFindSystemWidgetsBundles() { - TenantId tenantId = new TenantId(ModelConstants.NULL_UUID); + TenantId tenantId = TenantId.fromUUID(ModelConstants.NULL_UUID); List systemWidgetsBundles = widgetsBundleService.findSystemWidgetsBundles(tenantId); @@ -290,7 +290,7 @@ public abstract class BaseWidgetsBundleServiceTest extends AbstractServiceTest { tenant = tenantService.saveTenant(tenant); TenantId tenantId = tenant.getId(); - TenantId systemTenantId = new TenantId(ModelConstants.NULL_UUID); + TenantId systemTenantId = TenantId.fromUUID(ModelConstants.NULL_UUID); List createdWidgetsBundles = new ArrayList<>(); List createdSystemWidgetsBundles = new ArrayList<>(); @@ -376,7 +376,7 @@ public abstract class BaseWidgetsBundleServiceTest extends AbstractServiceTest { tenant = tenantService.saveTenant(tenant); TenantId tenantId = tenant.getId(); - TenantId systemTenantId = new TenantId(ModelConstants.NULL_UUID); + TenantId systemTenantId = TenantId.fromUUID(ModelConstants.NULL_UUID); List createdWidgetsBundles = new ArrayList<>(); List createdSystemWidgetsBundles = new ArrayList<>(); diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/event/BaseEventServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/event/BaseEventServiceTest.java index 13b97ea9b1..9d17e5e3e8 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/event/BaseEventServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/event/BaseEventServiceTest.java @@ -68,7 +68,7 @@ public abstract class BaseEventServiceTest extends AbstractServiceTest { long timeAfterEndTime = LocalDateTime.of(2016, Month.NOVEMBER, 1, 13, 30).toEpochSecond(ZoneOffset.UTC); CustomerId customerId = new CustomerId(Uuids.timeBased()); - TenantId tenantId = new TenantId(Uuids.timeBased()); + TenantId tenantId = TenantId.fromUUID(Uuids.timeBased()); saveEventWithProvidedTime(timeBeforeStartTime, customerId, tenantId); Event savedEvent = saveEventWithProvidedTime(eventTime, customerId, tenantId); Event savedEvent2 = saveEventWithProvidedTime(eventTime+1, customerId, tenantId); @@ -103,7 +103,7 @@ public abstract class BaseEventServiceTest extends AbstractServiceTest { long timeAfterEndTime = LocalDateTime.of(2016, Month.NOVEMBER, 1, 13, 30).toEpochSecond(ZoneOffset.UTC); CustomerId customerId = new CustomerId(Uuids.timeBased()); - TenantId tenantId = new TenantId(Uuids.timeBased()); + TenantId tenantId = TenantId.fromUUID(Uuids.timeBased()); saveEventWithProvidedTime(timeBeforeStartTime, customerId, tenantId); Event savedEvent = saveEventWithProvidedTime(eventTime, customerId, tenantId); Event savedEvent2 = saveEventWithProvidedTime(eventTime+1, customerId, tenantId); diff --git a/dao/src/test/java/org/thingsboard/server/dao/sql/alarm/JpaAlarmDaoTest.java b/dao/src/test/java/org/thingsboard/server/dao/sql/alarm/JpaAlarmDaoTest.java index d5e743e5af..23ec137dac 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/sql/alarm/JpaAlarmDaoTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/sql/alarm/JpaAlarmDaoTest.java @@ -53,9 +53,9 @@ public class JpaAlarmDaoTest extends AbstractJpaDaoTest { saveAlarm(alarm1Id, tenantId, originator1Id, "TEST_ALARM"); saveAlarm(alarm2Id, tenantId, originator1Id, "TEST_ALARM"); saveAlarm(alarm3Id, tenantId, originator2Id, "TEST_ALARM"); - assertEquals(3, alarmDao.find(new TenantId(tenantId)).size()); + assertEquals(3, alarmDao.find(TenantId.fromUUID(tenantId)).size()); ListenableFuture future = alarmDao - .findLatestByOriginatorAndType(new TenantId(tenantId), new DeviceId(originator1Id), "TEST_ALARM"); + .findLatestByOriginatorAndType(TenantId.fromUUID(tenantId), new DeviceId(originator1Id), "TEST_ALARM"); Alarm alarm = future.get(); assertNotNull(alarm); assertEquals(alarm2Id, alarm.getId().getId()); @@ -64,13 +64,13 @@ public class JpaAlarmDaoTest extends AbstractJpaDaoTest { private void saveAlarm(UUID id, UUID tenantId, UUID deviceId, String type) { Alarm alarm = new Alarm(); alarm.setId(new AlarmId(id)); - alarm.setTenantId(new TenantId(tenantId)); + alarm.setTenantId(TenantId.fromUUID(tenantId)); alarm.setOriginator(new DeviceId(deviceId)); alarm.setType(type); alarm.setPropagate(true); alarm.setStartTs(System.currentTimeMillis()); alarm.setEndTs(System.currentTimeMillis()); alarm.setStatus(AlarmStatus.ACTIVE_UNACK); - alarmDao.save(new TenantId(tenantId), alarm); + alarmDao.save(TenantId.fromUUID(tenantId), alarm); } } diff --git a/dao/src/test/java/org/thingsboard/server/dao/sql/asset/JpaAssetDaoTest.java b/dao/src/test/java/org/thingsboard/server/dao/sql/asset/JpaAssetDaoTest.java index a2a8adc927..db563639a6 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/sql/asset/JpaAssetDaoTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/sql/asset/JpaAssetDaoTest.java @@ -60,7 +60,7 @@ public class JpaAssetDaoTest extends AbstractJpaDaoTest { UUID customerId = i % 2 == 0 ? customerId1 : customerId2; saveAsset(assetId, tenantId, customerId, "ASSET_" + i, "TYPE_1"); } - assertEquals(60, assetDao.find(new TenantId(tenantId1)).size()); + assertEquals(60, assetDao.find(TenantId.fromUUID(tenantId1)).size()); PageLink pageLink = new PageLink(20, 0, "ASSET_"); PageData assets1 = assetDao.findAssetsByTenantId(tenantId1, pageLink); @@ -213,10 +213,10 @@ public class JpaAssetDaoTest extends AbstractJpaDaoTest { private void saveAsset(UUID id, UUID tenantId, UUID customerId, String name, String type) { Asset asset = new Asset(); asset.setId(new AssetId(id)); - asset.setTenantId(new TenantId(tenantId)); + asset.setTenantId(TenantId.fromUUID(tenantId)); asset.setCustomerId(new CustomerId(customerId)); asset.setName(name); asset.setType(type); - assetDao.save(new TenantId(tenantId), asset); + assetDao.save(TenantId.fromUUID(tenantId), asset); } } diff --git a/dao/src/test/java/org/thingsboard/server/dao/sql/customer/JpaCustomerDaoTest.java b/dao/src/test/java/org/thingsboard/server/dao/sql/customer/JpaCustomerDaoTest.java index 9e62116225..e00f6a1dca 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/sql/customer/JpaCustomerDaoTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/sql/customer/JpaCustomerDaoTest.java @@ -76,8 +76,8 @@ public class JpaCustomerDaoTest extends AbstractJpaDaoTest { private void createCustomer(UUID tenantId, int index) { Customer customer = new Customer(); customer.setId(new CustomerId(Uuids.timeBased())); - customer.setTenantId(new TenantId(tenantId)); + customer.setTenantId(TenantId.fromUUID(tenantId)); customer.setTitle("CUSTOMER_" + index); - customerDao.save(new TenantId(tenantId), customer); + customerDao.save(TenantId.fromUUID(tenantId), customer); } } diff --git a/dao/src/test/java/org/thingsboard/server/dao/sql/dashboard/JpaDashboardInfoDaoTest.java b/dao/src/test/java/org/thingsboard/server/dao/sql/dashboard/JpaDashboardInfoDaoTest.java index 174317ec00..aa23d3dff4 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/sql/dashboard/JpaDashboardInfoDaoTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/sql/dashboard/JpaDashboardInfoDaoTest.java @@ -60,7 +60,7 @@ public class JpaDashboardInfoDaoTest extends AbstractJpaDaoTest { private void createDashboard(UUID tenantId, int index) { DashboardInfo dashboardInfo = new DashboardInfo(); dashboardInfo.setId(new DashboardId(Uuids.timeBased())); - dashboardInfo.setTenantId(new TenantId(tenantId)); + dashboardInfo.setTenantId(TenantId.fromUUID(tenantId)); dashboardInfo.setTitle("DASHBOARD_" + index); dashboardInfoDao.save(AbstractServiceTest.SYSTEM_TENANT_ID, dashboardInfo); } diff --git a/dao/src/test/java/org/thingsboard/server/dao/sql/device/JpaDeviceDaoTest.java b/dao/src/test/java/org/thingsboard/server/dao/sql/device/JpaDeviceDaoTest.java index 9d9d128966..8c83a2050b 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/sql/device/JpaDeviceDaoTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/sql/device/JpaDeviceDaoTest.java @@ -81,15 +81,15 @@ public class JpaDeviceDaoTest extends AbstractJpaDaoTest { UUID tenantId = Uuids.timeBased(); UUID customerId = Uuids.timeBased(); Device device = getDevice(tenantId, customerId); - deviceDao.save(new TenantId(tenantId), device); + deviceDao.save(TenantId.fromUUID(tenantId), device); UUID uuid = device.getId().getId(); - Device entity = deviceDao.findById(new TenantId(tenantId), uuid); + Device entity = deviceDao.findById(TenantId.fromUUID(tenantId), uuid); assertNotNull(entity); assertEquals(uuid, entity.getId().getId()); executor = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(10, ThingsBoardThreadFactory.forName(getClass().getSimpleName() + "-test-scope"))); - ListenableFuture future = executor.submit(() -> deviceDao.findById(new TenantId(tenantId), uuid)); + ListenableFuture future = executor.submit(() -> deviceDao.findById(TenantId.fromUUID(tenantId), uuid)); Device asyncDevice = future.get(); assertNotNull("Async device expected to be not null", asyncDevice); } @@ -106,8 +106,8 @@ public class JpaDeviceDaoTest extends AbstractJpaDaoTest { for(int i = 0; i < 5; i++) { UUID deviceId1 = Uuids.timeBased(); UUID deviceId2 = Uuids.timeBased(); - deviceDao.save(new TenantId(tenantId1), getDevice(tenantId1, customerId1, deviceId1)); - deviceDao.save(new TenantId(tenantId2), getDevice(tenantId2, customerId2, deviceId2)); + deviceDao.save(TenantId.fromUUID(tenantId1), getDevice(tenantId1, customerId1, deviceId1)); + deviceDao.save(TenantId.fromUUID(tenantId2), getDevice(tenantId2, customerId2, deviceId2)); deviceIds.add(deviceId1); deviceIds.add(deviceId2); } @@ -129,8 +129,8 @@ public class JpaDeviceDaoTest extends AbstractJpaDaoTest { for(int i = 0; i < 20; i++) { UUID deviceId1 = Uuids.timeBased(); UUID deviceId2 = Uuids.timeBased(); - deviceDao.save(new TenantId(tenantId1), getDevice(tenantId1, customerId1, deviceId1)); - deviceDao.save(new TenantId(tenantId2), getDevice(tenantId2, customerId2, deviceId2)); + deviceDao.save(TenantId.fromUUID(tenantId1), getDevice(tenantId1, customerId1, deviceId1)); + deviceDao.save(TenantId.fromUUID(tenantId2), getDevice(tenantId2, customerId2, deviceId2)); deviceIds.add(deviceId1); deviceIds.add(deviceId2); } @@ -142,8 +142,8 @@ public class JpaDeviceDaoTest extends AbstractJpaDaoTest { private void createDevices(UUID tenantId1, UUID tenantId2, UUID customerId1, UUID customerId2, int count) { for (int i = 0; i < count / 2; i++) { - deviceDao.save(new TenantId(tenantId1), getDevice(tenantId1, customerId1)); - deviceDao.save(new TenantId(tenantId2), getDevice(tenantId2, customerId2)); + deviceDao.save(TenantId.fromUUID(tenantId1), getDevice(tenantId1, customerId1)); + deviceDao.save(TenantId.fromUUID(tenantId2), getDevice(tenantId2, customerId2)); } } @@ -154,7 +154,7 @@ public class JpaDeviceDaoTest extends AbstractJpaDaoTest { private Device getDevice(UUID tenantId, UUID customerID, UUID deviceId) { Device device = new Device(); device.setId(new DeviceId(deviceId)); - device.setTenantId(new TenantId(tenantId)); + device.setTenantId(TenantId.fromUUID(tenantId)); device.setCustomerId(new CustomerId(customerID)); device.setName("SEARCH_TEXT"); return device; diff --git a/dao/src/test/java/org/thingsboard/server/dao/sql/event/JpaBaseEventDaoTest.java b/dao/src/test/java/org/thingsboard/server/dao/sql/event/JpaBaseEventDaoTest.java index 57b9519f44..41f0c6985c 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/sql/event/JpaBaseEventDaoTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/sql/event/JpaBaseEventDaoTest.java @@ -149,10 +149,10 @@ public class JpaBaseEventDaoTest extends AbstractJpaDaoTest { String type = i % 2 == 0 ? STATS : ALARM; UUID eventId1 = Uuids.timeBased(); Event event1 = getEvent(eventId1, tenantId, entityId1, type); - eventDao.save(new TenantId(tenantId), event1); + eventDao.save(TenantId.fromUUID(tenantId), event1); UUID eventId2 = Uuids.timeBased(); Event event2 = getEvent(eventId2, tenantId, entityId2, type); - eventDao.save(new TenantId(tenantId), event2); + eventDao.save(TenantId.fromUUID(tenantId), event2); } return System.currentTimeMillis(); } @@ -161,10 +161,10 @@ public class JpaBaseEventDaoTest extends AbstractJpaDaoTest { for (int i = 0; i < count / 2; i++) { UUID eventId1 = Uuids.timeBased(); Event event1 = getEvent(eventId1, tenantId, entityId1); - eventDao.save(new TenantId(tenantId), event1); + eventDao.save(TenantId.fromUUID(tenantId), event1); UUID eventId2 = Uuids.timeBased(); Event event2 = getEvent(eventId2, tenantId, entityId2); - eventDao.save(new TenantId(tenantId), event2); + eventDao.save(TenantId.fromUUID(tenantId), event2); } return System.currentTimeMillis(); } @@ -178,7 +178,7 @@ public class JpaBaseEventDaoTest extends AbstractJpaDaoTest { private Event getEvent(UUID eventId, UUID tenantId, UUID entityId) { Event event = new Event(); event.setId(new EventId(eventId)); - event.setTenantId(new TenantId(tenantId)); + event.setTenantId(TenantId.fromUUID(tenantId)); EntityId deviceId = new DeviceId(entityId); event.setEntityId(deviceId); event.setUid(event.getId().getId().toString()); diff --git a/dao/src/test/java/org/thingsboard/server/dao/sql/tenant/JpaTenantDaoTest.java b/dao/src/test/java/org/thingsboard/server/dao/sql/tenant/JpaTenantDaoTest.java index 3b31be7a04..b965bd2a0c 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/sql/tenant/JpaTenantDaoTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/sql/tenant/JpaTenantDaoTest.java @@ -69,7 +69,7 @@ public class JpaTenantDaoTest extends AbstractJpaDaoTest { private void createTenant(String region, String title, int index) { Tenant tenant = new Tenant(); - tenant.setId(new TenantId(Uuids.timeBased())); + tenant.setId(TenantId.fromUUID(Uuids.timeBased())); tenant.setRegion(region); tenant.setTitle(title + "_" + index); tenantDao.save(AbstractServiceTest.SYSTEM_TENANT_ID, tenant); diff --git a/dao/src/test/java/org/thingsboard/server/dao/sql/user/JpaUserDaoTest.java b/dao/src/test/java/org/thingsboard/server/dao/sql/user/JpaUserDaoTest.java index b22dd08381..213f9c1d6b 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/sql/user/JpaUserDaoTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/sql/user/JpaUserDaoTest.java @@ -111,7 +111,7 @@ public class JpaUserDaoTest extends AbstractJpaDaoTest { public void testSave() throws IOException { User user = new User(); user.setId(new UserId(UUID.fromString("cd481534-27cc-11e7-93ae-92361f002671"))); - user.setTenantId(new TenantId(UUID.fromString("1edcb2c6-27cb-11e7-93ae-92361f002671"))); + user.setTenantId(TenantId.fromUUID(UUID.fromString("1edcb2c6-27cb-11e7-93ae-92361f002671"))); user.setCustomerId(new CustomerId(UUID.fromString("51477cb4-27cb-11e7-93ae-92361f002671"))); user.setEmail("user@thingsboard.org"); user.setFirstName("Jackson"); @@ -140,7 +140,7 @@ public class JpaUserDaoTest extends AbstractJpaDaoTest { User user = new User(); UUID id = Uuids.timeBased(); user.setId(new UserId(id)); - user.setTenantId(new TenantId(tenantId)); + user.setTenantId(TenantId.fromUUID(tenantId)); user.setCustomerId(new CustomerId(customerId)); if (customerId == NULL_UUID) { user.setAuthority(Authority.TENANT_ADMIN); diff --git a/dao/src/test/java/org/thingsboard/server/dao/sql/widget/JpaWidgetsBundleDaoTest.java b/dao/src/test/java/org/thingsboard/server/dao/sql/widget/JpaWidgetsBundleDaoTest.java index 11e2a47af0..54a97ea088 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/sql/widget/JpaWidgetsBundleDaoTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/sql/widget/JpaWidgetsBundleDaoTest.java @@ -149,7 +149,7 @@ public class JpaWidgetsBundleDaoTest extends AbstractJpaDaoTest { widgetsBundle.setAlias(prefix + i); widgetsBundle.setTitle(prefix + i); widgetsBundle.setId(new WidgetsBundleId(Uuids.timeBased())); - widgetsBundle.setTenantId(new TenantId(tenantId)); + widgetsBundle.setTenantId(TenantId.fromUUID(tenantId)); widgetsBundleDao.save(AbstractServiceTest.SYSTEM_TENANT_ID, widgetsBundle); } } diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCreateRelationNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCreateRelationNode.java index 28eafabfce..05b3bdc1d0 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCreateRelationNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCreateRelationNode.java @@ -208,7 +208,7 @@ public class TbCreateRelationNode extends TbAbstractRelationActionNode processTenant(TbContext ctx, EntityContainer entityContainer, SearchDirectionIds sdId, String relationType) { - return Futures.transformAsync(ctx.getTenantService().findTenantByIdAsync(ctx.getTenantId(), new TenantId(entityContainer.getEntityId().getId())), tenant -> { + return Futures.transformAsync(ctx.getTenantService().findTenantByIdAsync(ctx.getTenantId(), TenantId.fromUUID(entityContainer.getEntityId().getId())), tenant -> { if (tenant != null) { return processSave(ctx, sdId, relationType); } else { diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbAlarmNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbAlarmNodeTest.java index 0b800eb22c..e2de0bb55f 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbAlarmNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbAlarmNodeTest.java @@ -98,7 +98,7 @@ public class TbAlarmNodeTest { private EntityId originator = new DeviceId(Uuids.timeBased()); private EntityId alarmOriginator = new AlarmId(Uuids.timeBased()); - private TenantId tenantId = new TenantId(Uuids.timeBased()); + private TenantId tenantId = TenantId.fromUUID(Uuids.timeBased()); private TbMsgMetaData metaData = new TbMsgMetaData(); private String rawJson = "{\"name\": \"Vit\", \"passed\": 5}"; diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/AbstractAttributeNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/AbstractAttributeNodeTest.java index a0f2879851..b8265e2a2e 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/AbstractAttributeNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/AbstractAttributeNodeTest.java @@ -68,7 +68,7 @@ import static org.thingsboard.server.common.data.DataConstants.SERVER_SCOPE; @RunWith(MockitoJUnitRunner.class) public abstract class AbstractAttributeNodeTest { final CustomerId customerId = new CustomerId(Uuids.timeBased()); - final TenantId tenantId = new TenantId(Uuids.timeBased()); + final TenantId tenantId = TenantId.fromUUID(Uuids.timeBased()); final RuleChainId ruleChainId = new RuleChainId(Uuids.timeBased()); final RuleNodeId ruleNodeId = new RuleNodeId(Uuids.timeBased()); final String keyAttrConf = "${word}"; diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/profile/TbDeviceProfileNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/profile/TbDeviceProfileNodeTest.java index b2c79102c4..0f88f9baac 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/profile/TbDeviceProfileNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/profile/TbDeviceProfileNodeTest.java @@ -99,7 +99,7 @@ public class TbDeviceProfileNodeTest { @Mock private AttributesService attributesService; - private final TenantId tenantId = new TenantId(UUID.randomUUID()); + private final TenantId tenantId = TenantId.fromUUID(UUID.randomUUID()); private final DeviceId deviceId = new DeviceId(UUID.randomUUID()); private final CustomerId customerId = new CustomerId(UUID.randomUUID()); private final DeviceProfileId deviceProfileId = new DeviceProfileId(UUID.randomUUID()); From 8611349e4b8c08add2206777475d4e2808088891 Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Mon, 10 Jan 2022 15:03:15 +0200 Subject: [PATCH 071/798] TenantId static initializer --- .../org/thingsboard/server/common/data/id/TenantId.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/id/TenantId.java b/common/data/src/main/java/org/thingsboard/server/common/data/id/TenantId.java index 665edc0ca9..77a3480f0a 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/id/TenantId.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/id/TenantId.java @@ -28,10 +28,14 @@ import java.util.UUID; public final class TenantId extends UUIDBased implements EntityId { @JsonIgnore - public static final TenantId SYS_TENANT_ID = TenantId.fromUUID(EntityId.NULL_UUID); + public static final TenantId SYS_TENANT_ID = new TenantId(EntityId.NULL_UUID); static final ConcurrentReferenceHashMap tenants = new ConcurrentReferenceHashMap<>(16, ReferenceType.SOFT); + static { + tenants.put(SYS_TENANT_ID.getId(), SYS_TENANT_ID); + } + private static final long serialVersionUID = 1L; @JsonCreator From fa1cd21473e5fe6e2c21758b95846ae39685ea18 Mon Sep 17 00:00:00 2001 From: ArtemDzhereleiko Date: Mon, 10 Jan 2022 15:21:44 +0200 Subject: [PATCH 072/798] UI: Fixed cancel event on color picker --- .../shared/components/dialog/color-picker-dialog.component.ts | 2 +- .../src/app/shared/components/json-form/json-form.component.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ui-ngx/src/app/shared/components/dialog/color-picker-dialog.component.ts b/ui-ngx/src/app/shared/components/dialog/color-picker-dialog.component.ts index 2374749cbc..499f857df8 100644 --- a/ui-ngx/src/app/shared/components/dialog/color-picker-dialog.component.ts +++ b/ui-ngx/src/app/shared/components/dialog/color-picker-dialog.component.ts @@ -67,7 +67,7 @@ export class ColorPickerDialogComponent extends DialogComponent void) { this.dialogs.colorPicker(tinycolor(val).toRgbString()).subscribe((color) => { - if (colorSelectedFn) { + if (color && colorSelectedFn) { colorSelectedFn(tinycolor(color).toRgb()); } }); From 6875df9dab6750d2ba0fe2b55c844e639fbfc343 Mon Sep 17 00:00:00 2001 From: Kalutka Zhenya Date: Mon, 10 Jan 2022 16:49:15 +0200 Subject: [PATCH 073/798] Added Dynamic value to alarm schedule --- .../alarm/alarm-schedule.component.html | 40 ++++++++++++++++++- .../profile/alarm/alarm-schedule.component.ts | 28 +++++++++++-- ui-ngx/src/app/shared/models/device.models.ts | 4 ++ .../assets/locale/locale.constant-en_US.json | 1 + 4 files changed, 68 insertions(+), 5 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/profile/alarm/alarm-schedule.component.html b/ui-ngx/src/app/modules/home/components/profile/alarm/alarm-schedule.component.html index bf09b33d76..5d63b48f15 100644 --- a/ui-ngx/src/app/modules/home/components/profile/alarm/alarm-schedule.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/alarm/alarm-schedule.component.html @@ -73,8 +73,44 @@
-
device-profile.schedule-days
- + + +
+ +
+ {{'filter.dynamic-value' | translate}} +
+
+ +
+
+ +
+
+
+ + + + + {{'filter.no-dynamic-value' | translate}} + + + {{dynamicValueSourceTypeTranslations.get(sourceType) | translate}} + + + +
+
+ + + + +
+
+
+
+
+
device-profile.schedule-days
diff --git a/ui-ngx/src/app/modules/home/components/profile/alarm/alarm-schedule.component.ts b/ui-ngx/src/app/modules/home/components/profile/alarm/alarm-schedule.component.ts index d5c9f02876..6fd679f3f7 100644 --- a/ui-ngx/src/app/modules/home/components/profile/alarm/alarm-schedule.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/alarm/alarm-schedule.component.ts @@ -40,6 +40,12 @@ import { import { isDefined, isDefinedAndNotNull } from '@core/utils'; import { MatCheckboxChange } from '@angular/material/checkbox'; import { getDefaultTimezone } from '@shared/models/time/time.models'; +import { + DynamicValueSourceType, + dynamicValueSourceTypeTranslationMap, + getDynamicSourcesForAllowUser +} from '@shared/models/query/query.models'; +import { emit } from 'cluster'; @Component({ selector: 'tb-alarm-schedule', @@ -64,7 +70,8 @@ export class AlarmScheduleComponent implements ControlValueAccessor, Validator, alarmScheduleTypes = Object.keys(AlarmScheduleType); alarmScheduleType = AlarmScheduleType; alarmScheduleTypeTranslate = AlarmScheduleTypeTranslationMap; - + dynamicValueSourceTypes: DynamicValueSourceType[] = getDynamicSourcesForAllowUser(false); + dynamicValueSourceTypeTranslations = dynamicValueSourceTypeTranslationMap; dayOfWeekTranslationsArray = dayOfWeekTranslations; allDays = Array(7).fill(0).map((x, i) => i); @@ -91,8 +98,19 @@ export class AlarmScheduleComponent implements ControlValueAccessor, Validator, daysOfWeek: this.fb.array(new Array(7).fill(false), this.validateDayOfWeeks), startsOn: [0, Validators.required], endsOn: [0, Validators.required], - items: this.fb.array(Array.from({length: 7}, (value, i) => this.defaultItemsScheduler(i)), this.validateItems) + items: this.fb.array(Array.from({length: 7}, (value, i) => this.defaultItemsScheduler(i)), this.validateItems), + dynamicValue: this.fb.group({ + sourceType: [null], + sourceAttribute: [null] + }) }); + + this.alarmScheduleForm.get('dynamicValue.sourceType').valueChanges.subscribe((sourceType) => { + if (!sourceType) { + this.alarmScheduleForm.get('dynamicValue.sourceAttribute').patchValue('', {emitEvent:false}); + } + }) + this.alarmScheduleForm.get('type').valueChanges.subscribe((type) => { const defaultTimezone = getDefaultTimezone(); this.alarmScheduleForm.reset({type, items: this.defaultItems, timezone: defaultTimezone}, {emitEvent: false}); @@ -177,7 +195,11 @@ export class AlarmScheduleComponent implements ControlValueAccessor, Validator, this.alarmScheduleForm.patchValue({ type: this.modelValue.type, timezone: this.modelValue.timezone, - items: alarmDays + items: alarmDays, + dynamicValue: { + sourceAttribute: this.modelValue.dynamicValue.sourceAttribute, + sourceType: this.modelValue.dynamicValue.sourceType + } }, {emitEvent: false}); } break; diff --git a/ui-ngx/src/app/shared/models/device.models.ts b/ui-ngx/src/app/shared/models/device.models.ts index 7fc3d70f78..ffb944803c 100644 --- a/ui-ngx/src/app/shared/models/device.models.ts +++ b/ui-ngx/src/app/shared/models/device.models.ts @@ -477,6 +477,10 @@ export const AlarmScheduleTypeTranslationMap = new Map Date: Mon, 10 Jan 2022 17:16:30 +0200 Subject: [PATCH 074/798] TenantId static initializer replaced with a static variable order --- .../org/thingsboard/server/common/data/id/TenantId.java | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/id/TenantId.java b/common/data/src/main/java/org/thingsboard/server/common/data/id/TenantId.java index 77a3480f0a..1d62d8e3f2 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/id/TenantId.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/id/TenantId.java @@ -28,13 +28,10 @@ import java.util.UUID; public final class TenantId extends UUIDBased implements EntityId { @JsonIgnore - public static final TenantId SYS_TENANT_ID = new TenantId(EntityId.NULL_UUID); - static final ConcurrentReferenceHashMap tenants = new ConcurrentReferenceHashMap<>(16, ReferenceType.SOFT); - static { - tenants.put(SYS_TENANT_ID.getId(), SYS_TENANT_ID); - } + @JsonIgnore + public static final TenantId SYS_TENANT_ID = TenantId.fromUUID(EntityId.NULL_UUID); private static final long serialVersionUID = 1L; From 0ef9d5753cd271fd7c8729567b5a2765943538d1 Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Mon, 10 Jan 2022 17:36:57 +0200 Subject: [PATCH 075/798] MqttTransportHandler refactored to not reference on InetSocketAddress instances to free some heap space. IPv4 are stored and logged as int. --- .../transport/mqtt/MqttTransportHandler.java | 25 +++++++++++++------ .../mqtt/session/DeviceSessionCtx.java | 2 +- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java index bf474cc266..fe39721f27 100644 --- a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java +++ b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java @@ -76,6 +76,7 @@ import org.thingsboard.server.transport.mqtt.session.MqttTopicMatcher; import javax.net.ssl.SSLPeerUnverifiedException; import java.io.IOException; import java.net.InetSocketAddress; +import java.nio.ByteBuffer; import java.security.cert.Certificate; import java.security.cert.X509Certificate; import java.util.ArrayList; @@ -111,7 +112,6 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement private static final Pattern FW_REQUEST_PATTERN = Pattern.compile(MqttTopics.DEVICE_FIRMWARE_REQUEST_TOPIC_PATTERN); private static final Pattern SW_REQUEST_PATTERN = Pattern.compile(MqttTopics.DEVICE_SOFTWARE_REQUEST_TOPIC_PATTERN); - private static final String PAYLOAD_TOO_LARGE = "PAYLOAD_TOO_LARGE"; private static final MqttQoS MAX_SUPPORTED_QOS_LVL = AT_LEAST_ONCE; @@ -124,7 +124,8 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement private final ConcurrentMap mqttQoSMap; final DeviceSessionCtx deviceSessionCtx; - volatile InetSocketAddress address; + volatile int ip = 0; + volatile int port = 0; volatile GatewaySessionHandler gatewaySessionHandler; private final ConcurrentHashMap otaPackSessions; @@ -183,9 +184,14 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement } void processMqttMsg(ChannelHandlerContext ctx, MqttMessage msg) { - address = getAddress(ctx); + if (port == 0) { + InetSocketAddress address = getAddress(ctx); + ip = getIpv4(address); //ipv6 will not appear in logs + port = address.getPort(); + } + if (msg.fixedHeader() == null) { - log.info("[{}:{}] Invalid message received", address.getHostName(), address.getPort()); + log.info("[{}:{}] Invalid message received", ip, port); ctx.close(); return; } @@ -199,6 +205,11 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement } } + int getIpv4(InetSocketAddress address) { + byte[] ipBytes = address.getAddress().getAddress(); + return ipBytes.length == 4 ? ByteBuffer.wrap(ipBytes).getInt() : -1; + } + InetSocketAddress getAddress(ChannelHandlerContext ctx) { return (InetSocketAddress) ctx.channel().remoteAddress(); } @@ -791,7 +802,7 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement @Override public void onError(Throwable e) { - log.trace("[{}] Failed to process credentials: {}", address, userName, e); + log.trace("[{}] Failed to process credentials: {}", ip, userName, e); ctx.writeAndFlush(createMqttConnAckMsg(MqttConnectReturnCode.CONNECTION_REFUSED_SERVER_UNAVAILABLE, connectMessage)); ctx.close(); } @@ -814,14 +825,14 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement @Override public void onError(Throwable e) { - log.trace("[{}] Failed to process credentials: {}", address, sha3Hash, e); + log.trace("[{}] Failed to process credentials: {}", ip, sha3Hash, e); ctx.writeAndFlush(createMqttConnAckMsg(MqttConnectReturnCode.CONNECTION_REFUSED_SERVER_UNAVAILABLE, connectMessage)); ctx.close(); } }); } catch (Exception e) { ctx.writeAndFlush(createMqttConnAckMsg(CONNECTION_REFUSED_NOT_AUTHORIZED, connectMessage)); - log.trace("[{}] X509 auth failure: {}", sessionId, address, e); + log.trace("[{}] X509 auth failure: {}", sessionId, ip, e); ctx.close(); } } diff --git a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/DeviceSessionCtx.java b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/DeviceSessionCtx.java index 9f0c89714a..76e2911793 100644 --- a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/DeviceSessionCtx.java +++ b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/DeviceSessionCtx.java @@ -238,7 +238,7 @@ public class DeviceSessionCtx extends MqttDeviceAwareSessionContext { public void release() { if (!msgQueue.isEmpty()) { - log.warn("doDisconnect for device {} but unprocessed messages {} left in the msg queue", getDeviceId(), msgQueue.size()); + log.warn("doDisconnect for device {} but unprocessed messages {} left in the msg queue. cleared", getDeviceId(), getMsgQueueSize()); msgQueue.forEach(ReferenceCountUtil::safeRelease); msgQueue.clear(); } From ef53df75128a0502c5727d4229acd37cd79c1aaa Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Mon, 10 Jan 2022 18:41:53 +0200 Subject: [PATCH 076/798] MqttTransportHandler fixed test --- .../server/transport/mqtt/MqttTransportHandlerTest.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/common/transport/mqtt/src/test/java/org/thingsboard/server/transport/mqtt/MqttTransportHandlerTest.java b/common/transport/mqtt/src/test/java/org/thingsboard/server/transport/mqtt/MqttTransportHandlerTest.java index a9903fe867..7701a82cbe 100644 --- a/common/transport/mqtt/src/test/java/org/thingsboard/server/transport/mqtt/MqttTransportHandlerTest.java +++ b/common/transport/mqtt/src/test/java/org/thingsboard/server/transport/mqtt/MqttTransportHandlerTest.java @@ -23,7 +23,6 @@ import io.netty.handler.codec.mqtt.MqttConnectMessage; import io.netty.handler.codec.mqtt.MqttConnectPayload; import io.netty.handler.codec.mqtt.MqttConnectVariableHeader; import io.netty.handler.codec.mqtt.MqttFixedHeader; -import io.netty.handler.codec.mqtt.MqttMessage; import io.netty.handler.codec.mqtt.MqttMessageType; import io.netty.handler.codec.mqtt.MqttPublishMessage; import io.netty.handler.codec.mqtt.MqttPublishVariableHeader; @@ -118,7 +117,8 @@ public class MqttTransportHandlerTest { handler.processMqttMsg(ctx, msg); - assertThat(handler.address, is(IP_ADDR)); + assertThat(handler.ip, is(handler.getIpv4(IP_ADDR))); + assertThat(handler.port, is(IP_ADDR.getPort())); assertThat(handler.deviceSessionCtx.getChannel(), is(ctx)); verify(handler, never()).doDisconnect(); verify(handler, times(1)).processConnect(ctx, msg); @@ -154,7 +154,7 @@ public class MqttTransportHandlerTest { messages.forEach((msg) -> handler.processMqttMsg(ctx, msg)); - assertThat(handler.address, is(IP_ADDR)); + assertThat(handler.ip, is(handler.getIpv4(IP_ADDR))); assertThat(handler.deviceSessionCtx.getChannel(), is(ctx)); assertThat(handler.deviceSessionCtx.isConnected(), is(false)); assertThat(handler.deviceSessionCtx.getMsgQueueSize(), is(MSG_QUEUE_LIMIT)); From c4d180eecc3f147bbf35735074b0ce957b31d8f3 Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Mon, 10 Jan 2022 19:25:56 +0200 Subject: [PATCH 077/798] DeviceActorMessageProcessor(AbstractContextAwareMsgProcessor): ObjectMapper live instance count reduced with a static ObjectMapper --- .../actors/shared/AbstractContextAwareMsgProcessor.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/actors/shared/AbstractContextAwareMsgProcessor.java b/application/src/main/java/org/thingsboard/server/actors/shared/AbstractContextAwareMsgProcessor.java index a5de546cb6..0a89cabe5d 100644 --- a/application/src/main/java/org/thingsboard/server/actors/shared/AbstractContextAwareMsgProcessor.java +++ b/application/src/main/java/org/thingsboard/server/actors/shared/AbstractContextAwareMsgProcessor.java @@ -22,13 +22,13 @@ import org.thingsboard.server.actors.TbActorCtx; import org.thingsboard.server.common.msg.TbActorMsg; import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.TimeUnit; @Slf4j public abstract class AbstractContextAwareMsgProcessor { + protected final static ObjectMapper mapper = new ObjectMapper(); + protected final ActorSystemContext systemContext; - protected final ObjectMapper mapper = new ObjectMapper(); protected AbstractContextAwareMsgProcessor(ActorSystemContext systemContext) { super(); From f5c09d221de6ce6f307edc59e3ee0db39a382d3e Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Mon, 10 Jan 2022 23:47:50 +0200 Subject: [PATCH 078/798] TbMsgMetaData refactored constructor and copy method --- .../server/common/msg/TbMsgMetaData.java | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/TbMsgMetaData.java b/common/message/src/main/java/org/thingsboard/server/common/msg/TbMsgMetaData.java index 1fa6f0d4f4..8f23a917a2 100644 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/TbMsgMetaData.java +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/TbMsgMetaData.java @@ -15,9 +15,7 @@ */ package org.thingsboard.server.common.msg; -import lombok.AllArgsConstructor; import lombok.Data; -import lombok.NoArgsConstructor; import java.io.Serializable; import java.util.Collections; @@ -29,15 +27,18 @@ import java.util.concurrent.ConcurrentHashMap; * Created by ashvayka on 13.01.18. */ @Data -@NoArgsConstructor public final class TbMsgMetaData implements Serializable { public static final TbMsgMetaData EMPTY = new TbMsgMetaData(Collections.emptyMap()); - private final Map data = new ConcurrentHashMap<>(); + private final Map data; + + public TbMsgMetaData() { + this.data = new ConcurrentHashMap<>(); + } public TbMsgMetaData(Map data) { - data.forEach((key, val) -> putValue(key, val)); + this.data = new ConcurrentHashMap<>(data); } public String getValue(String key) { @@ -55,6 +56,6 @@ public final class TbMsgMetaData implements Serializable { } public TbMsgMetaData copy() { - return new TbMsgMetaData(new ConcurrentHashMap<>(data)); + return new TbMsgMetaData(data); } } From a11146b44592d13ef636e397a24250ebe4e910cb Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Mon, 10 Jan 2022 23:55:58 +0200 Subject: [PATCH 079/798] immutable TbMsgMetaData.EMPTY --- .../org/thingsboard/server/common/msg/TbMsgMetaData.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/TbMsgMetaData.java b/common/message/src/main/java/org/thingsboard/server/common/msg/TbMsgMetaData.java index 8f23a917a2..2376a7a35f 100644 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/TbMsgMetaData.java +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/TbMsgMetaData.java @@ -29,7 +29,7 @@ import java.util.concurrent.ConcurrentHashMap; @Data public final class TbMsgMetaData implements Serializable { - public static final TbMsgMetaData EMPTY = new TbMsgMetaData(Collections.emptyMap()); + public static final TbMsgMetaData EMPTY = new TbMsgMetaData(0); private final Map data; @@ -41,6 +41,13 @@ public final class TbMsgMetaData implements Serializable { this.data = new ConcurrentHashMap<>(data); } + /** + * Internal constructor to create immutable TbMsgMetaData.EMPTY + * */ + private TbMsgMetaData(int ignored) { + this.data = Collections.emptyMap(); + } + public String getValue(String key) { return data.get(key); } From d46575aeafddda85cf37b4de9b36103e660d5607 Mon Sep 17 00:00:00 2001 From: Nikitozin Date: Tue, 11 Jan 2022 11:49:28 +0200 Subject: [PATCH 080/798] [WIP][3.3.3] Add statement for support double type. --- .../engine/action/TbSaveToCustomCassandraTableNode.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbSaveToCustomCassandraTableNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbSaveToCustomCassandraTableNode.java index a56cb644be..bd3371edae 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbSaveToCustomCassandraTableNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbSaveToCustomCassandraTableNode.java @@ -182,7 +182,11 @@ public class TbSaveToCustomCassandraTableNode implements TbNode { if (dataAsObject.get(key).isJsonPrimitive()) { JsonPrimitive primitive = dataAsObject.get(key).getAsJsonPrimitive(); if (primitive.isNumber()) { - stmtBuilder.setLong(i.get(), dataAsObject.get(key).getAsLong()); + if (dataAsObject.get(key).getAsString().contains(".")) { + stmtBuilder.setDouble(i.get(), dataAsObject.get(key).getAsDouble()); + } else { + stmtBuilder.setLong(i.get(), dataAsObject.get(key).getAsLong()); + } } else if (primitive.isBoolean()) { stmtBuilder.setBoolean(i.get(), dataAsObject.get(key).getAsBoolean()); } else if (primitive.isString()) { From b0a79e9fc3236dffd2a763274cb7c9d15cc797fa Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Tue, 11 Jan 2022 12:23:48 +0200 Subject: [PATCH 081/798] static transport immutable proto SESSION_EVENT_MSG_OPEN, SESSION_EVENT_MSG_CLOSED, SESSION_CLOSE_NOTIFICATION_PROTO, SUBSCRIBE_TO_ATTRIBUTE_UPDATES_ASYNC_MSG, SUBSCRIBE_TO_RPC_ASYNC_MSG --- .../session/DefaultLwM2MSessionManager.java | 14 +++++++---- .../transport/mqtt/MqttTransportHandler.java | 7 +++--- .../mqtt/session/GatewaySessionHandler.java | 15 ++++++------ .../service/DefaultTransportService.java | 24 ++++++++++--------- 4 files changed, 34 insertions(+), 26 deletions(-) diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/session/DefaultLwM2MSessionManager.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/session/DefaultLwM2MSessionManager.java index ee6e4a779b..a9198f9fcd 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/session/DefaultLwM2MSessionManager.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/session/DefaultLwM2MSessionManager.java @@ -19,7 +19,6 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.thingsboard.server.common.transport.TransportService; -import org.thingsboard.server.common.transport.service.DefaultTransportService; import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.queue.util.TbLwM2mTransportComponent; import org.thingsboard.server.transport.lwm2m.server.LwM2mSessionMsgListener; @@ -27,6 +26,11 @@ import org.thingsboard.server.transport.lwm2m.server.attributes.LwM2MAttributesS import org.thingsboard.server.transport.lwm2m.server.rpc.LwM2MRpcRequestHandler; import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler; +import static org.thingsboard.server.common.transport.service.DefaultTransportService.SESSION_EVENT_MSG_CLOSED; +import static org.thingsboard.server.common.transport.service.DefaultTransportService.SESSION_EVENT_MSG_OPEN; +import static org.thingsboard.server.common.transport.service.DefaultTransportService.SUBSCRIBE_TO_ATTRIBUTE_UPDATES_ASYNC_MSG; +import static org.thingsboard.server.common.transport.service.DefaultTransportService.SUBSCRIBE_TO_RPC_ASYNC_MSG; + @Slf4j @Service @TbLwM2mTransportComponent @@ -52,16 +56,16 @@ public class DefaultLwM2MSessionManager implements LwM2MSessionManager { transportService.registerAsyncSession(sessionInfo, new LwM2mSessionMsgListener(uplinkHandler, attributesService, rpcHandler, sessionInfo, transportService)); TransportProtos.TransportToDeviceActorMsg msg = TransportProtos.TransportToDeviceActorMsg.newBuilder() .setSessionInfo(sessionInfo) - .setSessionEvent(DefaultTransportService.getSessionEventMsg(TransportProtos.SessionEvent.OPEN)) - .setSubscribeToAttributes(TransportProtos.SubscribeToAttributeUpdatesMsg.newBuilder().setSessionType(TransportProtos.SessionType.ASYNC).build()) - .setSubscribeToRPC(TransportProtos.SubscribeToRPCMsg.newBuilder().setSessionType(TransportProtos.SessionType.ASYNC).build()) + .setSessionEvent(SESSION_EVENT_MSG_OPEN) + .setSubscribeToAttributes(SUBSCRIBE_TO_ATTRIBUTE_UPDATES_ASYNC_MSG) + .setSubscribeToRPC(SUBSCRIBE_TO_RPC_ASYNC_MSG) .build(); transportService.process(msg, null); } @Override public void deregister(TransportProtos.SessionInfoProto sessionInfo) { - transportService.process(sessionInfo, DefaultTransportService.getSessionEventMsg(TransportProtos.SessionEvent.CLOSED), null); + transportService.process(sessionInfo, SESSION_EVENT_MSG_CLOSED, null); transportService.deregisterSession(sessionInfo); } } diff --git a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java index fe39721f27..c99b4e1a45 100644 --- a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java +++ b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java @@ -65,7 +65,6 @@ import org.thingsboard.server.common.transport.service.SessionMetaData; import org.thingsboard.server.common.transport.util.SslUtil; import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.gen.transport.TransportProtos.ProvisionDeviceResponseMsg; -import org.thingsboard.server.gen.transport.TransportProtos.SessionEvent; import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceX509CertRequestMsg; import org.thingsboard.server.queue.scheduler.SchedulerComponent; import org.thingsboard.server.transport.mqtt.adaptors.MqttTransportAdaptor; @@ -102,6 +101,8 @@ import static io.netty.handler.codec.mqtt.MqttMessageType.UNSUBACK; import static io.netty.handler.codec.mqtt.MqttQoS.AT_LEAST_ONCE; import static io.netty.handler.codec.mqtt.MqttQoS.AT_MOST_ONCE; import static io.netty.handler.codec.mqtt.MqttQoS.FAILURE; +import static org.thingsboard.server.common.transport.service.DefaultTransportService.SESSION_EVENT_MSG_CLOSED; +import static org.thingsboard.server.common.transport.service.DefaultTransportService.SESSION_EVENT_MSG_OPEN; /** * @author Andrew Shvayka @@ -929,7 +930,7 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement public void doDisconnect() { if (deviceSessionCtx.isConnected()) { log.debug("[{}] Client disconnected!", sessionId); - transportService.process(deviceSessionCtx.getSessionInfo(), DefaultTransportService.getSessionEventMsg(SessionEvent.CLOSED), null); + transportService.process(deviceSessionCtx.getSessionInfo(), SESSION_EVENT_MSG_CLOSED, null); transportService.deregisterSession(deviceSessionCtx.getSessionInfo()); if (gatewaySessionHandler != null) { gatewaySessionHandler.onGatewayDisconnect(); @@ -948,7 +949,7 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement deviceSessionCtx.setDeviceInfo(msg.getDeviceInfo()); deviceSessionCtx.setDeviceProfile(msg.getDeviceProfile()); deviceSessionCtx.setSessionInfo(SessionInfoCreator.create(msg, context, sessionId)); - transportService.process(deviceSessionCtx.getSessionInfo(), DefaultTransportService.getSessionEventMsg(SessionEvent.OPEN), new TransportServiceCallback() { + transportService.process(deviceSessionCtx.getSessionInfo(), SESSION_EVENT_MSG_OPEN, new TransportServiceCallback() { @Override public void onSuccess(Void msg) { SessionMetaData sessionMetaData = transportService.registerAsyncSession(deviceSessionCtx.getSessionInfo(), MqttTransportHandler.this); diff --git a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionHandler.java b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionHandler.java index 7b45b5f9d9..cb984327da 100644 --- a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionHandler.java +++ b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionHandler.java @@ -44,7 +44,6 @@ import org.thingsboard.server.common.transport.adaptor.JsonConverter; import org.thingsboard.server.common.transport.adaptor.ProtoConverter; import org.thingsboard.server.common.transport.auth.GetOrCreateDeviceFromGatewayResponse; import org.thingsboard.server.common.transport.auth.TransportDeviceInfo; -import org.thingsboard.server.common.transport.service.DefaultTransportService; import org.thingsboard.server.gen.transport.TransportApiProtos; import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.gen.transport.TransportProtos.GetOrCreateDeviceFromGatewayRequestMsg; @@ -68,6 +67,10 @@ import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import static org.springframework.util.ConcurrentReferenceHashMap.ReferenceType; +import static org.thingsboard.server.common.transport.service.DefaultTransportService.SESSION_EVENT_MSG_CLOSED; +import static org.thingsboard.server.common.transport.service.DefaultTransportService.SESSION_EVENT_MSG_OPEN; +import static org.thingsboard.server.common.transport.service.DefaultTransportService.SUBSCRIBE_TO_ATTRIBUTE_UPDATES_ASYNC_MSG; +import static org.thingsboard.server.common.transport.service.DefaultTransportService.SUBSCRIBE_TO_RPC_ASYNC_MSG; /** * Created by ashvayka on 19.01.17. @@ -267,11 +270,9 @@ public class GatewaySessionHandler { transportService.registerAsyncSession(deviceSessionInfo, deviceSessionCtx); transportService.process(TransportProtos.TransportToDeviceActorMsg.newBuilder() .setSessionInfo(deviceSessionInfo) - .setSessionEvent(DefaultTransportService.getSessionEventMsg(TransportProtos.SessionEvent.OPEN)) - .setSubscribeToAttributes(TransportProtos.SubscribeToAttributeUpdatesMsg.newBuilder() - .setSessionType(TransportProtos.SessionType.ASYNC).build()) - .setSubscribeToRPC(TransportProtos.SubscribeToRPCMsg.newBuilder() - .setSessionType(TransportProtos.SessionType.ASYNC).build()) + .setSessionEvent(SESSION_EVENT_MSG_OPEN) + .setSubscribeToAttributes(SUBSCRIBE_TO_ATTRIBUTE_UPDATES_ASYNC_MSG) + .setSubscribeToRPC(SUBSCRIBE_TO_RPC_ASYNC_MSG) .build(), null); } futureToSet.set(devices.get(deviceName)); @@ -720,7 +721,7 @@ public class GatewaySessionHandler { private void deregisterSession(String deviceName, GatewayDeviceSessionCtx deviceSessionCtx) { transportService.deregisterSession(deviceSessionCtx.getSessionInfo()); - transportService.process(deviceSessionCtx.getSessionInfo(), DefaultTransportService.getSessionEventMsg(TransportProtos.SessionEvent.CLOSED), null); + transportService.process(deviceSessionCtx.getSessionInfo(), SESSION_EVENT_MSG_CLOSED, null); log.debug("[{}] Removed device [{}] from the gateway session", sessionId, deviceName); } diff --git a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/DefaultTransportService.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/DefaultTransportService.java index 57d1da63b4..98ffcd721e 100644 --- a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/DefaultTransportService.java +++ b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/DefaultTransportService.java @@ -21,7 +21,6 @@ import com.google.common.util.concurrent.MoreExecutors; import com.google.gson.Gson; import com.google.gson.JsonObject; import com.google.protobuf.ByteString; -import lombok.Getter; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.ApplicationEventPublisher; @@ -124,6 +123,15 @@ import java.util.stream.Collectors; public class DefaultTransportService implements TransportService { public static final String OVERWRITE_ACTIVITY_TIME = "overwriteActivityTime"; + public static final String SESSION_EXPIRED_MESSAGE = "Session has expired due to last activity time!"; + public static final TransportProtos.SessionEventMsg SESSION_EVENT_MSG_OPEN = getSessionEventMsg(TransportProtos.SessionEvent.OPEN); + public static final TransportProtos.SessionEventMsg SESSION_EVENT_MSG_CLOSED = getSessionEventMsg(TransportProtos.SessionEvent.CLOSED); + public static final TransportProtos.SessionCloseNotificationProto SESSION_CLOSE_NOTIFICATION_PROTO = TransportProtos.SessionCloseNotificationProto.newBuilder() + .setMessage(SESSION_EXPIRED_MESSAGE).build(); + public static final TransportProtos.SubscribeToAttributeUpdatesMsg SUBSCRIBE_TO_ATTRIBUTE_UPDATES_ASYNC_MSG = TransportProtos.SubscribeToAttributeUpdatesMsg.newBuilder() + .setSessionType(TransportProtos.SessionType.ASYNC).build(); + public static final TransportProtos.SubscribeToRPCMsg SUBSCRIBE_TO_RPC_ASYNC_MSG = TransportProtos.SubscribeToRPCMsg.newBuilder() + .setSessionType(TransportProtos.SessionType.ASYNC).build(); private final AtomicInteger atomicTs = new AtomicInteger(0); @@ -267,9 +275,7 @@ public class DefaultTransportService implements TransportService { @Override public SessionMetaData registerAsyncSession(TransportProtos.SessionInfoProto sessionInfo, SessionMsgListener listener) { - SessionMetaData newValue = new SessionMetaData(sessionInfo, TransportProtos.SessionType.ASYNC, listener); - SessionMetaData oldValue = sessions.putIfAbsent(toSessionId(sessionInfo), newValue); - return oldValue != null ? oldValue : newValue; + return sessions.computeIfAbsent(toSessionId(sessionInfo), (x) -> new SessionMetaData(sessionInfo, TransportProtos.SessionType.ASYNC, listener)); } @Override @@ -719,12 +725,8 @@ public class DefaultTransportService implements TransportService { } sessions.remove(uuid); sessionsToRemove.add(uuid); - process(sessionInfo, getSessionEventMsg(TransportProtos.SessionEvent.CLOSED), null); - TransportProtos.SessionCloseNotificationProto sessionCloseNotificationProto = TransportProtos.SessionCloseNotificationProto - .newBuilder() - .setMessage("Session has expired due to last activity time!") - .build(); - sessionMD.getListener().onRemoteSessionCloseCommand(uuid, sessionCloseNotificationProto); + process(sessionInfo, SESSION_EVENT_MSG_CLOSED, null); + sessionMD.getListener().onRemoteSessionCloseCommand(uuid, SESSION_CLOSE_NOTIFICATION_PROTO); } } else { if (lastActivityTime > sessionAD.getLastReportedActivityTime()) { @@ -1018,7 +1020,7 @@ public class DefaultTransportService implements TransportService { return new DeviceId(new UUID(sessionInfo.getDeviceIdMSB(), sessionInfo.getDeviceIdLSB())); } - public static TransportProtos.SessionEventMsg getSessionEventMsg(TransportProtos.SessionEvent event) { + private static TransportProtos.SessionEventMsg getSessionEventMsg(TransportProtos.SessionEvent event) { return TransportProtos.SessionEventMsg.newBuilder() .setSessionType(TransportProtos.SessionType.ASYNC) .setEvent(event).build(); From 3c7ec8b7d3de98e20753aa4d31f5e3ba913b3c09 Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Tue, 11 Jan 2022 12:45:34 +0200 Subject: [PATCH 082/798] lwm2m tests refactoring by comments --- .../thingsboard/server/controller/TbTestWebSocketClient.java | 2 +- .../server/transport/lwm2m/AbstractLwM2MIntegrationTest.java | 1 - .../server/transport/lwm2m/ota/sql/OtaLwM2MIntegrationTest.java | 2 -- 3 files changed, 1 insertion(+), 4 deletions(-) diff --git a/application/src/test/java/org/thingsboard/server/controller/TbTestWebSocketClient.java b/application/src/test/java/org/thingsboard/server/controller/TbTestWebSocketClient.java index 2bb68737ac..ff6b004405 100644 --- a/application/src/test/java/org/thingsboard/server/controller/TbTestWebSocketClient.java +++ b/application/src/test/java/org/thingsboard/server/controller/TbTestWebSocketClient.java @@ -74,7 +74,7 @@ public class TbTestWebSocketClient extends WebSocketClient { } public String waitForUpdate() { - return waitForUpdate(TimeUnit.SECONDS.toMillis(8)); + return waitForUpdate(TimeUnit.SECONDS.toMillis(3)); } public String waitForUpdate(long ms) { diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java index 09495402f3..4fc9108862 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java @@ -16,7 +16,6 @@ package org.thingsboard.server.transport.lwm2m; import com.fasterxml.jackson.core.type.TypeReference; -import lombok.extern.slf4j.Slf4j; import org.apache.commons.io.IOUtils; import org.eclipse.californium.core.network.config.NetworkConfig; import org.eclipse.leshan.client.object.Security; diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/ota/sql/OtaLwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/ota/sql/OtaLwM2MIntegrationTest.java index e3c538c928..3b152ec728 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/ota/sql/OtaLwM2MIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/ota/sql/OtaLwM2MIntegrationTest.java @@ -200,8 +200,6 @@ public class OtaLwM2MIntegrationTest extends AbstractOtaLwM2MIntegrationTest { * */ @Test public void testSoftwareUpdateByObject9() throws Exception { -// String endpoint = "Ota9_Device"; -// setEndpoint(endpoint); createDeviceProfile(OTA_TRANSPORT_CONFIGURATION); NoSecClientCredential credentials = createNoSecClientCredentials(this.CLIENT_ENDPOINT_OTA9); final Device device = createDevice(credentials); From 217a84f1f2ea3bbed90ba0e2d624ed4e4cd94665 Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Tue, 11 Jan 2022 12:56:02 +0200 Subject: [PATCH 083/798] request added to the log on "Pending request map is full" in DefaultTbQueueRequestTemplate.java --- .../server/queue/common/DefaultTbQueueRequestTemplate.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/common/DefaultTbQueueRequestTemplate.java b/common/queue/src/main/java/org/thingsboard/server/queue/common/DefaultTbQueueRequestTemplate.java index 7463c11df5..58890fb2a3 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/common/DefaultTbQueueRequestTemplate.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/common/DefaultTbQueueRequestTemplate.java @@ -210,7 +210,7 @@ public class DefaultTbQueueRequestTemplate send(Request request, long requestTimeoutNs) { if (pendingRequests.mappingCount() >= maxPendingRequests) { - log.warn("Pending request map is full [{}]! Consider to increase maxPendingRequests or increase processing performance", maxPendingRequests); + log.warn("Pending request map is full [{}]! Consider to increase maxPendingRequests or increase processing performance. Request is {}", maxPendingRequests, request); return Futures.immediateFailedFuture(new RuntimeException("Pending request map is full!")); } UUID requestId = UUID.randomUUID(); From 6479160932d8a6f78e9e556a5349ef1c8946e8e9 Mon Sep 17 00:00:00 2001 From: Nikitozin Date: Tue, 11 Jan 2022 13:44:47 +0200 Subject: [PATCH 084/798] [WIP][3.3.3] Refactor variables. --- .../action/TbSaveToCustomCassandraTableNode.java | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbSaveToCustomCassandraTableNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbSaveToCustomCassandraTableNode.java index bd3371edae..66291f5245 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbSaveToCustomCassandraTableNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbSaveToCustomCassandraTableNode.java @@ -179,18 +179,19 @@ public class TbSaveToCustomCassandraTableNode implements TbNode { if (key.equals(ENTITY_ID)) { stmtBuilder.setUuid(i.get(), msg.getOriginator().getId()); } else if (dataAsObject.has(key)) { - if (dataAsObject.get(key).isJsonPrimitive()) { - JsonPrimitive primitive = dataAsObject.get(key).getAsJsonPrimitive(); + JsonElement dataKeyElement = dataAsObject.get(key); + if (dataKeyElement.isJsonPrimitive()) { + JsonPrimitive primitive = dataKeyElement.getAsJsonPrimitive(); if (primitive.isNumber()) { - if (dataAsObject.get(key).getAsString().contains(".")) { - stmtBuilder.setDouble(i.get(), dataAsObject.get(key).getAsDouble()); + if (primitive.getAsString().contains(".")) { + stmtBuilder.setDouble(i.get(), primitive.getAsDouble()); } else { - stmtBuilder.setLong(i.get(), dataAsObject.get(key).getAsLong()); + stmtBuilder.setLong(i.get(), primitive.getAsLong()); } } else if (primitive.isBoolean()) { - stmtBuilder.setBoolean(i.get(), dataAsObject.get(key).getAsBoolean()); + stmtBuilder.setBoolean(i.get(), primitive.getAsBoolean()); } else if (primitive.isString()) { - stmtBuilder.setString(i.get(), dataAsObject.get(key).getAsString()); + stmtBuilder.setString(i.get(), primitive.getAsString()); } else { stmtBuilder.setToNull(i.get()); } From 88a582fd3a405ce94d1d8b4c09611cbe17a6903f Mon Sep 17 00:00:00 2001 From: Kalutka Zhenya Date: Tue, 11 Jan 2022 14:38:07 +0200 Subject: [PATCH 085/798] Updated dynamic value logic --- .../home/components/home-components.module.ts | 4 +- .../alarm/alarm-dynamic-value.component.html | 37 ++++++++++ .../alarm/alarm-dynamic-value.component.ts | 68 +++++++++++++++++++ .../alarm/alarm-schedule.component.html | 41 +---------- .../profile/alarm/alarm-schedule.component.ts | 27 ++------ 5 files changed, 115 insertions(+), 62 deletions(-) create mode 100644 ui-ngx/src/app/modules/home/components/profile/alarm/alarm-dynamic-value.component.html create mode 100644 ui-ngx/src/app/modules/home/components/profile/alarm/alarm-dynamic-value.component.ts diff --git a/ui-ngx/src/app/modules/home/components/home-components.module.ts b/ui-ngx/src/app/modules/home/components/home-components.module.ts index 73361eddce..6614edebe8 100644 --- a/ui-ngx/src/app/modules/home/components/home-components.module.ts +++ b/ui-ngx/src/app/modules/home/components/home-components.module.ts @@ -147,6 +147,7 @@ import { HOME_COMPONENTS_MODULE_TOKEN } from '@home/components/tokens'; import { DashboardStateComponent } from '@home/components/dashboard-page/dashboard-state.component'; +import { AlarmDynamicValue } from '@home/components/profile/alarm/alarm-dynamic-value.component'; @NgModule({ declarations: @@ -245,6 +246,7 @@ import { DashboardStateComponent } from '@home/components/dashboard-page/dashboa AlarmScheduleInfoComponent, DeviceProfileProvisionConfigurationComponent, AlarmScheduleComponent, + AlarmDynamicValue, AlarmDurationPredicateValueComponent, DeviceWizardDialogComponent, AlarmScheduleDialogComponent, @@ -356,11 +358,11 @@ import { DashboardStateComponent } from '@home/components/dashboard-page/dashboa DeviceWizardDialogComponent, AlarmScheduleInfoComponent, AlarmScheduleComponent, + AlarmDynamicValue, AlarmScheduleDialogComponent, AlarmDurationPredicateValueComponent, EditAlarmDetailsDialogComponent, DeviceProfileProvisionConfigurationComponent, - AlarmScheduleComponent, SmsProviderConfigurationComponent, AwsSnsProviderConfigurationComponent, TwilioSmsProviderConfigurationComponent, diff --git a/ui-ngx/src/app/modules/home/components/profile/alarm/alarm-dynamic-value.component.html b/ui-ngx/src/app/modules/home/components/profile/alarm/alarm-dynamic-value.component.html new file mode 100644 index 0000000000..6b2852cd68 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/profile/alarm/alarm-dynamic-value.component.html @@ -0,0 +1,37 @@ + + +
+ +
+ {{'filter.dynamic-value' | translate}} +
+
+ +
+
+ +
+
+
+ + + + + {{'filter.no-dynamic-value' | translate}} + + + {{dynamicValueSourceTypeTranslations.get(sourceType) | translate}} + + + +
+
+ + + + +
+
+
+
+
diff --git a/ui-ngx/src/app/modules/home/components/profile/alarm/alarm-dynamic-value.component.ts b/ui-ngx/src/app/modules/home/components/profile/alarm/alarm-dynamic-value.component.ts new file mode 100644 index 0000000000..cb4efc1480 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/profile/alarm/alarm-dynamic-value.component.ts @@ -0,0 +1,68 @@ +import { Component, forwardRef, OnInit } from '@angular/core'; +import { + ControlValueAccessor, + FormBuilder, + FormGroup, + NG_VALUE_ACCESSOR, +} from '@angular/forms'; +import { + DynamicValueSourceType, + dynamicValueSourceTypeTranslationMap, + getDynamicSourcesForAllowUser +} from '@shared/models/query/query.models'; + +@Component({ + selector: 'tb-alarm-dynamic-value', + templateUrl: './alarm-dynamic-value.component.html', + providers: [{ + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => AlarmDynamicValue), + multi: true + }] +}) + +export class AlarmDynamicValue implements ControlValueAccessor, OnInit{ + public dynamicValue: FormGroup; + public dynamicValueSourceTypes: DynamicValueSourceType[] = getDynamicSourcesForAllowUser(false); + public dynamicValueSourceTypeTranslations = dynamicValueSourceTypeTranslationMap; + private propagateChange = (v: any) => { }; + + constructor(private fb: FormBuilder) { + } + + ngOnInit(): void { + this.dynamicValue = this.fb.group({ + sourceType: [null], + sourceAttribute: [null] + }) + + this.dynamicValue.get('sourceType').valueChanges.subscribe( + (sourceType) => { + if (!sourceType) { + this.dynamicValue.get('sourceAttribute').patchValue(null, {emitEvent: false}); + } + } + ); + + this.dynamicValue.valueChanges.subscribe(() => { + this.updateModel(); + }) + } + + registerOnChange(fn: any): void { + this.propagateChange = fn; + } + + registerOnTouched(fn: any): void { + } + + writeValue(dynamicValue: {sourceType: string, sourceAttribute: string}): void { + if(dynamicValue) { + this.dynamicValue.patchValue(dynamicValue, {emitEvent: false}); + } + } + + private updateModel() { + this.propagateChange(this.dynamicValue.value); + } +} diff --git a/ui-ngx/src/app/modules/home/components/profile/alarm/alarm-schedule.component.html b/ui-ngx/src/app/modules/home/components/profile/alarm/alarm-schedule.component.html index 5d63b48f15..67a3ac69c0 100644 --- a/ui-ngx/src/app/modules/home/components/profile/alarm/alarm-schedule.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/alarm/alarm-schedule.component.html @@ -34,6 +34,7 @@ formControlName="timezone">
+
device-profile.schedule-days
@@ -73,44 +74,8 @@
- - -
- -
- {{'filter.dynamic-value' | translate}} -
-
- -
-
- -
-
-
- - - - - {{'filter.no-dynamic-value' | translate}} - - - {{dynamicValueSourceTypeTranslations.get(sourceType) | translate}} - - - -
-
- - - - -
-
-
-
-
-
device-profile.schedule-days
+ +
device-profile.schedule-days
diff --git a/ui-ngx/src/app/modules/home/components/profile/alarm/alarm-schedule.component.ts b/ui-ngx/src/app/modules/home/components/profile/alarm/alarm-schedule.component.ts index 6fd679f3f7..2deef61a6d 100644 --- a/ui-ngx/src/app/modules/home/components/profile/alarm/alarm-schedule.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/alarm/alarm-schedule.component.ts @@ -40,12 +40,6 @@ import { import { isDefined, isDefinedAndNotNull } from '@core/utils'; import { MatCheckboxChange } from '@angular/material/checkbox'; import { getDefaultTimezone } from '@shared/models/time/time.models'; -import { - DynamicValueSourceType, - dynamicValueSourceTypeTranslationMap, - getDynamicSourcesForAllowUser -} from '@shared/models/query/query.models'; -import { emit } from 'cluster'; @Component({ selector: 'tb-alarm-schedule', @@ -70,8 +64,6 @@ export class AlarmScheduleComponent implements ControlValueAccessor, Validator, alarmScheduleTypes = Object.keys(AlarmScheduleType); alarmScheduleType = AlarmScheduleType; alarmScheduleTypeTranslate = AlarmScheduleTypeTranslationMap; - dynamicValueSourceTypes: DynamicValueSourceType[] = getDynamicSourcesForAllowUser(false); - dynamicValueSourceTypeTranslations = dynamicValueSourceTypeTranslationMap; dayOfWeekTranslationsArray = dayOfWeekTranslations; allDays = Array(7).fill(0).map((x, i) => i); @@ -99,18 +91,9 @@ export class AlarmScheduleComponent implements ControlValueAccessor, Validator, startsOn: [0, Validators.required], endsOn: [0, Validators.required], items: this.fb.array(Array.from({length: 7}, (value, i) => this.defaultItemsScheduler(i)), this.validateItems), - dynamicValue: this.fb.group({ - sourceType: [null], - sourceAttribute: [null] - }) + dynamicValue: [null] }); - this.alarmScheduleForm.get('dynamicValue.sourceType').valueChanges.subscribe((sourceType) => { - if (!sourceType) { - this.alarmScheduleForm.get('dynamicValue.sourceAttribute').patchValue('', {emitEvent:false}); - } - }) - this.alarmScheduleForm.get('type').valueChanges.subscribe((type) => { const defaultTimezone = getDefaultTimezone(); this.alarmScheduleForm.reset({type, items: this.defaultItems, timezone: defaultTimezone}, {emitEvent: false}); @@ -176,7 +159,8 @@ export class AlarmScheduleComponent implements ControlValueAccessor, Validator, timezone: this.modelValue.timezone, daysOfWeek, startsOn: utcTimestampToTimeOfDay(this.modelValue.startsOn), - endsOn: utcTimestampToTimeOfDay(this.modelValue.endsOn) + endsOn: utcTimestampToTimeOfDay(this.modelValue.endsOn), + dynamicValue: this.modelValue.dynamicValue }, {emitEvent: false}); break; case AlarmScheduleType.CUSTOM: @@ -196,10 +180,7 @@ export class AlarmScheduleComponent implements ControlValueAccessor, Validator, type: this.modelValue.type, timezone: this.modelValue.timezone, items: alarmDays, - dynamicValue: { - sourceAttribute: this.modelValue.dynamicValue.sourceAttribute, - sourceType: this.modelValue.dynamicValue.sourceType - } + dynamicValue: this.modelValue.dynamicValue }, {emitEvent: false}); } break; From 667045384649ac02f86e73418cf3daf07259c323 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Tue, 11 Jan 2022 14:59:49 +0200 Subject: [PATCH 086/798] UI: Added persistent page link for entities pages --- .../components/alarm/alarm-table-config.ts | 1 + .../audit-log/audit-log-table-config.ts | 4 +- .../audit-log/audit-log-table.component.ts | 8 +- .../entity/entities-table.component.ts | 96 ++++++++++++++----- .../components/event/event-table-config.ts | 1 + .../entity/entities-table-config.models.ts | 1 + .../entity/entity-table-component.models.ts | 1 + .../audit-log/audit-log-routing.module.ts | 3 +- 8 files changed, 88 insertions(+), 27 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/alarm/alarm-table-config.ts b/ui-ngx/src/app/modules/home/components/alarm/alarm-table-config.ts index 2bff69a53e..6c4e0a22e0 100644 --- a/ui-ngx/src/app/modules/home/components/alarm/alarm-table-config.ts +++ b/ui-ngx/src/app/modules/home/components/alarm/alarm-table-config.ts @@ -60,6 +60,7 @@ export class AlarmTableConfig extends EntityTableConfig this.loadDataOnInit = false; this.tableTitle = ''; this.useTimePageLink = true; + this.persistentPageLinkMode = false; this.defaultTimewindowInterval = historyInterval(DAY * 30); this.detailsPanelEnabled = false; this.selectionEnabled = false; diff --git a/ui-ngx/src/app/modules/home/components/audit-log/audit-log-table-config.ts b/ui-ngx/src/app/modules/home/components/audit-log/audit-log-table-config.ts index f5c928f275..a61d0c54ab 100644 --- a/ui-ngx/src/app/modules/home/components/audit-log/audit-log-table-config.ts +++ b/ui-ngx/src/app/modules/home/components/audit-log/audit-log-table-config.ts @@ -52,11 +52,13 @@ export class AuditLogTableConfig extends EntityTableConfig) { + private store: Store, + private route: ActivatedRoute) { } ngOnInit() { @@ -117,6 +119,7 @@ export class AuditLogTableComponent implements OnInit { } updateOnInit = true; } + const persistentPageLinkMode = !!this.route.snapshot.data.isPage; this.auditLogTableConfig = new AuditLogTableConfig( this.auditLogService, this.translate, @@ -126,7 +129,8 @@ export class AuditLogTableComponent implements OnInit { this.entityIdValue, this.userIdValue, this.customerIdValue, - updateOnInit + updateOnInit, + persistentPageLinkMode ); } diff --git a/ui-ngx/src/app/modules/home/components/entity/entities-table.component.ts b/ui-ngx/src/app/modules/home/components/entity/entities-table.component.ts index 33f1b2afe4..8fd2f8a976 100644 --- a/ui-ngx/src/app/modules/home/components/entity/entities-table.component.ts +++ b/ui-ngx/src/app/modules/home/components/entity/entities-table.component.ts @@ -41,9 +41,10 @@ import { Direction, SortOrder } from '@shared/models/page/sort-order'; import { forkJoin, fromEvent, merge, Observable, of, Subscription } from 'rxjs'; import { TranslateService } from '@ngx-translate/core'; import { BaseData, HasId } from '@shared/models/base-data'; -import { ActivatedRoute } from '@angular/router'; +import { ActivatedRoute, QueryParamsHandling, Router } from '@angular/router'; import { - CellActionDescriptor, CellActionDescriptorType, + CellActionDescriptor, + CellActionDescriptorType, EntityActionTableColumn, EntityColumn, EntityTableColumn, @@ -55,14 +56,10 @@ import { EntityTypeTranslation } from '@shared/models/entity-type.models'; import { DialogService } from '@core/services/dialog.service'; import { AddEntityDialogComponent } from './add-entity-dialog.component'; import { AddEntityDialogData, EntityAction } from '@home/models/entity/entity-component.models'; -import { - calculateIntervalStartEndTime, - HistoryWindowType, - Timewindow -} from '@shared/models/time/time.models'; +import { calculateIntervalStartEndTime, HistoryWindowType, Timewindow } from '@shared/models/time/time.models'; import { DomSanitizer, SafeHtml } from '@angular/platform-browser'; import { TbAnchorComponent } from '@shared/components/tb-anchor.component'; -import { isDefined, isUndefined } from '@core/utils'; +import { isDefined, isEmptyStr, isUndefined } from '@core/utils'; import { HasUUID } from '@shared/models/id/has-uuid'; import { IEntitiesTableComponent } from '@home/models/entity/entity-table-component.models'; @@ -101,6 +98,7 @@ export class EntitiesTableComponent extends PageComponent implements IEntitiesTa displayPagination = true; pageSizeOptions; pageLink: PageLink; + persistentPageLinkMode = true; textSearchMode = false; timewindow: Timewindow; dataSource: EntitiesDataSource>; @@ -117,7 +115,6 @@ export class EntitiesTableComponent extends PageComponent implements IEntitiesTa @ViewChild(MatPaginator) paginator: MatPaginator; @ViewChild(MatSort) sort: MatSort; - private sortSubscription: Subscription; private updateDataSubscription: Subscription; private viewInited = false; @@ -128,6 +125,7 @@ export class EntitiesTableComponent extends PageComponent implements IEntitiesTa private dialogService: DialogService, private domSanitizer: DomSanitizer, private cd: ChangeDetectorRef, + private router: Router, private componentFactoryResolver: ComponentFactoryResolver) { super(store); } @@ -196,16 +194,19 @@ export class EntitiesTableComponent extends PageComponent implements IEntitiesTa this.columnsUpdated(); + const routerQueryParams = this.route.snapshot.queryParams; + let sortOrder: SortOrder = null; - if (this.entitiesTableConfig.defaultSortOrder) { + if (this.entitiesTableConfig.defaultSortOrder || routerQueryParams.hasOwnProperty('direction') || routerQueryParams.hasOwnProperty('property')) { sortOrder = { - property: this.entitiesTableConfig.defaultSortOrder.property, - direction: this.entitiesTableConfig.defaultSortOrder.direction + property: routerQueryParams?.property || this.entitiesTableConfig.defaultSortOrder.property, + direction: routerQueryParams?.direction || this.entitiesTableConfig.defaultSortOrder.direction }; } this.displayPagination = this.entitiesTableConfig.displayPagination; this.defaultPageSize = this.entitiesTableConfig.defaultPageSize; + this.persistentPageLinkMode = this.entitiesTableConfig.persistentPageLinkMode; this.pageSizeOptions = [this.defaultPageSize, this.defaultPageSize * 2, this.defaultPageSize * 3]; if (this.entitiesTableConfig.useTimePageLink) { @@ -217,6 +218,16 @@ export class EntitiesTableComponent extends PageComponent implements IEntitiesTa this.pageLink = new PageLink(10, 0, null, sortOrder); } this.pageLink.pageSize = this.displayPagination ? this.defaultPageSize : MAX_SAFE_PAGE_SIZE; + if (routerQueryParams.hasOwnProperty('page')) { + this.pageLink.page = routerQueryParams.page; + } + if (routerQueryParams.hasOwnProperty('pageSize')) { + this.pageLink.pageSize = routerQueryParams.pageSize; + } + if (routerQueryParams.hasOwnProperty('textSearch') && !isEmptyStr(routerQueryParams.textSearch)) { + this.textSearchMode = true; + this.pageLink.textSearch = decodeURI(routerQueryParams.textSearch); + } this.dataSource = this.entitiesTableConfig.dataSource(this.dataLoaded.bind(this)); if (this.entitiesTableConfig.onLoadAction) { this.entitiesTableConfig.onLoadAction(this.route); @@ -238,9 +249,14 @@ export class EntitiesTableComponent extends PageComponent implements IEntitiesTa debounceTime(150), distinctUntilChanged(), tap(() => { + const queryParams: any = { + textSearch: encodeURI(this.pageLink.textSearch) || null + }; if (this.displayPagination) { this.paginator.pageIndex = 0; + queryParams.page = null; } + this.updatedRouterQueryParams(queryParams); this.updateData(); }) ) @@ -251,23 +267,42 @@ export class EntitiesTableComponent extends PageComponent implements IEntitiesTa } private updatePaginationSubscriptions() { - if (this.sortSubscription) { - this.sortSubscription.unsubscribe(); - this.sortSubscription = null; - } if (this.updateDataSubscription) { this.updateDataSubscription.unsubscribe(); this.updateDataSubscription = null; } + let paginatorSubscription$: Observable; + const sortSubscription$: Observable = this.sort.sortChange.asObservable().pipe( + map((data) => { + const direction = data.direction.toUpperCase(); + const queryParams: any = { + direction: this.entitiesTableConfig?.defaultSortOrder?.direction === direction ? null : direction, + property: this.entitiesTableConfig?.defaultSortOrder?.property === data.active ? null : data.active + }; + if (this.displayPagination) { + queryParams.page = null; + this.paginator.pageIndex = 0; + } + return queryParams; + }) + ); if (this.displayPagination) { - this.sortSubscription = this.sort.sortChange.subscribe(() => this.paginator.pageIndex = 0); + paginatorSubscription$ = this.paginator.page.asObservable().pipe( + map((data) => { + return { + page: data.pageIndex === 0 ? null : data.pageIndex, + pageSize: data.pageSize === this.defaultPageSize ? null : data.pageSize + }; + }) + ); } - this.updateDataSubscription = ((this.displayPagination ? merge(this.sort.sortChange, this.paginator.page) - : this.sort.sortChange) as Observable) - .pipe( - tap(() => this.updateData()) - ) - .subscribe(); + this.updateDataSubscription = ((this.displayPagination ? merge(sortSubscription$, paginatorSubscription$) + : sortSubscription$) as Observable).pipe( + tap((queryParams) => { + this.updatedRouterQueryParams(queryParams); + this.updateData(); + }) + ).subscribe(); } addEnabled() { @@ -451,9 +486,14 @@ export class EntitiesTableComponent extends PageComponent implements IEntitiesTa exitFilterMode() { this.textSearchMode = false; this.pageLink.textSearch = null; + const queryParams: any = { + textSearch: null + }; if (this.displayPagination) { this.paginator.pageIndex = 0; + queryParams.page = null; } + this.updatedRouterQueryParams(queryParams); this.updateData(); } @@ -468,6 +508,7 @@ export class EntitiesTableComponent extends PageComponent implements IEntitiesTa const sortable = this.sort.sortables.get(this.entitiesTableConfig.defaultSortOrder.property); this.sort.active = sortable.id; this.sort.direction = this.entitiesTableConfig.defaultSortOrder.direction === Direction.ASC ? 'asc' : 'desc'; + this.updatedRouterQueryParams({}, 'preserve'); if (update) { this.updateData(); } @@ -587,4 +628,13 @@ export class EntitiesTableComponent extends PageComponent implements IEntitiesTa return entity.id.id; } + protected updatedRouterQueryParams(queryParams: object, queryParamsHandling: QueryParamsHandling = 'merge') { + if (this.persistentPageLinkMode) { + this.router.navigate([], { + relativeTo: this.route, + queryParams, + queryParamsHandling + }); + } + } } diff --git a/ui-ngx/src/app/modules/home/components/event/event-table-config.ts b/ui-ngx/src/app/modules/home/components/event/event-table-config.ts index d407cf262b..d6eedb7ff6 100644 --- a/ui-ngx/src/app/modules/home/components/event/event-table-config.ts +++ b/ui-ngx/src/app/modules/home/components/event/event-table-config.ts @@ -92,6 +92,7 @@ export class EventTableConfig extends EntityTableConfig { this.searchEnabled = false; this.addEnabled = false; this.entitiesDeleteEnabled = false; + this.persistentPageLinkMode = false; this.headerComponent = EventTableHeaderComponent; diff --git a/ui-ngx/src/app/modules/home/models/entity/entities-table-config.models.ts b/ui-ngx/src/app/modules/home/models/entity/entities-table-config.models.ts index 93e68e40b7..d6ddf40735 100644 --- a/ui-ngx/src/app/modules/home/models/entity/entities-table-config.models.ts +++ b/ui-ngx/src/app/modules/home/models/entity/entities-table-config.models.ts @@ -160,6 +160,7 @@ export class EntityTableConfig, P extends PageLink = P addDialogStyle = {}; defaultSortOrder: SortOrder = {property: 'createdTime', direction: Direction.DESC}; displayPagination = true; + persistentPageLinkMode = true; defaultPageSize = 10; columns: Array> = []; cellActionDescriptors: Array> = []; diff --git a/ui-ngx/src/app/modules/home/models/entity/entity-table-component.models.ts b/ui-ngx/src/app/modules/home/models/entity/entity-table-component.models.ts index 20b1d15b32..4cf41f0fed 100644 --- a/ui-ngx/src/app/modules/home/models/entity/entity-table-component.models.ts +++ b/ui-ngx/src/app/modules/home/models/entity/entity-table-component.models.ts @@ -50,6 +50,7 @@ export interface IEntitiesTableComponent { displayPagination: boolean; pageSizeOptions: number[]; pageLink: PageLink; + persistentPageLinkMode: boolean; textSearchMode: boolean; timewindow: Timewindow; dataSource: EntitiesDataSource>; diff --git a/ui-ngx/src/app/modules/home/pages/audit-log/audit-log-routing.module.ts b/ui-ngx/src/app/modules/home/pages/audit-log/audit-log-routing.module.ts index 89d304e8f1..b471f3b540 100644 --- a/ui-ngx/src/app/modules/home/pages/audit-log/audit-log-routing.module.ts +++ b/ui-ngx/src/app/modules/home/pages/audit-log/audit-log-routing.module.ts @@ -29,7 +29,8 @@ const routes: Routes = [ breadcrumb: { label: 'audit-log.audit-logs', icon: 'track_changes' - } + }, + isPage: true } } ]; From 877257202e9e96925d684e8d448259d0ceada56b Mon Sep 17 00:00:00 2001 From: desoliture Date: Tue, 11 Jan 2022 15:38:20 +0200 Subject: [PATCH 087/798] fix incorrect work if endsOn equals zero when choosing schedule to be all day active (from 00:00 to 00:00) startsOn and endsOn equals zero, in this case make endsOn equals 24hours in millis --- .../thingsboard/rule/engine/profile/AlarmRuleState.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/AlarmRuleState.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/AlarmRuleState.java index abcc414206..f997efdeb3 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/AlarmRuleState.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/AlarmRuleState.java @@ -182,7 +182,12 @@ class AlarmRuleState { for (CustomTimeScheduleItem item : schedule.getItems()) { if (item.getDayOfWeek() == dayOfWeek) { if (item.isEnabled()) { - return isActive(eventTs, zoneId, zdt, item.getStartsOn(), item.getEndsOn()); + long endsOn = item.getEndsOn(); + if (endsOn == 0) { + // 24 hours in milliseconds + endsOn = 86400000; + } + return isActive(eventTs, zoneId, zdt, item.getStartsOn(), endsOn); } else { return false; } From 7328012cb491b16eebb89f1c6c0380861bf94c7f Mon Sep 17 00:00:00 2001 From: Kalutka Zhenya Date: Tue, 11 Jan 2022 16:06:08 +0200 Subject: [PATCH 088/798] Added license headers --- .../alarm/alarm-dynamic-value.component.html | 17 +++++++++++++++++ .../alarm/alarm-dynamic-value.component.ts | 16 ++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/ui-ngx/src/app/modules/home/components/profile/alarm/alarm-dynamic-value.component.html b/ui-ngx/src/app/modules/home/components/profile/alarm/alarm-dynamic-value.component.html index 6b2852cd68..50b2dee7a7 100644 --- a/ui-ngx/src/app/modules/home/components/profile/alarm/alarm-dynamic-value.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/alarm/alarm-dynamic-value.component.html @@ -1,3 +1,20 @@ +
diff --git a/ui-ngx/src/app/modules/home/components/profile/alarm/alarm-dynamic-value.component.ts b/ui-ngx/src/app/modules/home/components/profile/alarm/alarm-dynamic-value.component.ts index cb4efc1480..ad4b8cc02c 100644 --- a/ui-ngx/src/app/modules/home/components/profile/alarm/alarm-dynamic-value.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/alarm/alarm-dynamic-value.component.ts @@ -1,3 +1,19 @@ +/// +/// Copyright © 2016-2021 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + import { Component, forwardRef, OnInit } from '@angular/core'; import { ControlValueAccessor, From 9de9e6147fa5ef6db5d644b3cffd36bd6422a718 Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Tue, 11 Jan 2022 17:46:06 +0200 Subject: [PATCH 089/798] lwm2m fix bug update profile if security mode == null --- .../3.3.2/schema_update_lwm2m_bootstrap.sql | 51 ++++++++++++------- .../install/SqlDatabaseUpgradeService.java | 2 + 2 files changed, 34 insertions(+), 19 deletions(-) diff --git a/application/src/main/data/upgrade/3.3.2/schema_update_lwm2m_bootstrap.sql b/application/src/main/data/upgrade/3.3.2/schema_update_lwm2m_bootstrap.sql index a182bc8117..8daa1b1e1e 100644 --- a/application/src/main/data/upgrade/3.3.2/schema_update_lwm2m_bootstrap.sql +++ b/application/src/main/data/upgrade/3.3.2/schema_update_lwm2m_bootstrap.sql @@ -14,6 +14,7 @@ -- limitations under the License. -- + CREATE OR REPLACE PROCEDURE update_profile_bootstrap() LANGUAGE plpgsql AS $$ @@ -25,9 +26,11 @@ BEGIN profile_data, '{transportConfiguration}', get_bootstrap( - profile_data::jsonb #> '{transportConfiguration}', - subquery.publickey_bs, - subquery.publickey_lw), + profile_data::jsonb #> '{transportConfiguration}', + subquery.publickey_bs, + subquery.publickey_lw, + profile_data::json #>> '{transportConfiguration, bootstrap, bootstrapServer, securityMode}', + profile_data::json #>> '{transportConfiguration, bootstrap, lwm2mServer, securityMode}'), true) FROM ( SELECT id, @@ -48,7 +51,8 @@ END; $$; CREATE OR REPLACE FUNCTION get_bootstrap(transport_configuration_in jsonb, publickey_bs text, - publickey_lw text) RETURNS jsonb AS + publickey_lw text, security_mode_bs text, + security_mode_lw text) RETURNS jsonb AS $$ DECLARE @@ -56,10 +60,19 @@ DECLARE bootstrap_in jsonb; BEGIN + + IF security_mode_lw IS NULL THEN + security_mode_lw := 'NO_SEC'; + END IF; + + IF security_mode_bs IS NULL THEN + security_mode_bs := 'NO_SEC'; + END IF; + bootstrap_in := transport_configuration_in::jsonb #> '{bootstrap}'; bootstrap_new := json_build_array( json_build_object('shortServerId', bootstrap_in::json #> '{bootstrapServer}' -> 'serverId', - 'securityMode', bootstrap_in::json #> '{bootstrapServer}' ->> 'securityMode', + 'securityMode', security_mode_bs, 'binding', bootstrap_in::json #> '{servers}' ->> 'binding', 'lifetime', bootstrap_in::json #> '{servers}' -> 'lifetime', 'notifIfDisabled', bootstrap_in::json #> '{servers}' -> 'notifIfDisabled', @@ -73,7 +86,7 @@ BEGIN bootstrap_in::json #> '{bootstrapServer}' -> 'bootstrapServerAccountTimeout' ), json_build_object('shortServerId', bootstrap_in::json #> '{lwm2mServer}' -> 'serverId', - 'securityMode', bootstrap_in::json #> '{lwm2mServer}' ->> 'securityMode', + 'securityMode', security_mode_lw, 'binding', bootstrap_in::json #> '{servers}' ->> 'binding', 'lifetime', bootstrap_in::json #> '{servers}' -> 'lifetime', 'notifIfDisabled', bootstrap_in::json #> '{servers}' -> 'notifIfDisabled', @@ -93,7 +106,7 @@ BEGIN bootstrap_new, true) || '{"bootstrapServerUpdateEnable": true}'; -END ; +END; $$ LANGUAGE plpgsql; CREATE OR REPLACE PROCEDURE update_device_credentials_to_base64_and_bootstrap() @@ -102,9 +115,9 @@ $$ BEGIN -UPDATE device_credentials -SET credentials_value = get_device_and_bootstrap(credentials_value::text) -WHERE credentials_type = 'LWM2M_CREDENTIALS'; + UPDATE device_credentials + SET credentials_value = get_device_and_bootstrap(credentials_value::text) + WHERE credentials_type = 'LWM2M_CREDENTIALS'; END; $$; @@ -112,7 +125,7 @@ CREATE OR REPLACE FUNCTION get_device_and_bootstrap(IN credentials_value text, O LANGUAGE plpgsql AS $$ DECLARE -client_secret_key text; + client_secret_key text; client_public_key_or_id text; client_key_value_object jsonb; client_bootstrap_server_value_object jsonb; @@ -130,7 +143,7 @@ BEGIN 'key', client_public_key_or_id); credentials_value_new := credentials_value_new::jsonb || json_build_object('client', client_key_value_object)::jsonb; -END IF; + END IF; IF credentials_value::jsonb #> '{client}' ->> 'securityConfigClientMode' = 'X509' AND NULLIF((credentials_value::jsonb #> '{client}' ->> 'cert' ~ '^[0-9a-fA-F]+$')::text, 'false') = 'true' THEN client_public_key_or_id := @@ -141,8 +154,8 @@ END IF; 'cert', client_public_key_or_id); credentials_value_new := credentials_value_new::jsonb || json_build_object('client', client_key_value_object)::jsonb; -END IF; - + END IF; + IF credentials_value::jsonb #> '{bootstrap,lwm2mServer}' ->> 'securityMode' = 'RPK' OR credentials_value::jsonb #> '{bootstrap,lwm2mServer}' ->> 'securityMode' = 'X509' THEN IF NULLIF((credentials_value::jsonb #> '{bootstrap,lwm2mServer}' ->> 'clientSecretKey' ~ '^[0-9a-fA-F]+$')::text, @@ -165,9 +178,9 @@ END IF; client_bootstrap_object := credentials_value_new::jsonb #> '{bootstrap}' || client_bootstrap_server_object::jsonb; credentials_value_new := jsonb_set(credentials_value_new::jsonb, '{bootstrap}', client_bootstrap_object::jsonb, false)::jsonb; -END IF; -END IF; - + END IF; + END IF; + IF credentials_value::jsonb #> '{bootstrap,bootstrapServer}' ->> 'securityMode' = 'RPK' OR credentials_value::jsonb #> '{bootstrap,bootstrapServer}' ->> 'securityMode' = 'X509' THEN IF NULLIF( @@ -193,8 +206,8 @@ END IF; client_bootstrap_object := credentials_value_new::jsonb #> '{bootstrap}' || client_bootstrap_server_object::jsonb; credentials_value_new := jsonb_set(credentials_value_new::jsonb, '{bootstrap}', client_bootstrap_object::jsonb, false)::jsonb; -END IF; -END IF; + END IF; + END IF; END; $$; \ No newline at end of file diff --git a/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java b/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java index eef29026ba..c98e46d86e 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java +++ b/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java @@ -476,6 +476,8 @@ public class SqlDatabaseUpgradeService implements DatabaseEntitiesUpgradeService schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "3.3.2", SCHEMA_UPDATE_SQL); loadSql(schemaUpdateFile, conn); log.info("Updating server`s public key from HexDec to Base64 in profile for LWM2M..."); + schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "3.3.2", "schema_update_lwm2m_bootstrap.sql"); + loadSql(schemaUpdateFile, conn); conn.createStatement().execute("call update_profile_bootstrap();"); log.info("Server`s public key from HexDec to Base64 in profile for LWM2M updated."); log.info("Updating client`s public key and secret key from HexDec to Base64 for LWM2M..."); From b49c32e7b15117df4842386eebb02b6e0bedffd2 Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Tue, 11 Jan 2022 18:19:36 +0200 Subject: [PATCH 090/798] lwm2m frefactoring by comments2 --- .../lwm2m/AbstractLwM2MIntegrationTest.java | 4 +- .../transport/lwm2m/Lwm2mTestHelper.java | 42 +++++----- .../lwm2m/client/LwM2MTestClient.java | 25 ++---- .../client/LwM2mBinaryAppDataContainer.java | 11 +-- .../transport/lwm2m/client/LwM2mLocation.java | 4 - .../ota/AbstractOtaLwM2MIntegrationTest.java | 4 +- .../ota/sql/OtaLwM2MIntegrationTest.java | 2 +- .../rpc/AbstractRpcLwM2MIntegrationTest.java | 54 ++++++------- .../sql/RpcLwm2mIntegrationCreateTest.java | 30 ++++---- .../sql/RpcLwm2mIntegrationDeleteTest.java | 13 ++-- .../sql/RpcLwm2mIntegrationDiscoverTest.java | 8 +- .../sql/RpcLwm2mIntegrationExecuteTest.java | 30 ++++---- .../sql/RpcLwm2mIntegrationObserveTest.java | 26 +++---- .../rpc/sql/RpcLwm2mIntegrationReadTest.java | 68 ++++++++-------- ...pcLwm2mIntegrationWriteAttributesTest.java | 11 +-- .../rpc/sql/RpcLwm2mIntegrationWriteTest.java | 77 +++++++++---------- .../AbstractSecurityLwM2MIntegrationTest.java | 4 +- 17 files changed, 190 insertions(+), 223 deletions(-) diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java index 4fc9108862..402e95b9ca 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java @@ -69,7 +69,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. @DaoSqlTest public abstract class AbstractLwM2MIntegrationTest extends AbstractWebsocketTest { - protected String transportConfiguration = "{\n" + + protected final String TRANSPORT_CONFIGURATION = "{\n" + " \"type\": \"LWM2M\",\n" + " \"observeAttr\": {\n" + " \"keyName\": {\n" + @@ -176,7 +176,7 @@ public abstract class AbstractLwM2MIntegrationTest extends AbstractWebsocketTest LwM2MClientCredential credentials, NetworkConfig coapConfig, String endpoint) throws Exception { - createDeviceProfile(transportConfiguration); + createDeviceProfile(TRANSPORT_CONFIGURATION); Device device = createDevice(credentials); SingleEntityFilter sef = new SingleEntityFilter(); diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/Lwm2mTestHelper.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/Lwm2mTestHelper.java index 8dd44d25ae..19bde32467 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/Lwm2mTestHelper.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/Lwm2mTestHelper.java @@ -43,26 +43,26 @@ public class Lwm2mTestHelper { public static final int TEMPERATURE_SENSOR = 3303; // Ids in Client - public static final int objectId_0 = 0; - public static final int objectInstanceId_0 = 0; - public static final int objectInstanceId_1 = 1; - public static final int objectInstanceId_2 = 2; - public static final int objectInstanceId_12 = 12; - public static final int resourceId_0 = 0; - public static final int resourceId_1 = 1; - public static final int resourceId_2 = 2; - public static final int resourceId_3 = 3; - public static final int resourceId_4 = 4; - public static final int resourceId_7 = 7; - public static final int resourceId_8 = 8; - public static final int resourceId_9 = 9; - public static final int resourceId_11 = 11; - public static final int resourceId_14 = 14; - public static final int resourceId_15= 15; - public static final int resourceInstanceId_2 = 2; + public static final int OBJECT_ID_0 = 0; + public static final int OBJECT_INSTANCE_ID_0 = 0; + public static final int OBJECT_INSTANCE_ID_1 = 1; + public static final int OBJECT_INSTANCE_ID_2 = 2; + public static final int OBJECT_INSTANCE_ID_12 = 12; + public static final int RESOURCE_ID_0 = 0; + public static final int RESOURCE_ID_1 = 1; + public static final int RESOURCE_ID_2 = 2; + public static final int RESOURCE_ID_3 = 3; + public static final int RESOURCE_ID_4 = 4; + public static final int RESOURCE_ID_7 = 7; + public static final int RESOURCE_ID_8 = 8; + public static final int RESOURCE_ID_9 = 9; + public static final int RESOURCE_ID_11 = 11; + public static final int RESOURCE_ID_14 = 14; + public static final int RESOURCE_ID_15 = 15; + public static final int RESOURCE_INSTANCE_ID_2 = 2; - public static final String resourceIdName_3_9 = "batteryLevel"; - public static final String resourceIdName_3_14 = "UtfOffset"; - public static final String resourceIdName_19_0_0 = "dataRead"; - public static final String resourceIdName_19_1_0 = "dataWrite"; + public static final String RESOURCE_ID_NAME_3_9 = "batteryLevel"; + public static final String RESOURCE_ID_NAME_3_14 = "UtfOffset"; + public static final String RESOURCE_ID_NAME_19_0_0 = "dataRead"; + public static final String RESOURCE_ID_NAME_19_1_0 = "dataWrite"; } diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/LwM2MTestClient.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/LwM2MTestClient.java index 1c62b2b79d..af12168802 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/LwM2MTestClient.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/LwM2MTestClient.java @@ -62,9 +62,9 @@ import static org.eclipse.leshan.core.LwM2mId.SERVER; import static org.eclipse.leshan.core.LwM2mId.SOFTWARE_MANAGEMENT; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.BINARY_APP_DATA_CONTAINER; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.TEMPERATURE_SENSOR; -import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.objectInstanceId_0; -import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.objectInstanceId_1; -import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.objectInstanceId_12; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.OBJECT_INSTANCE_ID_0; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.OBJECT_INSTANCE_ID_1; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.OBJECT_INSTANCE_ID_12; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resources; @@ -99,12 +99,12 @@ public class LwM2MTestClient { initializer.setInstancesForObject(FIRMWARE, fwLwM2MDevice = new FwLwM2MDevice()); initializer.setInstancesForObject(SOFTWARE_MANAGEMENT, swLwM2MDevice = new SwLwM2MDevice()); initializer.setClassForObject(ACCESS_CONTROL, DummyInstanceEnabler.class); - initializer.setInstancesForObject(BINARY_APP_DATA_CONTAINER, lwM2MBinaryAppDataContainer = new LwM2mBinaryAppDataContainer(executor, objectInstanceId_0), - new LwM2mBinaryAppDataContainer(executor, objectInstanceId_1)); + initializer.setInstancesForObject(BINARY_APP_DATA_CONTAINER, lwM2MBinaryAppDataContainer = new LwM2mBinaryAppDataContainer(executor, OBJECT_INSTANCE_ID_0), + new LwM2mBinaryAppDataContainer(executor, OBJECT_INSTANCE_ID_1)); locationParams = new LwM2MLocationParams(); locationParams.getPos(); - initializer.setInstancesForObject(LOCATION, new LwM2mLocation(locationParams.getLatitude(), locationParams.getLongitude(), locationParams.getScaleFactor(), executor, objectInstanceId_0)); - initializer.setInstancesForObject(TEMPERATURE_SENSOR, lwM2MTemperatureSensor = new LwM2mTemperatureSensor(executor, objectInstanceId_0), new LwM2mTemperatureSensor(executor, objectInstanceId_12)); + initializer.setInstancesForObject(LOCATION, new LwM2mLocation(locationParams.getLatitude(), locationParams.getLongitude(), locationParams.getScaleFactor(), executor, OBJECT_INSTANCE_ID_0)); + initializer.setInstancesForObject(TEMPERATURE_SENSOR, lwM2MTemperatureSensor = new LwM2mTemperatureSensor(executor, OBJECT_INSTANCE_ID_0), new LwM2mTemperatureSensor(executor, OBJECT_INSTANCE_ID_12)); DtlsConnectorConfig.Builder dtlsConfig = new DtlsConnectorConfig.Builder(); dtlsConfig.setRecommendedCipherSuitesOnly(true); @@ -130,16 +130,6 @@ public class LwM2MTestClient { ObservationStore store) { CoapEndpoint.Builder builder = new CoapEndpoint.Builder(); DtlsConnectorConfig.Builder dtlsConfigBuilder = new DtlsConnectorConfig.Builder(dtlsConfig); - - // tricks to be able to change psk information on the fly -// AdvancedPskStore pskStore = dtlsConfig.getAdvancedPskStore(); -// if (pskStore != null) { -// PskPublicInformation identity = pskStore.getIdentity(null, null); -// SecretKey key = pskStore -// .requestPskSecretResult(ConnectionId.EMPTY, null, identity, null, null, null).getSecret(); -// singlePSKStore = new SinglePSKStore(identity, key); -// dtlsConfigBuilder.setAdvancedPskStore(singlePSKStore); -// } builder.setConnector(new DTLSConnector(dtlsConfigBuilder.build())); builder.setNetworkConfig(coapConfig); return builder.build(); @@ -283,5 +273,4 @@ public class LwM2MTestClient { client.start(); } } - } diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/LwM2mBinaryAppDataContainer.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/LwM2mBinaryAppDataContainer.java index b7ac889649..7da7599119 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/LwM2mBinaryAppDataContainer.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/LwM2mBinaryAppDataContainer.java @@ -66,8 +66,7 @@ public class LwM2mBinaryAppDataContainer extends BaseInstanceEnabler implements * "value":4 * }, */ -// private String data = "InNlcnZpY2VJZCI6Ik1ldGVyIiwNCiJzZXJ2aWNlRGF0YSI6ew0KImN1cnJlbnRSZWFkaW5nIjoiNDYuMyIsDQoic2lnbmFsU3RyZW5ndGgiOjE2LA0KImRhaWx5QWN0aXZpdHlUaW1lIjo1NzA2DQo="; -// private byte[] data; + Map data; private Integer priority = 0; private Time timestamp; @@ -83,7 +82,6 @@ public class LwM2mBinaryAppDataContainer extends BaseInstanceEnabler implements try { if (id != null) this.setId(id); executorService.scheduleWithFixedDelay(() -> -// fireResourcesChange(0, 2), 5000, 5000, TimeUnit.MILLISECONDS); fireResourcesChange(0, 2), 1800000, 1800000, TimeUnit.MILLISECONDS); // 30 MIN } catch (Throwable e) { log.error("[{}]Throwable", e.toString()); @@ -93,15 +91,11 @@ public class LwM2mBinaryAppDataContainer extends BaseInstanceEnabler implements @Override public ReadResponse read(ServerIdentity identity, int resourceId) { -// log.warn("Read on Location resource /[{}]/[{}]/[{}]", getModel().id, getId(), resourceId); try { switch (resourceId) { case 0: -// log.warn("Read on Location resource /[{}]/[{}]/[{}]", getModel().id, getId(), resourceId); ReadResponse response = ReadResponse.success(resourceId, getData(), ResourceModel.Type.OPAQUE); -// log.warn("Response [{}]", response); return response; - case 1: return ReadResponse.success(resourceId, getPriority()); case 2: @@ -168,7 +162,6 @@ public class LwM2mBinaryAppDataContainer extends BaseInstanceEnabler implements } private String getDataFormat() { -// return this.dataFormat == null ? "base64" : this.dataFormat; return this.dataFormat == null ? "OPAQUE" : this.dataFormat; } @@ -188,7 +181,6 @@ public class LwM2mBinaryAppDataContainer extends BaseInstanceEnabler implements return this.timestamp != null ? this.timestamp : new Time(new Date().getTime()); } -// fireResourcesChange(resourceId); private boolean setData(LwM2mResource value, boolean replace) { try { if (value instanceof LwM2mMultipleResource) { @@ -208,7 +200,6 @@ public class LwM2mBinaryAppDataContainer extends BaseInstanceEnabler implements } private Map getData() { -// this.data.put(23, new byte[]{0,0, 2,3}); return data; } diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/LwM2mLocation.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/LwM2mLocation.java index 079cd9fdac..b4dd8531f2 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/LwM2mLocation.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/LwM2mLocation.java @@ -101,19 +101,15 @@ public class LwM2mLocation extends BaseInstanceEnabler implements Destroyable { switch (nextMove.charAt(0)) { case 'w': moveLatitude(1.0f); -// log.info("Move to North [{}]/[{}]", getLatitude(), getLongitude()); break; case 'a': moveLongitude(-1.0f); -// log.info("Move to East [{}]/[{}]", getLatitude(), getLongitude()); break; case 's': moveLatitude(-1.0f); -// log.info("Move to South [{}]/[{}]", getLatitude(), getLongitude()); break; case 'd': moveLongitude(1.0f); -// log.info("Move to West [{}]/[{}]", getLatitude(), getLongitude()); break; } } diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/ota/AbstractOtaLwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/ota/AbstractOtaLwM2MIntegrationTest.java index 6cf35aeb94..a4e5e92db4 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/ota/AbstractOtaLwM2MIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/ota/AbstractOtaLwM2MIntegrationTest.java @@ -29,13 +29,13 @@ import static org.thingsboard.server.common.data.ota.OtaPackageType.SOFTWARE; @DaoSqlTest public abstract class AbstractOtaLwM2MIntegrationTest extends AbstractLwM2MIntegrationTest { - private final String[] resources = new String[]{"3.xml", "5.xml", "9.xml"}; + private final String[] RESOURCES_OTA = new String[]{"3.xml", "5.xml", "9.xml"}; protected static final String CLIENT_ENDPOINT_WITHOUT_FW_INFO = "WithoutFirmwareInfoDevice"; protected static final String CLIENT_ENDPOINT_OTA5 = "Ota5_Device"; protected static final String CLIENT_ENDPOINT_OTA9 = "Ota9_Device"; public AbstractOtaLwM2MIntegrationTest() { - setResources(this.resources); + setResources(this.RESOURCES_OTA); } protected OtaPackageInfo createFirmware() throws Exception { diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/ota/sql/OtaLwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/ota/sql/OtaLwM2MIntegrationTest.java index 3b152ec728..38db441e5d 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/ota/sql/OtaLwM2MIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/ota/sql/OtaLwM2MIntegrationTest.java @@ -138,7 +138,7 @@ public class OtaLwM2MIntegrationTest extends AbstractOtaLwM2MIntegrationTest { @Test public void testFirmwareUpdateWithClientWithoutFirmwareOtaInfoFromProfile() throws Exception { - createDeviceProfile(transportConfiguration); + createDeviceProfile(TRANSPORT_CONFIGURATION); NoSecClientCredential credentials = createNoSecClientCredentials(this.CLIENT_ENDPOINT_WITHOUT_FW_INFO); final Device device = createDevice(credentials); createNewClient(SECURITY, COAP_CONFIG, false, this.CLIENT_ENDPOINT_WITHOUT_FW_INFO); diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/AbstractRpcLwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/AbstractRpcLwM2MIntegrationTest.java index 4303c5e6eb..fba8109dbf 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/AbstractRpcLwM2MIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/AbstractRpcLwM2MIntegrationTest.java @@ -34,16 +34,16 @@ import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.BINARY_APP_ import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.COAP_CONFIG; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.SECURITY; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.TEMPERATURE_SENSOR; -import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.objectId_0; -import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.objectInstanceId_0; -import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.objectInstanceId_1; -import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceIdName_19_0_0; -import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceIdName_19_1_0; -import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceIdName_3_14; -import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceIdName_3_9; -import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceId_0; -import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceId_14; -import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceId_9; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.OBJECT_ID_0; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.OBJECT_INSTANCE_ID_0; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.OBJECT_INSTANCE_ID_1; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_NAME_19_0_0; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_NAME_19_1_0; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_NAME_3_14; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_NAME_3_9; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_0; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_14; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_9; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resources; @DaoSqlTest @@ -60,13 +60,13 @@ public abstract class AbstractRpcLwM2MIntegrationTest extends AbstractLwM2MInteg protected String objectInstanceIdVer_1; protected String objectIdVer_0; protected String objectIdVer_2; - private static final Predicate predicate_3 = path -> (!((String) path).contains("/" + TEMPERATURE_SENSOR) && ((String) path).contains("/" + DEVICE)); + private static final Predicate PREDICATE_3 = path -> (!((String) path).contains("/" + TEMPERATURE_SENSOR) && ((String) path).contains("/" + DEVICE)); protected String objectIdVer_3; protected String objectInstanceIdVer_3; protected String objectInstanceIdVer_5; protected String objectInstanceIdVer_9; protected String objectIdVer_19; - protected String objectIdVer_50 = "/50"; + protected final String OBJECT_ID_VER_50 = "/50"; protected String objectIdVer_3303; protected static AtomicInteger endpointSequence = new AtomicInteger(); protected static String DEVICE_ENDPOINT_RPC_PREF = "deviceEndpointRpc"; @@ -100,19 +100,19 @@ public abstract class AbstractRpcLwM2MIntegrationTest extends AbstractLwM2MInteg }); } }); - String ver_Id_0 = client.getClient().getObjectTree().getModel().getObjectModel(objectId_0).version; + String ver_Id_0 = client.getClient().getObjectTree().getModel().getObjectModel(OBJECT_ID_0).version; if ("1.0".equals(ver_Id_0)) { - objectIdVer_0 = "/" + objectId_0; + objectIdVer_0 = "/" + OBJECT_ID_0; } else { - objectIdVer_0 = "/" + objectId_0 + "_" + ver_Id_0; + objectIdVer_0 = "/" + OBJECT_ID_0 + "_" + ver_Id_0; } objectIdVer_2 = (String) expectedObjectIdVers.stream().filter(path -> ((String) path).contains("/" + ACCESS_CONTROL)).findFirst().get(); - objectIdVer_3 = (String) expectedObjects.stream().filter(predicate_3).findFirst().get(); + objectIdVer_3 = (String) expectedObjects.stream().filter(PREDICATE_3).findFirst().get(); objectIdVer_19 = (String) expectedObjectIdVers.stream().filter(path -> ((String) path).contains("/" + BINARY_APP_DATA_CONTAINER)).findFirst().get(); objectIdVer_3303 = (String) expectedObjectIdVers.stream().filter(path -> ((String) path).contains("/" + TEMPERATURE_SENSOR)).findFirst().get(); objectInstanceIdVer_1 = (String) expectedObjectIdVerInstances.stream().filter(path -> (!((String) path).contains("/" + BINARY_APP_DATA_CONTAINER) && ((String) path).contains("/" + SERVER))).findFirst().get(); - objectInstanceIdVer_3 = (String) expectedObjectIdVerInstances.stream().filter(predicate_3).findFirst().get(); + objectInstanceIdVer_3 = (String) expectedObjectIdVerInstances.stream().filter(PREDICATE_3).findFirst().get(); objectInstanceIdVer_5 = (String) expectedObjectIdVerInstances.stream().filter(path -> ((String) path).contains("/" + FIRMWARE)).findFirst().get(); objectInstanceIdVer_9 = (String) expectedObjectIdVerInstances.stream().filter(path -> ((String) path).contains("/" + SOFTWARE_MANAGEMENT)).findFirst().get(); @@ -120,22 +120,22 @@ public abstract class AbstractRpcLwM2MIntegrationTest extends AbstractLwM2MInteg " \"type\": \"LWM2M\",\n" + " \"observeAttr\": {\n" + " \"keyName\": {\n" + - " \"" + objectIdVer_3 + "/" + objectInstanceId_0 + "/" + resourceId_9 + "\": \"" + resourceIdName_3_9 + "\",\n" + - " \"" + objectIdVer_3 + "/" + objectInstanceId_0 + "/" + resourceId_14 + "\": \"" + resourceIdName_3_14 + "\",\n" + - " \"" + objectIdVer_19 + "/" + objectInstanceId_0 + "/" + resourceId_0 + "\": \"" + resourceIdName_19_0_0 + "\",\n" + - " \"" + objectIdVer_19 + "/" + objectInstanceId_1 + "/" + resourceId_0 + "\": \"" + resourceIdName_19_1_0 + "\"\n" + + " \"" + objectIdVer_3 + "/" + OBJECT_INSTANCE_ID_0 + "/" + RESOURCE_ID_9 + "\": \"" + RESOURCE_ID_NAME_3_9 + "\",\n" + + " \"" + objectIdVer_3 + "/" + OBJECT_INSTANCE_ID_0 + "/" + RESOURCE_ID_14 + "\": \"" + RESOURCE_ID_NAME_3_14 + "\",\n" + + " \"" + objectIdVer_19 + "/" + OBJECT_INSTANCE_ID_0 + "/" + RESOURCE_ID_0 + "\": \"" + RESOURCE_ID_NAME_19_0_0 + "\",\n" + + " \"" + objectIdVer_19 + "/" + OBJECT_INSTANCE_ID_1 + "/" + RESOURCE_ID_0 + "\": \"" + RESOURCE_ID_NAME_19_1_0 + "\"\n" + " },\n" + " \"observe\": [\n" + - " \"" + objectIdVer_3 + "/" + objectInstanceId_0 + "/" + resourceId_9 + "\",\n" + - " \"" + objectIdVer_19 + "/" + objectInstanceId_0 + "/" + resourceId_0 + "\"\n" + + " \"" + objectIdVer_3 + "/" + OBJECT_INSTANCE_ID_0 + "/" + RESOURCE_ID_9 + "\",\n" + + " \"" + objectIdVer_19 + "/" + OBJECT_INSTANCE_ID_0 + "/" + RESOURCE_ID_0 + "\"\n" + " ],\n" + " \"attribute\": [\n" + " ],\n" + " \"telemetry\": [\n" + - " \"" + objectIdVer_3 + "/" + objectInstanceId_0 + "/" + resourceId_9 + "\",\n" + - " \"" + objectIdVer_3 + "/" + objectInstanceId_0 + "/" + resourceId_14 + "\",\n" + - " \"" + objectIdVer_19 + "/" + objectInstanceId_0 + "/" + resourceId_0 + "\",\n" + - " \"" + objectIdVer_19 + "/" + objectInstanceId_1 + "/" + resourceId_0 + "\"\n" + + " \"" + objectIdVer_3 + "/" + OBJECT_INSTANCE_ID_0 + "/" + RESOURCE_ID_9 + "\",\n" + + " \"" + objectIdVer_3 + "/" + OBJECT_INSTANCE_ID_0 + "/" + RESOURCE_ID_14 + "\",\n" + + " \"" + objectIdVer_19 + "/" + OBJECT_INSTANCE_ID_0 + "/" + RESOURCE_ID_0 + "\",\n" + + " \"" + objectIdVer_19 + "/" + OBJECT_INSTANCE_ID_1 + "/" + RESOURCE_ID_0 + "\"\n" + " ],\n" + " \"attributeLwm2m\": {}\n" + " },\n" + diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationCreateTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationCreateTest.java index 4ab0d68794..40d26bfc17 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationCreateTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationCreateTest.java @@ -25,10 +25,10 @@ import org.thingsboard.server.transport.lwm2m.rpc.AbstractRpcLwM2MIntegrationTes import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; -import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.objectInstanceId_0; -import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.objectInstanceId_1; -import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.objectInstanceId_12; -import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceId_0; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.OBJECT_INSTANCE_ID_0; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.OBJECT_INSTANCE_ID_1; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.OBJECT_INSTANCE_ID_12; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_0; public class RpcLwm2mIntegrationCreateTest extends AbstractRpcLwM2MIntegrationTest { @@ -43,8 +43,8 @@ public class RpcLwm2mIntegrationCreateTest extends AbstractRpcLwM2MIntegrationTe */ @Test public void testCreateObjectInstanceWithInstanceIdByIdKey_Result_CREATED() throws Exception { - String expectedPath = objectIdVer_19 + "/" + objectInstanceId_12; - String expectedValue = "{\"" + resourceId_0 + "\":{\"0\":\"00AC\"}, \"1\":1}"; + String expectedPath = objectIdVer_19 + "/" + OBJECT_INSTANCE_ID_12; + String expectedValue = "{\"" + RESOURCE_ID_0 + "\":{\"0\":\"00AC\"}, \"1\":1}"; String actualResult = sendRPCreateById(expectedPath, expectedValue); ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); assertEquals(ResponseCode.CREATED.getName(), rpcActualResult.get("result").asText()); @@ -60,12 +60,12 @@ public class RpcLwm2mIntegrationCreateTest extends AbstractRpcLwM2MIntegrationTe */ @Test public void testCreateObjectInstanceWithInstanceIdAlreadyExistsById_Result_BAD_REQUEST() throws Exception { - String expectedPath = objectIdVer_19 + "/" + objectInstanceId_0; - String expectedValue = "{\"" + resourceId_0 + "\":{\"0\":\"00AC\"}, \"1\":1}"; + String expectedPath = objectIdVer_19 + "/" + OBJECT_INSTANCE_ID_0; + String expectedValue = "{\"" + RESOURCE_ID_0 + "\":{\"0\":\"00AC\"}, \"1\":1}"; String actualResult = sendRPCreateById(expectedPath, expectedValue); ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); assertEquals(ResponseCode.BAD_REQUEST.getName(), rpcActualResult.get("result").asText()); - String expected = "instance " + objectInstanceId_0 + " already exists"; + String expected = "instance " + OBJECT_INSTANCE_ID_0 + " already exists"; String actual = rpcActualResult.get("error").asText(); assertTrue(actual.equals(expected)); } @@ -77,8 +77,8 @@ public class RpcLwm2mIntegrationCreateTest extends AbstractRpcLwM2MIntegrationTe */ @Test public void testCreateObjectInstanceWithInstanceIdMandatorySingleObjectById_Result_BAD_REQUEST() throws Exception { - String expectedPath = objectIdVer_3 + "/" + objectInstanceId_1; - String expectedValue = "{\"" + resourceId_0 + "\":{\"0\":\"00AC\"}}"; + String expectedPath = objectIdVer_3 + "/" + OBJECT_INSTANCE_ID_1; + String expectedValue = "{\"" + RESOURCE_ID_0 + "\":{\"0\":\"00AC\"}}"; String actualResult = sendRPCreateById(expectedPath, expectedValue); ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); assertEquals(ResponseCode.BAD_REQUEST.getName(), rpcActualResult.get("result").asText()); @@ -94,8 +94,8 @@ public class RpcLwm2mIntegrationCreateTest extends AbstractRpcLwM2MIntegrationTe */ @Test public void testCreateObjectInstanceWithInstanceIdSecurityObjectById_Result_BAD_REQUEST() throws Exception { - String expectedPath = objectIdVer_0 + "/" + objectInstanceId_1; - String expectedValue = "{\"" + resourceId_0 + "\":{\"2\":4}}"; + String expectedPath = objectIdVer_0 + "/" + OBJECT_INSTANCE_ID_1; + String expectedValue = "{\"" + RESOURCE_ID_0 + "\":{\"2\":4}}"; String actualResult = sendRPCreateById(expectedPath, expectedValue); ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); assertEquals(ResponseCode.BAD_REQUEST.getName(), rpcActualResult.get("result").asText()); @@ -113,8 +113,8 @@ public class RpcLwm2mIntegrationCreateTest extends AbstractRpcLwM2MIntegrationTe */ @Test public void testCreateObjectInstanceWithInstanceIdAbsentObjectById_Result_BAD_REQUEST() throws Exception { - String expectedPath = objectIdVer_50+ "/" + objectInstanceId_1; - String expectedValue = "{\"" + resourceId_0 + "\":{\"0\":\"00AC\"}}"; + String expectedPath = OBJECT_ID_VER_50 + "/" + OBJECT_INSTANCE_ID_1; + String expectedValue = "{\"" + RESOURCE_ID_0 + "\":{\"0\":\"00AC\"}}"; String actualResult = sendRPCreateById(expectedPath, expectedValue); ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); assertEquals(ResponseCode.BAD_REQUEST.getName(), rpcActualResult.get("result").asText()); diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationDeleteTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationDeleteTest.java index ebc0f6d783..099477d216 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationDeleteTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationDeleteTest.java @@ -24,10 +24,9 @@ import org.thingsboard.server.transport.lwm2m.rpc.AbstractRpcLwM2MIntegrationTes import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; -import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.objectInstanceId_0; -import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.objectInstanceId_12; -import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceId_7; -import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceId_9; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.OBJECT_INSTANCE_ID_0; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.OBJECT_INSTANCE_ID_12; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_7; public class RpcLwm2mIntegrationDeleteTest extends AbstractRpcLwM2MIntegrationTest { @@ -39,7 +38,7 @@ public class RpcLwm2mIntegrationDeleteTest extends AbstractRpcLwM2MIntegrationTe */ @Test public void testDeleteObjectInstanceIsSuchByIdKey_Result_DELETED() throws Exception { - String expectedPath = objectIdVer_3303 + "/" + objectInstanceId_12; + String expectedPath = objectIdVer_3303 + "/" + OBJECT_INSTANCE_ID_12; String actualResult = sendRPCDeleteById(expectedPath); ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); assertEquals(ResponseCode.DELETED.getName(), rpcActualResult.get("result").asText()); @@ -52,7 +51,7 @@ public class RpcLwm2mIntegrationDeleteTest extends AbstractRpcLwM2MIntegrationTe */ @Test public void testDeleteObjectInstanceIsNotSuchByIdKey_Result_NOT_FOUND() throws Exception { - String expectedPath = objectIdVer_19 + "/" + objectInstanceId_12; + String expectedPath = objectIdVer_19 + "/" + OBJECT_INSTANCE_ID_12; String actualResult = sendRPCDeleteById(expectedPath); ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); assertEquals(ResponseCode.NOT_FOUND.getName(), rpcActualResult.get("result").asText()); @@ -82,7 +81,7 @@ public class RpcLwm2mIntegrationDeleteTest extends AbstractRpcLwM2MIntegrationTe */ @Test public void testDeleteResourceByIdKey_Result_METHOD_NOT_ALLOWED() throws Exception { - String expectedPath = objectIdVer_3 + "/" + objectInstanceId_0 + resourceId_7; + String expectedPath = objectIdVer_3 + "/" + OBJECT_INSTANCE_ID_0 + RESOURCE_ID_7; String actualResult = sendRPCDeleteById(expectedPath); ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); assertEquals(ResponseCode.METHOD_NOT_ALLOWED.getName(), rpcActualResult.get("result").asText()); diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationDiscoverTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationDiscoverTest.java index b8ca7be61f..ede501c4a2 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationDiscoverTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationDiscoverTest.java @@ -31,8 +31,8 @@ import java.util.stream.Collectors; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; -import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.objectInstanceId_0; -import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceId_2; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.OBJECT_INSTANCE_ID_0; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_2; public class RpcLwm2mIntegrationDiscoverTest extends AbstractRpcLwM2MIntegrationTest { @@ -141,7 +141,7 @@ public class RpcLwm2mIntegrationDiscoverTest extends AbstractRpcLwM2MIntegration */ @Test public void testDiscoverObjectInstanceAbsentInObject_Return_NOT_FOUND() throws Exception { - String expected = objectIdVer_2 + "/" + objectInstanceId_0; + String expected = objectIdVer_2 + "/" + OBJECT_INSTANCE_ID_0; String actualResult = sendDiscover(expected); ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); assertEquals(ResponseCode.NOT_FOUND.getName(), rpcActualResult.get("result").asText()); @@ -152,7 +152,7 @@ public class RpcLwm2mIntegrationDiscoverTest extends AbstractRpcLwM2MIntegration */ @Test public void testDiscoverResourceAbsentInObject_Return_NOT_FOUND() throws Exception { - String expected = objectIdVer_2 + "/" + objectInstanceId_0 + "/" + resourceId_2; + String expected = objectIdVer_2 + "/" + OBJECT_INSTANCE_ID_0 + "/" + RESOURCE_ID_2; String actualResult = sendDiscover(expected); ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); assertEquals(ResponseCode.NOT_FOUND.getName(), rpcActualResult.get("result").asText()); diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationExecuteTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationExecuteTest.java index 73ea0fb74c..4d13cbd386 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationExecuteTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationExecuteTest.java @@ -25,12 +25,12 @@ import org.thingsboard.server.transport.lwm2m.rpc.AbstractRpcLwM2MIntegrationTes import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; -import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.objectInstanceId_0; -import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceId_2; -import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceId_3; -import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceId_4; -import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceId_8; -import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceId_9; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.OBJECT_INSTANCE_ID_0; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_2; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_3; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_4; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_8; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_9; public class RpcLwm2mIntegrationExecuteTest extends AbstractRpcLwM2MIntegrationTest { @@ -43,7 +43,7 @@ public class RpcLwm2mIntegrationExecuteTest extends AbstractRpcLwM2MIntegrationT */ @Test public void testExecuteUpdateFWById_Result_CHANGED() throws Exception { - String expectedPath = objectInstanceIdVer_5 + "/" + resourceId_2; + String expectedPath = objectInstanceIdVer_5 + "/" + RESOURCE_ID_2; String actualResult = sendRPCExecuteById(expectedPath); ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); assertEquals(ResponseCode.CHANGED.getName(), rpcActualResult.get("result").asText()); @@ -56,7 +56,7 @@ public class RpcLwm2mIntegrationExecuteTest extends AbstractRpcLwM2MIntegrationT */ @Test public void testExecuteUpdateSWById_Result_CHANGED() throws Exception { - String expectedPath = objectInstanceIdVer_9 + "/" + resourceId_4; + String expectedPath = objectInstanceIdVer_9 + "/" + RESOURCE_ID_4; String actualResult = sendRPCExecuteById(expectedPath); ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); assertEquals(ResponseCode.CHANGED.getName(), rpcActualResult.get("result").asText()); @@ -69,7 +69,7 @@ public class RpcLwm2mIntegrationExecuteTest extends AbstractRpcLwM2MIntegrationT */ @Test public void testExecuteRebootById_Result_CHANGED() throws Exception { - String expectedPath = objectInstanceIdVer_3 + "/" + resourceId_4; + String expectedPath = objectInstanceIdVer_3 + "/" + RESOURCE_ID_4; String actualResult = sendRPCExecuteById(expectedPath); ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); assertEquals(ResponseCode.CHANGED.getName(), rpcActualResult.get("result").asText()); @@ -82,7 +82,7 @@ public class RpcLwm2mIntegrationExecuteTest extends AbstractRpcLwM2MIntegrationT */ @Test public void testExecuteRegistrationUpdateTriggerById_Result_CHANGED() throws Exception { - String expectedPath = objectInstanceIdVer_1 + "/" + resourceId_8; + String expectedPath = objectInstanceIdVer_1 + "/" + RESOURCE_ID_8; String actualResult = sendRPCExecuteById(expectedPath); ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); assertEquals(ResponseCode.CHANGED.getName(), rpcActualResult.get("result").asText()); @@ -96,7 +96,7 @@ public class RpcLwm2mIntegrationExecuteTest extends AbstractRpcLwM2MIntegrationT */ @Test public void testExecuteResourceWithParametersById_Result_CHANGED() throws Exception { - String expectedPath = objectInstanceIdVer_3 + "/" + resourceId_4; + String expectedPath = objectInstanceIdVer_3 + "/" + RESOURCE_ID_4; Object expectedValue = 60; String actualResult = sendRPCExecuteWithValueById(expectedPath, expectedValue); ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); @@ -110,7 +110,7 @@ public class RpcLwm2mIntegrationExecuteTest extends AbstractRpcLwM2MIntegrationT */ @Test public void testExecuteBootstrapRequestTriggerById_Result_BAD_REQUEST_Error_NoBootstrapServerConfigured() throws Exception { - String expectedPath = objectInstanceIdVer_1 + "/" + resourceId_9; + String expectedPath = objectInstanceIdVer_1 + "/" + RESOURCE_ID_9; String actualResult = sendRPCExecuteById(expectedPath); ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); assertEquals(ResponseCode.BAD_REQUEST.getName(), rpcActualResult.get("result").asText()); @@ -126,7 +126,7 @@ public class RpcLwm2mIntegrationExecuteTest extends AbstractRpcLwM2MIntegrationT */ @Test public void testExecuteResourceWithOperationNotExecuteById_Result_METHOD_NOT_ALLOWED() throws Exception { - String expectedPath = objectInstanceIdVer_5 + "/" + resourceId_3; + String expectedPath = objectInstanceIdVer_5 + "/" + RESOURCE_ID_3; String actualResult = sendRPCExecuteById(expectedPath); ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); assertEquals(ResponseCode.BAD_REQUEST.getName(), rpcActualResult.get("result").asText()); @@ -143,7 +143,7 @@ public class RpcLwm2mIntegrationExecuteTest extends AbstractRpcLwM2MIntegrationT */ @Test public void testExecuteNonExistingResourceOnNonExistingObjectById_Result_BAD_REQUEST() throws Exception { - String expectedPath = objectIdVer_50 + "/" + objectInstanceId_0 + "/" + resourceId_3; + String expectedPath = OBJECT_ID_VER_50 + "/" + OBJECT_INSTANCE_ID_0 + "/" + RESOURCE_ID_3; String actualResult = sendRPCExecuteById(expectedPath); ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); assertEquals(ResponseCode.BAD_REQUEST.getName(), rpcActualResult.get("result").asText()); @@ -161,7 +161,7 @@ public class RpcLwm2mIntegrationExecuteTest extends AbstractRpcLwM2MIntegrationT */ @Test public void testExecuteSecurityObjectById_Result_NOT_FOUND() throws Exception { - String expectedPath = objectIdVer_0 + "/" + objectInstanceId_0 + "/" + resourceId_3; + String expectedPath = objectIdVer_0 + "/" + OBJECT_INSTANCE_ID_0 + "/" + RESOURCE_ID_3; String actualResult = sendRPCExecuteById(expectedPath); ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); assertEquals(ResponseCode.BAD_REQUEST.getName(), rpcActualResult.get("result").asText()); diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationObserveTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationObserveTest.java index 848a9563ed..f6b53c8c07 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationObserveTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationObserveTest.java @@ -27,10 +27,10 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.BINARY_APP_DATA_CONTAINER; -import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.objectInstanceId_0; -import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceId_0; -import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceId_3; -import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceId_9; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.OBJECT_INSTANCE_ID_0; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_0; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_3; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_9; public class RpcLwm2mIntegrationObserveTest extends AbstractRpcLwM2MIntegrationTest { @@ -55,7 +55,7 @@ public class RpcLwm2mIntegrationObserveTest extends AbstractRpcLwM2MIntegrationT */ @Test public void testObserveSingleResource_Result_CONTENT_Value_SingleResource() throws Exception { - String expectedIdVer = objectInstanceIdVer_3 + "/" + resourceId_9; + String expectedIdVer = objectInstanceIdVer_3 + "/" + RESOURCE_ID_9; String actualResult = sendObserve("Observe", expectedIdVer); ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); assertEquals(ResponseCode.CONTENT.getName(), rpcActualResult.get("result").asText()); @@ -87,7 +87,7 @@ public class RpcLwm2mIntegrationObserveTest extends AbstractRpcLwM2MIntegrationT @Test public void testObserveNoImplementedInstanceOnDevice_Result_NotFound() throws Exception { String objectInstanceIdVer = (String) expectedObjectIdVers.stream().filter(path -> ((String)path).contains("/" + ACCESS_CONTROL)).findFirst().get(); - String expected = objectInstanceIdVer + "/" + objectInstanceId_0; + String expected = objectInstanceIdVer + "/" + OBJECT_INSTANCE_ID_0; String actualResult = sendObserve("Observe", expected); ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); assertEquals(ResponseCode.NOT_FOUND.getName(), rpcActualResult.get("result").asText()); @@ -101,7 +101,7 @@ public class RpcLwm2mIntegrationObserveTest extends AbstractRpcLwM2MIntegrationT @Test public void testObserveNoImplementedResourceOnDeviceValueNull_Result_BadRequest() throws Exception { String objectIdVer = (String) expectedObjectIdVers.stream().filter(path -> ((String)path).contains("/" + BINARY_APP_DATA_CONTAINER)).findFirst().get(); - String expected = objectIdVer + "/" + objectInstanceId_0 + "/" + resourceId_0; + String expected = objectIdVer + "/" + OBJECT_INSTANCE_ID_0 + "/" + RESOURCE_ID_0; String actualResult = sendObserve("Observe", expected); ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); String expectedValue = "values MUST NOT be null"; @@ -116,7 +116,7 @@ public class RpcLwm2mIntegrationObserveTest extends AbstractRpcLwM2MIntegrationT */ @Test public void testObserveRSourceNotRead_Result_METHOD_NOT_ALLOWED() throws Exception { - String expectedId = objectInstanceIdVer_5 + "/" + resourceId_0; + String expectedId = objectInstanceIdVer_5 + "/" + RESOURCE_ID_0; sendObserve("Observe", expectedId); String actualResult = sendObserve("Observe", expectedId); ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); @@ -130,7 +130,7 @@ public class RpcLwm2mIntegrationObserveTest extends AbstractRpcLwM2MIntegrationT */ @Test public void testObserveRepeatedRequestObserveOnDevice_Result_BAD_REQUEST_ErrorMsg_AlreadyRegistered() throws Exception { - String expectedId = objectInstanceIdVer_3 + "/" + resourceId_0; + String expectedId = objectInstanceIdVer_3 + "/" + RESOURCE_ID_0; sendObserve("Observe", expectedId); String actualResult = sendObserve("Observe", expectedId); ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); @@ -146,8 +146,8 @@ public class RpcLwm2mIntegrationObserveTest extends AbstractRpcLwM2MIntegrationT @Test public void testObserveReadAll_Result_CONTENT_Value_Contains_Paths_Count_ObserveAll() throws Exception { sendObserve("ObserveCancelAll", null); - String expectedId_0 = objectInstanceIdVer_3 + "/" + resourceId_0; - String expectedId_9 = objectInstanceIdVer_3 + "/" + resourceId_9; + String expectedId_0 = objectInstanceIdVer_3 + "/" + RESOURCE_ID_0; + String expectedId_9 = objectInstanceIdVer_3 + "/" + RESOURCE_ID_9; sendObserve("Observe", expectedId_0); sendObserve("Observe", expectedId_9); String actualResult = sendObserve("ObserveReadAll", null); @@ -167,8 +167,8 @@ public class RpcLwm2mIntegrationObserveTest extends AbstractRpcLwM2MIntegrationT @Test public void testObserveCancelOneResource_Result_CONTENT_Value_Count_1() throws Exception { sendObserve("ObserveCancelAll", null); - String expectedId_0 = objectInstanceIdVer_3 + "/" + resourceId_0; - String expectedId_3 = objectInstanceIdVer_5 + "/" + resourceId_3; + String expectedId_0 = objectInstanceIdVer_3 + "/" + RESOURCE_ID_0; + String expectedId_3 = objectInstanceIdVer_5 + "/" + RESOURCE_ID_3; sendObserve("Observe", expectedId_0); sendObserve("Observe", expectedId_3); String actualResult = sendObserve("ObserveCancel", expectedId_0); diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationReadTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationReadTest.java index 9116659bc3..aece59d119 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationReadTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationReadTest.java @@ -27,18 +27,18 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.BINARY_APP_DATA_CONTAINER; -import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.objectInstanceId_0; -import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.objectInstanceId_1; -import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceIdName_19_0_0; -import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceIdName_19_1_0; -import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceIdName_3_14; -import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceIdName_3_9; -import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceId_0; -import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceId_1; -import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceId_11; -import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceId_14; -import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceId_2; -import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceId_9; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.OBJECT_INSTANCE_ID_0; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.OBJECT_INSTANCE_ID_1; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_NAME_19_0_0; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_NAME_19_1_0; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_NAME_3_14; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_NAME_3_9; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_0; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_1; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_11; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_14; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_2; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_9; public class RpcLwm2mIntegrationReadTest extends AbstractRpcLwM2MIntegrationTest { @@ -96,11 +96,11 @@ public class RpcLwm2mIntegrationReadTest extends AbstractRpcLwM2MIntegrationTest */ @Test public void testReadMultipleResourceById_Result_CONTENT_Value_IsLwM2mMultipleResource() throws Exception { - String expectedIdVer = objectInstanceIdVer_3 +"/" + resourceId_11 ; + String expectedIdVer = objectInstanceIdVer_3 +"/" + RESOURCE_ID_11; String actualResult = sendRPCById(expectedIdVer); ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); assertEquals(ResponseCode.CONTENT.getName(), rpcActualResult.get("result").asText()); - String expected = "LwM2mMultipleResource [id=" + resourceId_11 + ", values={"; + String expected = "LwM2mMultipleResource [id=" + RESOURCE_ID_11 + ", values={"; assertTrue(rpcActualResult.get("value").asText().contains(expected)); } @@ -109,11 +109,11 @@ public class RpcLwm2mIntegrationReadTest extends AbstractRpcLwM2MIntegrationTest */ @Test public void testReadSingleResourceById_Result_CONTENT_Value_IsLwM2mSingleResource() throws Exception { - String expectedIdVer = objectInstanceIdVer_3 +"/" + resourceId_14 ; + String expectedIdVer = objectInstanceIdVer_3 +"/" + RESOURCE_ID_14; String actualResult = sendRPCById(expectedIdVer); ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); assertEquals(ResponseCode.CONTENT.getName(), rpcActualResult.get("result").asText()); - String expected = "LwM2mSingleResource [id=" + resourceId_14 + ", value="; + String expected = "LwM2mSingleResource [id=" + RESOURCE_ID_14 + ", value="; assertTrue(rpcActualResult.get("value").asText().contains(expected)); } @@ -122,11 +122,11 @@ public class RpcLwm2mIntegrationReadTest extends AbstractRpcLwM2MIntegrationTest */ @Test public void testReadSingleResourceByKey_Result_CONTENT_Value_IsLwM2mSingleResource() throws Exception { - String expectedKey = resourceIdName_3_14 ; + String expectedKey = RESOURCE_ID_NAME_3_14; String actualResult = sendRPCByKey(expectedKey); ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); assertEquals(ResponseCode.CONTENT.getName(), rpcActualResult.get("result").asText()); - String expected = "LwM2mSingleResource [id=" + resourceId_14 + ", value="; + String expected = "LwM2mSingleResource [id=" + RESOURCE_ID_14 + ", value="; assertTrue(rpcActualResult.get("value").asText().contains(expected)); } @@ -137,16 +137,16 @@ public class RpcLwm2mIntegrationReadTest extends AbstractRpcLwM2MIntegrationTest public void testReadCompositeSingleResourceByIds_Result_CONTENT_Value_IsObjectIsLwM2mSingleResourceIsLwM2mMultipleResource() throws Exception { String expectedIdVer_1 = (String) expectedObjectIdVers.stream().filter(path -> (!((String)path).contains("/" + BINARY_APP_DATA_CONTAINER) && ((String)path).contains("/" + SERVER))).findFirst().get(); String objectId_1 = pathIdVerToObjectId(expectedIdVer_1); - String expectedIdVer3_0_1 = objectInstanceIdVer_3 + "/" + resourceId_1; - String expectedIdVer3_0_11 = objectInstanceIdVer_3 + "/" + resourceId_11; + String expectedIdVer3_0_1 = objectInstanceIdVer_3 + "/" + RESOURCE_ID_1; + String expectedIdVer3_0_11 = objectInstanceIdVer_3 + "/" + RESOURCE_ID_11; String objectInstanceId_3 = pathIdVerToObjectId(objectInstanceIdVer_3); String expectedIds = "[\"" + expectedIdVer_1 + "\", \"" + expectedIdVer3_0_1 + "\", \"" + expectedIdVer3_0_11 + "\"]"; String actualResult = sendCompositeRPCByIds(expectedIds); ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); assertEquals(ResponseCode.CONTENT.getName(), rpcActualResult.get("result").asText()); String expected1 = objectId_1 + "=LwM2mObject [id=" + new LwM2mPath(objectId_1).getObjectId() + ", instances={"; - String expected3_0_1 = objectInstanceId_3 + "/" + resourceId_1 + "=LwM2mSingleResource [id=" + resourceId_1 + ", value="; - String expected3_0_11 = objectInstanceId_3 + "/" + resourceId_11 + "=LwM2mMultipleResource [id=" + resourceId_11 + ", values={"; + String expected3_0_1 = objectInstanceId_3 + "/" + RESOURCE_ID_1 + "=LwM2mSingleResource [id=" + RESOURCE_ID_1 + ", value="; + String expected3_0_11 = objectInstanceId_3 + "/" + RESOURCE_ID_11 + "=LwM2mMultipleResource [id=" + RESOURCE_ID_11 + ", values={"; String actualValues = rpcActualResult.get("value").asText(); assertTrue(actualValues.contains(expected1)); assertTrue(actualValues.contains(expected3_0_1)); @@ -159,8 +159,8 @@ public class RpcLwm2mIntegrationReadTest extends AbstractRpcLwM2MIntegrationTest @Test public void testReadCompositeSingleResourceByIds_Result_CONTENT_Value_IsObjectInstanceIsLwM2mSingleResource() throws Exception { String expectedIdVer3_0 = objectInstanceIdVer_3; - String expectedIdVer1_0_1 = objectInstanceIdVer_1 + "/" + resourceId_1; - String expectedIdVer1_0_2 = objectInstanceIdVer_1 + "/" + resourceId_2; + String expectedIdVer1_0_1 = objectInstanceIdVer_1 + "/" + RESOURCE_ID_1; + String expectedIdVer1_0_2 = objectInstanceIdVer_1 + "/" + RESOURCE_ID_2; String expectedIds = "[\"" + expectedIdVer1_0_1 + "\", \"" + expectedIdVer1_0_2 + "\", \"" + expectedIdVer3_0 + "\"]"; String actualResult = sendCompositeRPCByIds(expectedIds); ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); @@ -169,8 +169,8 @@ public class RpcLwm2mIntegrationReadTest extends AbstractRpcLwM2MIntegrationTest LwM2mPath path = new LwM2mPath(objectInstanceId_3); String expected3_0 = objectInstanceId_3 + "=LwM2mObjectInstance [id=" + path.getObjectInstanceId() + ", resources={"; String objectInstanceId_1 = pathIdVerToObjectId(objectInstanceIdVer_1); - String expected1_0_1 = objectInstanceId_1 + "/" + resourceId_1 + "=LwM2mSingleResource [id=" + resourceId_1 + ", value="; - String expected1_0_2 = objectInstanceId_1 + "/" + resourceId_2 + "=null"; + String expected1_0_1 = objectInstanceId_1 + "/" + RESOURCE_ID_1 + "=LwM2mSingleResource [id=" + RESOURCE_ID_1 + ", value="; + String expected1_0_2 = objectInstanceId_1 + "/" + RESOURCE_ID_2 + "=null"; String actualValues = rpcActualResult.get("value").asText(); assertTrue(actualValues.contains(expected3_0)); assertTrue(actualValues.contains(expected1_0_1)); @@ -182,20 +182,20 @@ public class RpcLwm2mIntegrationReadTest extends AbstractRpcLwM2MIntegrationTest */ @Test public void testReadCompositeSingleResourceByKeys_Result_CONTENT_Value_3_0_IsLwM2mSingleResource_19_0_0_AND_19_0_1_Null() throws Exception { - String expectedKey3_0_9 = resourceIdName_3_9; - String expectedKey3_0_14 = resourceIdName_3_14; - String expectedKey19_0_0 = resourceIdName_19_0_0; - String expectedKey19_1_0 = resourceIdName_19_1_0; + String expectedKey3_0_9 = RESOURCE_ID_NAME_3_9; + String expectedKey3_0_14 = RESOURCE_ID_NAME_3_14; + String expectedKey19_0_0 = RESOURCE_ID_NAME_19_0_0; + String expectedKey19_1_0 = RESOURCE_ID_NAME_19_1_0; String expectedKeys = "[\"" + expectedKey3_0_9 + "\", \"" + expectedKey3_0_14 + "\", \"" + expectedKey19_0_0 + "\", \"" + expectedKey19_1_0 + "\"]"; String actualResult = sendCompositeRPCByKeys(expectedKeys); ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); assertEquals(ResponseCode.CONTENT.getName(), rpcActualResult.get("result").asText()); String objectInstanceId_3 = pathIdVerToObjectId(objectInstanceIdVer_3); String objectId_19 = pathIdVerToObjectId(objectIdVer_19); - String expected3_0_9 = objectInstanceId_3 + "/" + resourceId_9 + "=LwM2mSingleResource [id=" + resourceId_9 + ", value="; - String expected3_0_14 = objectInstanceId_3 + "/" + resourceId_14 + "=LwM2mSingleResource [id=" + resourceId_14 + ", value="; - String expected19_0_0 = objectId_19 + "/" + objectInstanceId_0 + "/" + resourceId_0 + "=null"; - String expected19_1_0 = objectId_19 + "/" + objectInstanceId_1 + "/" + resourceId_0 + "=null"; + String expected3_0_9 = objectInstanceId_3 + "/" + RESOURCE_ID_9 + "=LwM2mSingleResource [id=" + RESOURCE_ID_9 + ", value="; + String expected3_0_14 = objectInstanceId_3 + "/" + RESOURCE_ID_14 + "=LwM2mSingleResource [id=" + RESOURCE_ID_14 + ", value="; + String expected19_0_0 = objectId_19 + "/" + OBJECT_INSTANCE_ID_0 + "/" + RESOURCE_ID_0 + "=null"; + String expected19_1_0 = objectId_19 + "/" + OBJECT_INSTANCE_ID_1 + "/" + RESOURCE_ID_0 + "=null"; String actualValues = rpcActualResult.get("value").asText(); assertTrue(actualValues.contains(expected3_0_9)); assertTrue(actualValues.contains(expected3_0_14)); diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationWriteAttributesTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationWriteAttributesTest.java index ea23ab48b9..531fa33dc9 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationWriteAttributesTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationWriteAttributesTest.java @@ -17,7 +17,6 @@ package org.thingsboard.server.transport.lwm2m.rpc.sql; import com.fasterxml.jackson.databind.node.ObjectNode; import org.eclipse.leshan.core.ResponseCode; -import org.eclipse.leshan.core.node.LwM2mPath; import org.junit.Test; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.transport.lwm2m.rpc.AbstractRpcLwM2MIntegrationTest; @@ -25,13 +24,7 @@ import org.thingsboard.server.transport.lwm2m.rpc.AbstractRpcLwM2MIntegrationTes import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; -import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.objectInstanceId_0; -import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceId_14; -import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceId_2; -import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceId_3; -import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceId_4; -import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceId_8; -import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceId_9; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_14; public class RpcLwm2mIntegrationWriteAttributesTest extends AbstractRpcLwM2MIntegrationTest { @@ -45,7 +38,7 @@ public class RpcLwm2mIntegrationWriteAttributesTest extends AbstractRpcLwM2MInte */ @Test public void testWriteAttributesResourceWithParametersById_Result_INTERNAL_SERVER_ERROR() throws Exception { - String expectedPath = objectInstanceIdVer_3 + "/" + resourceId_14; + String expectedPath = objectInstanceIdVer_3 + "/" + RESOURCE_ID_14; String expectedValue = "{\"pmax\":100, \"pmin\":10}"; String actualResult = sendRPCExecuteWithValueById(expectedPath, expectedValue); ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationWriteTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationWriteTest.java index 1a92831a36..f5f06631b9 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationWriteTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationWriteTest.java @@ -25,16 +25,15 @@ import org.thingsboard.server.transport.lwm2m.rpc.AbstractRpcLwM2MIntegrationTes import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; -import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.BINARY_APP_DATA_CONTAINER; -import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.objectInstanceId_0; -import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.objectInstanceId_1; -import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.objectInstanceId_2; -import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceIdName_3_14; -import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceId_0; -import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceId_14; -import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceId_15; -import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceId_9; -import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceInstanceId_2; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.OBJECT_INSTANCE_ID_0; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.OBJECT_INSTANCE_ID_1; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.OBJECT_INSTANCE_ID_2; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_NAME_3_14; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_0; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_14; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_15; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_9; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_INSTANCE_ID_2; public class RpcLwm2mIntegrationWriteTest extends AbstractRpcLwM2MIntegrationTest { @@ -46,7 +45,7 @@ public class RpcLwm2mIntegrationWriteTest extends AbstractRpcLwM2MIntegrationTes */ @Test public void testWriteReplaceValueSingleResourceById_Result_CHANGED() throws Exception { - String expectedPath = objectInstanceIdVer_3 + "/" + resourceId_14; + String expectedPath = objectInstanceIdVer_3 + "/" + RESOURCE_ID_14; String expectedValue = "+12"; String actualResult = sendRPCWriteStringById("WriteReplace", expectedPath, expectedValue); ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); @@ -54,7 +53,7 @@ public class RpcLwm2mIntegrationWriteTest extends AbstractRpcLwM2MIntegrationTes actualResult = sendRPCReadById(expectedPath); rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); String actualValues = rpcActualResult.get("value").asText(); - String expected = "LwM2mSingleResource [id=" + resourceId_14 + ", value=" + expectedValue + ", type=STRING]"; + String expected = "LwM2mSingleResource [id=" + RESOURCE_ID_14 + ", value=" + expectedValue + ", type=STRING]"; assertTrue(actualValues.contains(expected)); } @@ -65,7 +64,7 @@ public class RpcLwm2mIntegrationWriteTest extends AbstractRpcLwM2MIntegrationTes */ @Test public void testWriteReplaceValueSingleResourceByKey_Result_CHANGED() throws Exception { - String expectedKey = resourceIdName_3_14; + String expectedKey = RESOURCE_ID_NAME_3_14; String expectedValue = "+09"; String actualResult = sendRPCWriteByKey("WriteReplace", expectedKey, expectedValue); ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); @@ -73,7 +72,7 @@ public class RpcLwm2mIntegrationWriteTest extends AbstractRpcLwM2MIntegrationTes actualResult = sendRPCReadByKey(expectedKey); rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); String actualValues = rpcActualResult.get("value").asText(); - String expected = "LwM2mSingleResource [id=" + resourceId_14 + ", value=" + expectedValue + ", type=STRING]"; + String expected = "LwM2mSingleResource [id=" + RESOURCE_ID_14 + ", value=" + expectedValue + ", type=STRING]"; assertTrue(actualValues.contains(expected)); } @@ -85,7 +84,7 @@ public class RpcLwm2mIntegrationWriteTest extends AbstractRpcLwM2MIntegrationTes */ @Test public void testWriteReplaceValueMultipleResource_Result_CHANGED_Value_Multi_Instance_Resource_must_in_Json_format() throws Exception { - String expectedPath = objectIdVer_19 + "/" + objectInstanceId_0 + "/" + resourceId_0; + String expectedPath = objectIdVer_19 + "/" + OBJECT_INSTANCE_ID_0 + "/" + RESOURCE_ID_0; int resourceInstanceId0 = 0; int resourceInstanceId15 = 15; String expectedValue0 = "0000ad45675600"; @@ -115,7 +114,7 @@ public class RpcLwm2mIntegrationWriteTest extends AbstractRpcLwM2MIntegrationTes */ @Test public void testWriteReplaceValueSingleResourceR_ById_Result_CHANGED() throws Exception { - String expectedPath = objectInstanceIdVer_3 + "/" + resourceId_9; + String expectedPath = objectInstanceIdVer_3 + "/" + RESOURCE_ID_9; Integer expectedValue = 90; String actualResult = sendRPCWriteObjectById("WriteReplace", expectedPath, expectedValue); ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); @@ -132,21 +131,21 @@ public class RpcLwm2mIntegrationWriteTest extends AbstractRpcLwM2MIntegrationTes String expectedPath = objectInstanceIdVer_3; String expectedValue14 = "+5"; String expectedValue15 = "Kiyv/Europe"; - String expectedValue = "{\"" + resourceId_14 + "\":\"" + expectedValue14 + "\",\"" + resourceId_15 + "\":\"" + expectedValue15 + "\"}"; + String expectedValue = "{\"" + RESOURCE_ID_14 + "\":\"" + expectedValue14 + "\",\"" + RESOURCE_ID_15 + "\":\"" + expectedValue15 + "\"}"; String actualResult = sendRPCWriteObjectById("WriteUpdate", expectedPath, expectedValue); ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); assertEquals(ResponseCode.CHANGED.getName(), rpcActualResult.get("result").asText()); - String expectedPath14 = objectInstanceIdVer_3 + "/" + resourceId_14; - String expectedPath15 = objectInstanceIdVer_3 + "/" + resourceId_15; + String expectedPath14 = objectInstanceIdVer_3 + "/" + RESOURCE_ID_14; + String expectedPath15 = objectInstanceIdVer_3 + "/" + RESOURCE_ID_15; actualResult = sendRPCReadById(expectedPath14); rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); String actualValues = rpcActualResult.get("value").asText(); - String expected = "LwM2mSingleResource [id=" + resourceId_14 + ", value=" + expectedValue14 + ", type=STRING]"; + String expected = "LwM2mSingleResource [id=" + RESOURCE_ID_14 + ", value=" + expectedValue14 + ", type=STRING]"; assertTrue(actualValues.contains(expected)); actualResult = sendRPCReadById(expectedPath15); rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); actualValues = rpcActualResult.get("value").asText(); - expected = "LwM2mSingleResource [id=" + resourceId_15 + ", value=" + expectedValue15 + ", type=STRING]"; + expected = "LwM2mSingleResource [id=" + RESOURCE_ID_15 + ", value=" + expectedValue15 + ", type=STRING]"; assertTrue(actualValues.contains(expected)); } @@ -157,17 +156,17 @@ public class RpcLwm2mIntegrationWriteTest extends AbstractRpcLwM2MIntegrationTes */ @Test public void testWriteUpdateValueMultipleResourceById_Result_CHANGED() throws Exception { - String expectedPath = objectIdVer_19 + "/" + objectInstanceId_0; + String expectedPath = objectIdVer_19 + "/" + OBJECT_INSTANCE_ID_0; int resourceInstanceId0 = 0; int resourceInstanceId25 = 25; String expectedValue0 = "00ad45675600"; String expectedValue25 = "25ad45675600cdef"; - String expectedValue = "{\"" + resourceId_0 + "\":{\"" + resourceInstanceId0 + "\":\"" + expectedValue0 + "\", \"" + resourceInstanceId25 + "\":\"" + expectedValue25 + "\"}}"; + String expectedValue = "{\"" + RESOURCE_ID_0 + "\":{\"" + resourceInstanceId0 + "\":\"" + expectedValue0 + "\", \"" + resourceInstanceId25 + "\":\"" + expectedValue25 + "\"}}"; String actualResult = sendRPCWriteObjectById("WriteUpdate", expectedPath, expectedValue); ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); assertEquals(ResponseCode.CHANGED.getName(), rpcActualResult.get("result").asText()); - String expectedPath0 = expectedPath + "/" + resourceId_0 + "/" + resourceInstanceId0; - String expectedPath25 =expectedPath + "/" + resourceId_0 + "/" + resourceInstanceId25; + String expectedPath0 = expectedPath + "/" + RESOURCE_ID_0 + "/" + resourceInstanceId0; + String expectedPath25 =expectedPath + "/" + RESOURCE_ID_0 + "/" + resourceInstanceId25; actualResult = sendRPCReadById(expectedPath0); rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); String actualValues = rpcActualResult.get("value").asText(); @@ -188,11 +187,11 @@ public class RpcLwm2mIntegrationWriteTest extends AbstractRpcLwM2MIntegrationTes @Test public void testWriteCompositeValueSingleResourceResourceInstanceByIdKey_Result_CHANGED() throws Exception { int resourceInstanceId2 = 2; - String expectedPath19_1_0_2 = objectIdVer_19 + "/" + objectInstanceId_1 + "/" + resourceId_0 + "/" + resourceInstanceId2; + String expectedPath19_1_0_2 = objectIdVer_19 + "/" + OBJECT_INSTANCE_ID_1 + "/" + RESOURCE_ID_0 + "/" + resourceInstanceId2; String expectedValue19_1_0_2 = "00001234"; - String expectedKey3_0_14 = resourceIdName_3_14; + String expectedKey3_0_14 = RESOURCE_ID_NAME_3_14; String expectedValue3_0_14 = "+04"; - String expectedPath3_0_15 = objectInstanceIdVer_3 + "/" + resourceId_15; + String expectedPath3_0_15 = objectInstanceIdVer_3 + "/" + RESOURCE_ID_15; String expectedValue3_0_15 = "Kiyv/Europe"; String nodes = "{\"" + expectedPath19_1_0_2 + "\":\"" + expectedValue19_1_0_2 + "\", \"" + expectedKey3_0_14 + "\":\"" + expectedValue3_0_14 + "\", \"" + expectedPath3_0_15 + "\":\"" + expectedValue3_0_15 + "\"}"; @@ -207,12 +206,12 @@ public class RpcLwm2mIntegrationWriteTest extends AbstractRpcLwM2MIntegrationTes actualResult = sendRPCReadByKey(expectedKey3_0_14); rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); actualValues = rpcActualResult.get("value").asText(); - expected = "LwM2mSingleResource [id=" + resourceId_14 + ", value=" + expectedValue3_0_14 + ", type=STRING]"; + expected = "LwM2mSingleResource [id=" + RESOURCE_ID_14 + ", value=" + expectedValue3_0_14 + ", type=STRING]"; assertTrue(actualValues.contains(expected)); actualResult = sendRPCReadById(expectedPath3_0_15); rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); actualValues = rpcActualResult.get("value").asText(); - expected = "LwM2mSingleResource [id=" + resourceId_15 + ", value=" + expectedValue3_0_15 + ", type=STRING]"; + expected = "LwM2mSingleResource [id=" + RESOURCE_ID_15 + ", value=" + expectedValue3_0_15 + ", type=STRING]"; assertTrue(actualValues.contains(expected)); } @@ -246,11 +245,11 @@ public class RpcLwm2mIntegrationWriteTest extends AbstractRpcLwM2MIntegrationTes */ @Test public void testWriteCompositeCreateResourceInstanceUpdateSingleResourceByIdKey_Result_CHANGED() throws Exception { - String expectedPath19_1_0_2 = objectIdVer_19 + "/" + objectInstanceId_1 + "/" + resourceId_0 + "/" + resourceInstanceId_2; + String expectedPath19_1_0_2 = objectIdVer_19 + "/" + OBJECT_INSTANCE_ID_1 + "/" + RESOURCE_ID_0 + "/" + RESOURCE_INSTANCE_ID_2; String expectedValue19_1_0_2 = "00001234"; - String expectedKey3_0_14 = resourceIdName_3_14; + String expectedKey3_0_14 = RESOURCE_ID_NAME_3_14; String expectedValue3_0_14 = "+04"; - String expectedPath3_0_15 = objectInstanceIdVer_3 + "/" + resourceId_15; + String expectedPath3_0_15 = objectInstanceIdVer_3 + "/" + RESOURCE_ID_15; String expectedValue3_0_15 = "Kiyv/Europe"; String nodes = "{\"" + expectedPath19_1_0_2 + "\":\"" + expectedValue19_1_0_2 + "\", \"" + expectedKey3_0_14 + "\":\"" + expectedValue3_0_14 + "\", \"" + expectedPath3_0_15 + "\":\"" + expectedValue3_0_15 + "\"}"; @@ -260,17 +259,17 @@ public class RpcLwm2mIntegrationWriteTest extends AbstractRpcLwM2MIntegrationTes actualResult = sendRPCReadById(expectedPath19_1_0_2); rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); String actualValues = rpcActualResult.get("value").asText(); - String expected = "LwM2mResourceInstance [id=" + resourceInstanceId_2 + ", value=" + expectedValue19_1_0_2.length()/2 + "Bytes, type=OPAQUE]"; + String expected = "LwM2mResourceInstance [id=" + RESOURCE_INSTANCE_ID_2 + ", value=" + expectedValue19_1_0_2.length()/2 + "Bytes, type=OPAQUE]"; assertTrue(actualValues.contains(expected)); actualResult = sendRPCReadByKey(expectedKey3_0_14); rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); actualValues = rpcActualResult.get("value").asText(); - expected = "LwM2mSingleResource [id=" + resourceId_14 + ", value=" + expectedValue3_0_14 + ", type=STRING]"; + expected = "LwM2mSingleResource [id=" + RESOURCE_ID_14 + ", value=" + expectedValue3_0_14 + ", type=STRING]"; assertTrue(actualValues.contains(expected)); actualResult = sendRPCReadById(expectedPath3_0_15); rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); actualValues = rpcActualResult.get("value").asText(); - expected = "LwM2mSingleResource [id=" + resourceId_15 + ", value=" + expectedValue3_0_15 + ", type=STRING]"; + expected = "LwM2mSingleResource [id=" + RESOURCE_ID_15 + ", value=" + expectedValue3_0_15 + ", type=STRING]"; assertTrue(actualValues.contains(expected)); } @@ -285,11 +284,11 @@ public class RpcLwm2mIntegrationWriteTest extends AbstractRpcLwM2MIntegrationTes */ @Test public void testWriteCompositeCreateObjectInstanceUpdateSingleResourceByIdKey_Result_BAD_REQUEST() throws Exception { - String expectedPath19_1_2_2 = objectIdVer_19 + "/" + objectInstanceId_2 + "/" + resourceId_0 + "/" + resourceInstanceId_2; + String expectedPath19_1_2_2 = objectIdVer_19 + "/" + OBJECT_INSTANCE_ID_2 + "/" + RESOURCE_ID_0 + "/" + RESOURCE_INSTANCE_ID_2; String expectedValue19_1_0_2 = "00001234"; - String expectedKey3_0_14 = resourceIdName_3_14; + String expectedKey3_0_14 = RESOURCE_ID_NAME_3_14; String expectedValue3_0_14 = "+04"; - String expectedPath3_0_15 = objectInstanceIdVer_3 + "/" + resourceId_15; + String expectedPath3_0_15 = objectInstanceIdVer_3 + "/" + RESOURCE_ID_15; String expectedValue3_0_15 = "Kiyv/Europe"; String nodes = "{\"" + expectedPath19_1_2_2 + "\":\"" + expectedValue19_1_0_2 + "\", \"" + expectedKey3_0_14 + "\":\"" + expectedValue3_0_14 + "\", \"" + expectedPath3_0_15 + "\":\"" + expectedValue3_0_15 + "\"}"; diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/AbstractSecurityLwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/AbstractSecurityLwM2MIntegrationTest.java index c9d2b556af..9108366395 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/AbstractSecurityLwM2MIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/AbstractSecurityLwM2MIntegrationTest.java @@ -62,7 +62,7 @@ protected final X509Certificate serverX509Cert; protected final X509Certificate clientX509CertTrustNo; // client certificate signed by intermediate, rootCA with a good CN ("host name") protected final PrivateKey clientPrivateKeyFromCertTrustNo; // client private key used for X509 and RPK protected final PublicKey clientPublicKeyFromCertTrustNo; // client public key used for RPK - private final String[] resources = new String[]{"1.xml", "2.xml", "3.xml", "5.xml", "9.xml"}; + private final String[] RESOURCES_SECURITY = new String[]{"1.xml", "2.xml", "3.xml", "5.xml", "9.xml"}; private final LwM2MBootstrapClientCredentials defaultBootstrapCredentials; @@ -71,7 +71,7 @@ protected final X509Certificate serverX509Cert; public AbstractSecurityLwM2MIntegrationTest() { // create client credentials - setResources(this.resources); + setResources(this.RESOURCES_SECURITY); try { // Get certificates from key store char[] clientKeyStorePwd = CLIENT_STORE_PWD.toCharArray(); From 1b488781d5690093e968fec4f6c6adb5f852af00 Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Tue, 11 Jan 2022 19:07:33 +0200 Subject: [PATCH 091/798] lwm2m for profile bootstrap need input: X509 certificate (instead of X509 public key) --- .../server/service/lwm2m/LwM2MServiceImpl.java | 18 ++++++++++++++++++ .../bootstrap/LwM2MServerSecurityConfig.java | 14 ++++++++++---- .../dao/device/DeviceProfileServiceImpl.java | 6 +++--- 3 files changed, 31 insertions(+), 7 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/lwm2m/LwM2MServiceImpl.java b/application/src/main/java/org/thingsboard/server/service/lwm2m/LwM2MServiceImpl.java index b5ec5cc654..7d0c623f96 100644 --- a/application/src/main/java/org/thingsboard/server/service/lwm2m/LwM2MServiceImpl.java +++ b/application/src/main/java/org/thingsboard/server/service/lwm2m/LwM2MServiceImpl.java @@ -63,6 +63,12 @@ public class LwM2MServiceImpl implements LwM2MService { } else { bsServ.setServerPublicKey(Base64.encodeBase64String(publicKeyBase64)); } + byte[] certificateBase64 = getCertificate(bsServerConfig); + if (certificateBase64 == null) { + bsServ.setServerCertificate(""); + } else { + bsServ.setServerCertificate(Base64.encodeBase64String(certificateBase64)); + } return bsServ; } @@ -77,5 +83,17 @@ public class LwM2MServiceImpl implements LwM2MService { } return null; } + + private byte[] getCertificate(LwM2MSecureServerConfig config) { + try { + SslCredentials sslCredentials = config.getSslCredentials(); + if (sslCredentials != null) { + return sslCredentials.getCertificateChain()[0].getEncoded(); + } + } catch (Exception e) { + log.trace("Failed to fetch certificate from key store!", e); + } + return null; + } } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/lwm2m/bootstrap/LwM2MServerSecurityConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/lwm2m/bootstrap/LwM2MServerSecurityConfig.java index 12dd9c5a64..11bf049621 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/lwm2m/bootstrap/LwM2MServerSecurityConfig.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/lwm2m/bootstrap/LwM2MServerSecurityConfig.java @@ -42,17 +42,23 @@ public class LwM2MServerSecurityConfig { @ApiModelProperty(position = 8, value = "Server Public Key for 'Security' mode (DTLS): RPK or X509. Format: base64 encoded", example = "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEAZ0pSaGKHk/GrDaUDnQZpeEdGwX7m3Ws+U/kiVat\n" + "+44sgk3c8g0LotfMpLlZJPhPwJ6ipXV+O1r7IZUjBs3LNA==", readOnly = true) protected String serverPublicKey; - @ApiModelProperty(position = 9, value = "Bootstrap Server Account Timeout (If the value is set to 0, or if this resource is not instantiated, the Bootstrap-Server Account lifetime is infinite.)", example = "0", readOnly = true) + @ApiModelProperty(position = 9, value = "Server Public Key for 'Security' mode (DTLS): X509. Format: base64 encoded", example = "MMIICODCCAd6gAwIBAgIUI88U1zowOdrxDK/dOV+36gJxI2MwCgYIKoZIzj0EAwIwejELMAkGA1UEBhMCVUs\n" + + "xEjAQBgNVBAgTCUt5aXYgY2l0eTENMAsGA1UEBxMES3lpdjEUMBIGA1UEChMLVGhpbmdzYm9hcmQxFzAVBgNVBAsMDkRFVkVMT1BFUl9URVNUMRkwFwYDVQQDDBBpbnRlcm1lZGlhdGVfY2EwMB4XDTIyMDEwOTEzMDMwMFoXDTI3MDEwODEzMDMwMFowFDESMBAGA1UEAxM\n" + + "JbG9jYWxob3N0MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEUO3vBo/JTv0eooY7XHiKAIVDoWKFqtrU7C6q8AIKqpLcqhCdW+haFeBOH3PjY6EwaWkY04Bir4oanU0s7tz2uKOBpzCBpDAOBgNVHQ8BAf8EBAMCBaAwEwYDVR0lBAwwCgYIKwYBBQUHAwEwDAYDVR0TAQH/\n" + + "BAIwADAdBgNVHQ4EFgQUEjc3Q4a0TxzP/3x3EV4fHxYUg0YwHwYDVR0jBBgwFoAUuSquGycMU6Q0SYNcbtSkSD3TfH0wLwYDVR0RBCgwJoIVbG9jYWxob3N0LmxvY2FsZG9tYWlugglsb2NhbGhvc3SCAiAtMAoGCCqGSM49BAMCA0gAMEUCIQD7dbZObyUaoDiNbX+9fUNp\n" + + "AWrD7N7XuJUwZ9FcN75R3gIgb2RNjDkHoyUyF1YajwkBk+7XmIXNClmizNJigj908mw=", readOnly = true) + protected String serverCertificate; + @ApiModelProperty(position = 10, value = "Bootstrap Server Account Timeout (If the value is set to 0, or if this resource is not instantiated, the Bootstrap-Server Account lifetime is infinite.)", example = "0", readOnly = true) Integer bootstrapServerAccountTimeout = 0; /** Config -> ObjectId = 1 'LwM2M Server' */ - @ApiModelProperty(position = 10, value = "Specify the lifetime of the registration in seconds.", example = "300", readOnly = true) + @ApiModelProperty(position = 11, value = "Specify the lifetime of the registration in seconds.", example = "300", readOnly = true) private Integer lifetime = 300; - @ApiModelProperty(position = 11, value = "The default value the LwM2M Client should use for the Minimum Period of an Observation in the absence of this parameter being included in an Observation. " + + @ApiModelProperty(position = 12, value = "The default value the LwM2M Client should use for the Minimum Period of an Observation in the absence of this parameter being included in an Observation. " + "If this Resource doesn’t exist, the default value is 0.", example = "1", readOnly = true) private Integer defaultMinPeriod = 1; /** ResourceID=6 'Notification Storing When Disabled or Offline' */ - @ApiModelProperty(position = 12, value = "If true, the LwM2M Client stores “Notify” operations to the LwM2M Server while the LwM2M Server account is disabled or the LwM2M Client is offline. After the LwM2M Server account is enabled or the LwM2M Client is online, the LwM2M Client reports the stored “Notify” operations to the Server. " + + @ApiModelProperty(position = 13, value = "If true, the LwM2M Client stores “Notify” operations to the LwM2M Server while the LwM2M Server account is disabled or the LwM2M Client is offline. After the LwM2M Server account is enabled or the LwM2M Client is online, the LwM2M Client reports the stored “Notify” operations to the Server. " + "If false, the LwM2M Client discards all the “Notify” operations or temporarily disables the Observe function while the LwM2M Server is disabled or the LwM2M Client is offline. " + "The default value is true.", example = "true", readOnly = true) private boolean notifIfDisabled = true; diff --git a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileServiceImpl.java index 18a8a1ecde..b685fc7aa8 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileServiceImpl.java @@ -765,15 +765,15 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D X509LwM2MBootstrapServerCredential x509ServerCredentials = (X509LwM2MBootstrapServerCredential) bootstrapServerConfig; server = x509ServerCredentials.isBootstrapServerIs() ? "Bootstrap Server" : "LwM2M Server"; if (StringUtils.isEmpty(x509ServerCredentials.getServerPublicKey())) { - throw new DeviceCredentialsValidationException(server + " X509 public key must be specified!"); + throw new DeviceCredentialsValidationException(server + " X509 certificate must be specified!"); } try { String certServer = EncryptionUtil.certTrimNewLines(x509ServerCredentials.getServerPublicKey()); x509ServerCredentials.setServerPublicKey(certServer); - SecurityUtil.publicKey.decode(x509ServerCredentials.getDecodedCServerPublicKey()); + SecurityUtil.certificate.decode(x509ServerCredentials.getDecodedCServerPublicKey()); } catch (Exception e) { - throw new DeviceCredentialsValidationException(server + " X509 public key must be in standard [RFC7250] and then encoded to Base64 format!"); + throw new DeviceCredentialsValidationException(server + " X509 certificate must be in DER-encoded X509v3 format and support only EC algorithm and then encoded to Base64 format!"); } break; } From e4579d21d788db8a0784a7dd9bf68383683cd216 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Wed, 12 Jan 2022 11:01:29 +0200 Subject: [PATCH 092/798] UI: Added persistent page link models and subscribe to router query params --- .../entity/entities-table.component.ts | 53 ++++++++++++------- .../src/app/shared/models/page/page-link.ts | 6 +++ 2 files changed, 39 insertions(+), 20 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/entity/entities-table.component.ts b/ui-ngx/src/app/modules/home/components/entity/entities-table.component.ts index 8fd2f8a976..11b0ea0488 100644 --- a/ui-ngx/src/app/modules/home/components/entity/entities-table.component.ts +++ b/ui-ngx/src/app/modules/home/components/entity/entities-table.component.ts @@ -31,12 +31,12 @@ import { import { PageComponent } from '@shared/components/page.component'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; -import { MAX_SAFE_PAGE_SIZE, PageLink, TimePageLink } from '@shared/models/page/page-link'; +import { MAX_SAFE_PAGE_SIZE, PageLink, PageQueryParam, TimePageLink } from '@shared/models/page/page-link'; import { MatDialog } from '@angular/material/dialog'; import { MatPaginator } from '@angular/material/paginator'; -import { MatSort } from '@angular/material/sort'; +import { MatSort, SortDirection } from '@angular/material/sort'; import { EntitiesDataSource } from '@home/models/datasource/entity-datasource'; -import { catchError, debounceTime, distinctUntilChanged, map, tap } from 'rxjs/operators'; +import { catchError, debounceTime, distinctUntilChanged, map, skip, tap } from 'rxjs/operators'; import { Direction, SortOrder } from '@shared/models/page/sort-order'; import { forkJoin, fromEvent, merge, Observable, of, Subscription } from 'rxjs'; import { TranslateService } from '@ngx-translate/core'; @@ -194,7 +194,7 @@ export class EntitiesTableComponent extends PageComponent implements IEntitiesTa this.columnsUpdated(); - const routerQueryParams = this.route.snapshot.queryParams; + const routerQueryParams: PageQueryParam = this.route.snapshot.queryParams; let sortOrder: SortOrder = null; if (this.entitiesTableConfig.defaultSortOrder || routerQueryParams.hasOwnProperty('direction') || routerQueryParams.hasOwnProperty('property')) { @@ -219,10 +219,10 @@ export class EntitiesTableComponent extends PageComponent implements IEntitiesTa } this.pageLink.pageSize = this.displayPagination ? this.defaultPageSize : MAX_SAFE_PAGE_SIZE; if (routerQueryParams.hasOwnProperty('page')) { - this.pageLink.page = routerQueryParams.page; + this.pageLink.page = Number(routerQueryParams.page); } if (routerQueryParams.hasOwnProperty('pageSize')) { - this.pageLink.pageSize = routerQueryParams.pageSize; + this.pageLink.pageSize = Number(routerQueryParams.pageSize); } if (routerQueryParams.hasOwnProperty('textSearch') && !isEmptyStr(routerQueryParams.textSearch)) { this.textSearchMode = true; @@ -249,19 +249,33 @@ export class EntitiesTableComponent extends PageComponent implements IEntitiesTa debounceTime(150), distinctUntilChanged(), tap(() => { - const queryParams: any = { + const queryParams: PageQueryParam = { textSearch: encodeURI(this.pageLink.textSearch) || null }; if (this.displayPagination) { this.paginator.pageIndex = 0; queryParams.page = null; } - this.updatedRouterQueryParams(queryParams); - this.updateData(); + this.updatedRouterParamsAndData(queryParams); }) ) .subscribe(); + this.route.queryParams.pipe(skip(1)).subscribe((params: PageQueryParam) => { + this.paginator.pageIndex = Number(params.page) || 0; + this.paginator.pageSize = Number(params.pageSize) || this.defaultPageSize; + this.sort.active = params.property || this.entitiesTableConfig.defaultSortOrder.property; + this.sort.direction = (params.direction || this.entitiesTableConfig.defaultSortOrder.direction).toLowerCase() as SortDirection; + if (params.hasOwnProperty('textSearch') && !isEmptyStr(params.textSearch)) { + this.textSearchMode = true; + this.pageLink.textSearch = decodeURI(params.textSearch); + } else { + this.textSearchMode = false; + this.pageLink.textSearch = null; + } + this.updateData(); + }); + this.updatePaginationSubscriptions(); this.viewInited = true; } @@ -275,8 +289,8 @@ export class EntitiesTableComponent extends PageComponent implements IEntitiesTa const sortSubscription$: Observable = this.sort.sortChange.asObservable().pipe( map((data) => { const direction = data.direction.toUpperCase(); - const queryParams: any = { - direction: this.entitiesTableConfig?.defaultSortOrder?.direction === direction ? null : direction, + const queryParams: PageQueryParam = { + direction: (this.entitiesTableConfig?.defaultSortOrder?.direction === direction ? null : direction) as Direction, property: this.entitiesTableConfig?.defaultSortOrder?.property === data.active ? null : data.active }; if (this.displayPagination) { @@ -297,10 +311,9 @@ export class EntitiesTableComponent extends PageComponent implements IEntitiesTa ); } this.updateDataSubscription = ((this.displayPagination ? merge(sortSubscription$, paginatorSubscription$) - : sortSubscription$) as Observable).pipe( + : sortSubscription$) as Observable).pipe( tap((queryParams) => { - this.updatedRouterQueryParams(queryParams); - this.updateData(); + this.updatedRouterParamsAndData(queryParams); }) ).subscribe(); } @@ -486,15 +499,14 @@ export class EntitiesTableComponent extends PageComponent implements IEntitiesTa exitFilterMode() { this.textSearchMode = false; this.pageLink.textSearch = null; - const queryParams: any = { + const queryParams: PageQueryParam = { textSearch: null }; if (this.displayPagination) { this.paginator.pageIndex = 0; queryParams.page = null; } - this.updatedRouterQueryParams(queryParams); - this.updateData(); + this.updatedRouterParamsAndData(queryParams); } resetSortAndFilter(update: boolean = true, preserveTimewindow: boolean = false) { @@ -508,9 +520,8 @@ export class EntitiesTableComponent extends PageComponent implements IEntitiesTa const sortable = this.sort.sortables.get(this.entitiesTableConfig.defaultSortOrder.property); this.sort.active = sortable.id; this.sort.direction = this.entitiesTableConfig.defaultSortOrder.direction === Direction.ASC ? 'asc' : 'desc'; - this.updatedRouterQueryParams({}, 'preserve'); if (update) { - this.updateData(); + this.updatedRouterParamsAndData({}, 'preserve'); } } @@ -628,13 +639,15 @@ export class EntitiesTableComponent extends PageComponent implements IEntitiesTa return entity.id.id; } - protected updatedRouterQueryParams(queryParams: object, queryParamsHandling: QueryParamsHandling = 'merge') { + protected updatedRouterParamsAndData(queryParams: object, queryParamsHandling: QueryParamsHandling = 'merge') { if (this.persistentPageLinkMode) { this.router.navigate([], { relativeTo: this.route, queryParams, queryParamsHandling }); + } else { + this.updateData(); } } } diff --git a/ui-ngx/src/app/shared/models/page/page-link.ts b/ui-ngx/src/app/shared/models/page/page-link.ts index 92fe4634f5..2528b6ce99 100644 --- a/ui-ngx/src/app/shared/models/page/page-link.ts +++ b/ui-ngx/src/app/shared/models/page/page-link.ts @@ -23,6 +23,12 @@ export const MAX_SAFE_PAGE_SIZE = 2147483647; export type PageLinkSearchFunction = (entity: T, textSearch: string, searchProperty?: string) => boolean; +export interface PageQueryParam extends Partial{ + textSearch?: string; + pageSize?: number; + page?: number; +} + export function defaultPageLinkSearchFunction(searchProperty?: string): PageLinkSearchFunction { return (entity, textSearch) => defaultPageLinkSearch(entity, textSearch, searchProperty); } From 08997d682c0c69392cd268be8633408d6a2b325f Mon Sep 17 00:00:00 2001 From: Viacheslav Klimov Date: Wed, 12 Jan 2022 12:23:43 +0200 Subject: [PATCH 093/798] Don't allow sysadmin to delete himself --- .../org/thingsboard/server/controller/UserController.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/application/src/main/java/org/thingsboard/server/controller/UserController.java b/application/src/main/java/org/thingsboard/server/controller/UserController.java index e83bc8970c..c7369fa5b9 100644 --- a/application/src/main/java/org/thingsboard/server/controller/UserController.java +++ b/application/src/main/java/org/thingsboard/server/controller/UserController.java @@ -302,6 +302,10 @@ public class UserController extends BaseController { UserId userId = new UserId(toUUID(strUserId)); User user = checkUserId(userId, Operation.DELETE); + if (user.getAuthority() == Authority.SYS_ADMIN && getCurrentUser().getId().equals(userId)) { + throw new ThingsboardException("Sysadmin is not allowed to delete himself", ThingsboardErrorCode.PERMISSION_DENIED); + } + List relatedEdgeIds = findRelatedEdgeIds(getTenantId(), userId); userService.deleteUser(getCurrentUser().getTenantId(), userId); From b84ab58d7b41f307d8d8a4cd99f160c5a73dc330 Mon Sep 17 00:00:00 2001 From: Sergey Tarnavskiy Date: Wed, 12 Jan 2022 12:56:02 +0200 Subject: [PATCH 094/798] LwM2M transport. Added serverCertificate value for X509-securityMode in Bootstrap-config. --- .../device/lwm2m/lwm2m-device-config-server.component.ts | 3 +++ .../profile/device/lwm2m/lwm2m-profile-config.models.ts | 1 + 2 files changed, 4 insertions(+) diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-config-server.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-config-server.component.ts index fa6d31153c..de75ea416b 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-config-server.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-config-server.component.ts @@ -112,6 +112,9 @@ export class Lwm2mDeviceConfigServerComponent implements OnInit, ControlValueAcc this.changeSecurityHostPortFields(serverSecurityConfig); } this.serverFormGroup.patchValue(serverSecurityConfig, {emitEvent: false}); + if (this.currentSecurityMode === Lwm2mSecurityType.X509) { + this.serverFormGroup.get('serverPublicKey').patchValue(serverSecurityConfig.serverCertificate, {emitEvent: false}); + } }); this.serverFormGroup.valueChanges.pipe( takeUntil(this.destroy$) diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-config.models.ts b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-config.models.ts index 56ae00c5a9..ce2824fb12 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-config.models.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-config.models.ts @@ -129,6 +129,7 @@ export interface ServerSecurityConfig { securityHost?: string; securityPort?: number; serverPublicKey?: string; + serverCertificate?: string; clientHoldOffTime?: number; shortServerId?: number; bootstrapServerAccountTimeout: number; From ab76d5a7a6c84053a93013c01237a13e84a0d5d0 Mon Sep 17 00:00:00 2001 From: YevhenBondarenko Date: Wed, 12 Jan 2022 13:59:13 +0200 Subject: [PATCH 095/798] fixed timescale agg by timezone --- .../dao/sqlts/timescale/AggregationRepository.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/dao/src/main/java/org/thingsboard/server/dao/sqlts/timescale/AggregationRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sqlts/timescale/AggregationRepository.java index 6559ea2c9e..8628d3e160 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sqlts/timescale/AggregationRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sqlts/timescale/AggregationRepository.java @@ -38,15 +38,15 @@ public class AggregationRepository { public static final String FROM_WHERE_CLAUSE = "FROM ts_kv tskv WHERE tskv.entity_id = cast(:entityId AS uuid) AND tskv.key= cast(:entityKey AS int) AND tskv.ts > :startTs AND tskv.ts <= :endTs GROUP BY tskv.entity_id, tskv.key, tsBucket ORDER BY tskv.entity_id, tskv.key, tsBucket"; - public static final String FIND_AVG_QUERY = "SELECT time_bucket(:timeBucket, tskv.ts) AS tsBucket, :timeBucket AS interval, SUM(COALESCE(tskv.long_v, 0)) AS longValue, SUM(COALESCE(tskv.dbl_v, 0.0)) AS doubleValue, SUM(CASE WHEN tskv.long_v IS NULL THEN 0 ELSE 1 END) AS longCountValue, SUM(CASE WHEN tskv.dbl_v IS NULL THEN 0 ELSE 1 END) AS doubleCountValue, null AS strValue, 'AVG' AS aggType "; + public static final String FIND_AVG_QUERY = "SELECT time_bucket(:timeBucket, tskv.ts, :startTs) AS tsBucket, :timeBucket AS interval, SUM(COALESCE(tskv.long_v, 0)) AS longValue, SUM(COALESCE(tskv.dbl_v, 0.0)) AS doubleValue, SUM(CASE WHEN tskv.long_v IS NULL THEN 0 ELSE 1 END) AS longCountValue, SUM(CASE WHEN tskv.dbl_v IS NULL THEN 0 ELSE 1 END) AS doubleCountValue, null AS strValue, 'AVG' AS aggType "; - public static final String FIND_MAX_QUERY = "SELECT time_bucket(:timeBucket, tskv.ts) AS tsBucket, :timeBucket AS interval, MAX(COALESCE(tskv.long_v, -9223372036854775807)) AS longValue, MAX(COALESCE(tskv.dbl_v, -1.79769E+308)) as doubleValue, SUM(CASE WHEN tskv.long_v IS NULL THEN 0 ELSE 1 END) AS longCountValue, SUM(CASE WHEN tskv.dbl_v IS NULL THEN 0 ELSE 1 END) AS doubleCountValue, MAX(tskv.str_v) AS strValue, 'MAX' AS aggType "; + public static final String FIND_MAX_QUERY = "SELECT time_bucket(:timeBucket, tskv.ts, :startTs) AS tsBucket, :timeBucket AS interval, MAX(COALESCE(tskv.long_v, -9223372036854775807)) AS longValue, MAX(COALESCE(tskv.dbl_v, -1.79769E+308)) as doubleValue, SUM(CASE WHEN tskv.long_v IS NULL THEN 0 ELSE 1 END) AS longCountValue, SUM(CASE WHEN tskv.dbl_v IS NULL THEN 0 ELSE 1 END) AS doubleCountValue, MAX(tskv.str_v) AS strValue, 'MAX' AS aggType "; - public static final String FIND_MIN_QUERY = "SELECT time_bucket(:timeBucket, tskv.ts) AS tsBucket, :timeBucket AS interval, MIN(COALESCE(tskv.long_v, 9223372036854775807)) AS longValue, MIN(COALESCE(tskv.dbl_v, 1.79769E+308)) as doubleValue, SUM(CASE WHEN tskv.long_v IS NULL THEN 0 ELSE 1 END) AS longCountValue, SUM(CASE WHEN tskv.dbl_v IS NULL THEN 0 ELSE 1 END) AS doubleCountValue, MIN(tskv.str_v) AS strValue, 'MIN' AS aggType "; + public static final String FIND_MIN_QUERY = "SELECT time_bucket(:timeBucket, tskv.ts, :startTs) AS tsBucket, :timeBucket AS interval, MIN(COALESCE(tskv.long_v, 9223372036854775807)) AS longValue, MIN(COALESCE(tskv.dbl_v, 1.79769E+308)) as doubleValue, SUM(CASE WHEN tskv.long_v IS NULL THEN 0 ELSE 1 END) AS longCountValue, SUM(CASE WHEN tskv.dbl_v IS NULL THEN 0 ELSE 1 END) AS doubleCountValue, MIN(tskv.str_v) AS strValue, 'MIN' AS aggType "; - public static final String FIND_SUM_QUERY = "SELECT time_bucket(:timeBucket, tskv.ts) AS tsBucket, :timeBucket AS interval, SUM(COALESCE(tskv.long_v, 0)) AS longValue, SUM(COALESCE(tskv.dbl_v, 0.0)) AS doubleValue, SUM(CASE WHEN tskv.long_v IS NULL THEN 0 ELSE 1 END) AS longCountValue, SUM(CASE WHEN tskv.dbl_v IS NULL THEN 0 ELSE 1 END) AS doubleCountValue, null AS strValue, null AS jsonValue, 'SUM' AS aggType "; + public static final String FIND_SUM_QUERY = "SELECT time_bucket(:timeBucket, tskv.ts, :startTs) AS tsBucket, :timeBucket AS interval, SUM(COALESCE(tskv.long_v, 0)) AS longValue, SUM(COALESCE(tskv.dbl_v, 0.0)) AS doubleValue, SUM(CASE WHEN tskv.long_v IS NULL THEN 0 ELSE 1 END) AS longCountValue, SUM(CASE WHEN tskv.dbl_v IS NULL THEN 0 ELSE 1 END) AS doubleCountValue, null AS strValue, null AS jsonValue, 'SUM' AS aggType "; - public static final String FIND_COUNT_QUERY = "SELECT time_bucket(:timeBucket, tskv.ts) AS tsBucket, :timeBucket AS interval, SUM(CASE WHEN tskv.bool_v IS NULL THEN 0 ELSE 1 END) AS booleanValueCount, SUM(CASE WHEN tskv.str_v IS NULL THEN 0 ELSE 1 END) AS strValueCount, SUM(CASE WHEN tskv.long_v IS NULL THEN 0 ELSE 1 END) AS longValueCount, SUM(CASE WHEN tskv.dbl_v IS NULL THEN 0 ELSE 1 END) AS doubleValueCount, SUM(CASE WHEN tskv.json_v IS NULL THEN 0 ELSE 1 END) AS jsonValueCount "; + public static final String FIND_COUNT_QUERY = "SELECT time_bucket(:timeBucket, tskv.ts, :startTs) AS tsBucket, :timeBucket AS interval, SUM(CASE WHEN tskv.bool_v IS NULL THEN 0 ELSE 1 END) AS booleanValueCount, SUM(CASE WHEN tskv.str_v IS NULL THEN 0 ELSE 1 END) AS strValueCount, SUM(CASE WHEN tskv.long_v IS NULL THEN 0 ELSE 1 END) AS longValueCount, SUM(CASE WHEN tskv.dbl_v IS NULL THEN 0 ELSE 1 END) AS doubleValueCount, SUM(CASE WHEN tskv.json_v IS NULL THEN 0 ELSE 1 END) AS jsonValueCount "; @PersistenceContext private EntityManager entityManager; From 6c166b90773092db02c702e24cd7739abddbec8e Mon Sep 17 00:00:00 2001 From: Andrew Shvayka Date: Wed, 12 Jan 2022 11:20:18 +0200 Subject: [PATCH 096/798] Restore alarm relations update script. Add Bootstrap upgrade script # Conflicts: # application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java --- .../install/SqlDatabaseUpgradeService.java | 40 +++++++++++++------ 1 file changed, 28 insertions(+), 12 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java b/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java index c98e46d86e..6f142bab4c 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java +++ b/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java @@ -1,12 +1,12 @@ /** * Copyright © 2016-2021 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 - * + *

+ * 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. @@ -475,14 +475,30 @@ public class SqlDatabaseUpgradeService implements DatabaseEntitiesUpgradeService log.info("Updating schema ..."); schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "3.3.2", SCHEMA_UPDATE_SQL); loadSql(schemaUpdateFile, conn); - log.info("Updating server`s public key from HexDec to Base64 in profile for LWM2M..."); - schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "3.3.2", "schema_update_lwm2m_bootstrap.sql"); - loadSql(schemaUpdateFile, conn); - conn.createStatement().execute("call update_profile_bootstrap();"); - log.info("Server`s public key from HexDec to Base64 in profile for LWM2M updated."); - log.info("Updating client`s public key and secret key from HexDec to Base64 for LWM2M..."); - conn.createStatement().execute("call update_device_credentials_to_base64_and_bootstrap();"); - log.info("Client`s public key and secret key from HexDec to Base64 for LWM2M updated."); + try { + conn.createStatement().execute("insert into entity_alarm(tenant_id, entity_id, created_time, type, customer_id, alarm_id)" + + " select tenant_id, originator_id, created_time, type, customer_id, id from alarm;"); + conn.createStatement().execute("insert into entity_alarm(tenant_id, entity_id, created_time, type, customer_id, alarm_id)" + + " select a.tenant_id, r.from_id, created_time, type, customer_id, id" + + " from alarm a inner join relation r on r.relation_type_group = 'ALARM' and r.relation_type = 'ANY' and a.id = r.to_id ON CONFLICT DO NOTHING;"); + conn.createStatement().execute("delete from relation r where r.relation_type_group = 'ALARM';"); + } catch (Exception e) { + log.error("Failed to update alarm relations!!!", e); + } + + log.info("Updating lwm2m device profiles ..."); + try { + schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "3.3.2", "schema_update_lwm2m_bootstrap.sql"); + loadSql(schemaUpdateFile, conn); + log.info("Updating server`s public key from HexDec to Base64 in profile for LWM2M..."); + conn.createStatement().execute("call update_profile_bootstrap();"); + log.info("Server`s public key from HexDec to Base64 in profile for LWM2M updated."); + log.info("Updating client`s public key and secret key from HexDec to Base64 for LWM2M..."); + conn.createStatement().execute("call update_device_credentials_to_base64_and_bootstrap();"); + log.info("Client`s public key and secret key from HexDec to Base64 for LWM2M updated."); + } catch (Exception e) { + log.error("Failed to update lwm2m profiles!!!", e); + } log.info("Updating schema settings..."); conn.createStatement().execute("UPDATE tb_schema_settings SET schema_version = 3003003;"); log.info("Schema updated."); From 69f060a48ce37cfc757ca93ca6bba4c975c1c9ad Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Wed, 12 Jan 2022 14:50:16 +0200 Subject: [PATCH 097/798] - license --- .../server/service/install/SqlDatabaseUpgradeService.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java b/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java index 6f142bab4c..4a6d64caea 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java +++ b/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java @@ -1,12 +1,12 @@ /** * Copyright © 2016-2021 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 - *

+ * + * 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. From 949d3065609ad712df8df7ea5af03abea746679b Mon Sep 17 00:00:00 2001 From: Volodymyr Babak Date: Wed, 12 Jan 2022 15:42:43 +0200 Subject: [PATCH 098/798] Added functionality to support 3.3.0 edge version rule chains --- .../service/edge/rpc/EdgeGrpcSession.java | 9 +- .../constructor/RuleChainMsgConstructor.java | 187 ++++++++- .../rpc/processor/RuleChainEdgeProcessor.java | 5 +- .../RuleChainMsgConstructorTest.java | 358 ++++++++++++++++++ common/edge-api/src/main/proto/edge.proto | 6 + 5 files changed, 543 insertions(+), 22 deletions(-) create mode 100644 application/src/test/java/org/thingsboard/server/service/edge/rpc/constructor/RuleChainMsgConstructorTest.java diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcSession.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcSession.java index 46b36ca99f..2fc8cdf841 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcSession.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcSession.java @@ -52,6 +52,7 @@ import org.thingsboard.server.gen.edge.v1.DownlinkMsg; import org.thingsboard.server.gen.edge.v1.DownlinkResponseMsg; import org.thingsboard.server.gen.edge.v1.EdgeConfiguration; import org.thingsboard.server.gen.edge.v1.EdgeUpdateMsg; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.gen.edge.v1.EntityDataProto; import org.thingsboard.server.gen.edge.v1.EntityViewsRequestMsg; import org.thingsboard.server.gen.edge.v1.RelationRequestMsg; @@ -73,14 +74,11 @@ import org.thingsboard.server.service.edge.rpc.fetch.GeneralEdgeEventFetcher; import java.io.Closeable; import java.util.ArrayList; import java.util.Collections; -import java.util.LinkedHashMap; import java.util.List; -import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.UUID; import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.ReentrantLock; import java.util.function.BiConsumer; @@ -109,6 +107,8 @@ public final class EdgeGrpcSession implements Closeable { private boolean connected; private boolean syncCompleted; + private EdgeVersion edgeVersion; + private ScheduledExecutorService sendDownlinkExecutorService; EdgeGrpcSession(EdgeContextComponent ctx, StreamObserver outputStream, BiConsumer sessionOpenListener, @@ -525,7 +525,7 @@ public final class EdgeGrpcSession implements Closeable { case RULE_CHAIN: return ctx.getRuleChainProcessor().processRuleChainToEdge(edge, edgeEvent, msgType, action); case RULE_CHAIN_METADATA: - return ctx.getRuleChainProcessor().processRuleChainMetadataToEdge(edgeEvent, msgType); + return ctx.getRuleChainProcessor().processRuleChainMetadataToEdge(edgeEvent, msgType, this.edgeVersion); case ALARM: return ctx.getAlarmProcessor().processAlarmToEdge(edge, edgeEvent, msgType, action); case USER: @@ -655,6 +655,7 @@ public final class EdgeGrpcSession implements Closeable { try { if (edge.getSecret().equals(request.getEdgeSecret())) { sessionOpenListener.accept(edge.getId(), this); + this.edgeVersion = request.getEdgeVersion(); return ConnectResponseMsg.newBuilder() .setResponseCode(ConnectResponseCode.ACCEPTED) .setErrorMsg("") diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/RuleChainMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/RuleChainMsgConstructor.java index 11b14a78dc..e564b921f2 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/RuleChainMsgConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/RuleChainMsgConstructor.java @@ -17,15 +17,18 @@ package org.thingsboard.server.service.edge.rpc.constructor; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.rule.engine.flow.TbRuleChainInputNodeConfiguration; import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.rule.NodeConnectionInfo; import org.thingsboard.server.common.data.rule.RuleChain; import org.thingsboard.server.common.data.rule.RuleChainConnectionInfo; import org.thingsboard.server.common.data.rule.RuleChainMetaData; import org.thingsboard.server.common.data.rule.RuleNode; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.gen.edge.v1.NodeConnectionInfoProto; import org.thingsboard.server.gen.edge.v1.RuleChainConnectionInfoProto; import org.thingsboard.server.gen.edge.v1.RuleChainMetadataUpdateMsg; @@ -36,6 +39,10 @@ import org.thingsboard.server.queue.util.TbCoreComponent; import java.util.ArrayList; import java.util.List; +import java.util.NavigableSet; +import java.util.TreeSet; +import java.util.UUID; +import java.util.stream.Collectors; @Component @Slf4j @@ -43,6 +50,8 @@ import java.util.List; public class RuleChainMsgConstructor { private static final ObjectMapper objectMapper = new ObjectMapper(); + private static final String RULE_CHAIN_INPUT_NODE = "org.thingsboard.rule.engine.flow.TbRuleChainInputNode"; + private static final String TB_RULE_CHAIN_OUTPUT_NODE = "org.thingsboard.rule.engine.flow.TbRuleChainOutputNode"; public RuleChainUpdateMsg constructRuleChainUpdatedMsg(RuleChainId edgeRootRuleChainId, UpdateMsgType msgType, RuleChain ruleChain) { RuleChainUpdateMsg.Builder builder = RuleChainUpdateMsg.newBuilder() @@ -60,18 +69,19 @@ public class RuleChainMsgConstructor { return builder.build(); } - public RuleChainMetadataUpdateMsg constructRuleChainMetadataUpdatedMsg(UpdateMsgType msgType, RuleChainMetaData ruleChainMetaData) { + public RuleChainMetadataUpdateMsg constructRuleChainMetadataUpdatedMsg(UpdateMsgType msgType, + RuleChainMetaData ruleChainMetaData, + EdgeVersion edgeVersion) { try { - RuleChainMetadataUpdateMsg.Builder builder = RuleChainMetadataUpdateMsg.newBuilder() - .setRuleChainIdMSB(ruleChainMetaData.getRuleChainId().getId().getMostSignificantBits()) - .setRuleChainIdLSB(ruleChainMetaData.getRuleChainId().getId().getLeastSignificantBits()) - .addAllNodes(constructNodes(ruleChainMetaData.getNodes())) - .addAllConnections(constructConnections(ruleChainMetaData.getConnections())) - .addAllRuleChainConnections(constructRuleChainConnections(ruleChainMetaData.getRuleChainConnections())); - if (ruleChainMetaData.getFirstNodeIndex() != null) { - builder.setFirstNodeIndex(ruleChainMetaData.getFirstNodeIndex()); - } else { - builder.setFirstNodeIndex(-1); + RuleChainMetadataUpdateMsg.Builder builder = RuleChainMetadataUpdateMsg.newBuilder(); + switch (edgeVersion) { + case V_3_3_0: + constructRuleChainMetadataUpdatedMsg_V_3_3_0(builder, ruleChainMetaData); + break; + case V_3_3_3: + default: + constructRuleChainMetadataUpdatedMsg_V_3_3_3(builder, ruleChainMetaData); + break; } builder.setMsgType(msgType); return builder.build(); @@ -81,6 +91,104 @@ public class RuleChainMsgConstructor { return null; } + private void constructRuleChainMetadataUpdatedMsg_V_3_3_3(RuleChainMetadataUpdateMsg.Builder builder, + RuleChainMetaData ruleChainMetaData) throws JsonProcessingException { + builder.setRuleChainIdMSB(ruleChainMetaData.getRuleChainId().getId().getMostSignificantBits()) + .setRuleChainIdLSB(ruleChainMetaData.getRuleChainId().getId().getLeastSignificantBits()) + .addAllNodes(constructNodes(ruleChainMetaData.getNodes())) + .addAllConnections(constructConnections(ruleChainMetaData.getConnections())) + .addAllRuleChainConnections(constructRuleChainConnections(ruleChainMetaData.getRuleChainConnections(), new TreeSet<>())); + if (ruleChainMetaData.getFirstNodeIndex() != null) { + builder.setFirstNodeIndex(ruleChainMetaData.getFirstNodeIndex()); + } else { + builder.setFirstNodeIndex(-1); + } + } + + private void constructRuleChainMetadataUpdatedMsg_V_3_3_0(RuleChainMetadataUpdateMsg.Builder builder, + RuleChainMetaData ruleChainMetaData) throws JsonProcessingException { + List supportedNodes = filterNodes_V_3_3_0(ruleChainMetaData.getNodes()); + NavigableSet removedNodeIndexes = getRemovedNodeIndexes(ruleChainMetaData.getNodes(), ruleChainMetaData.getConnections()); + List connections = filterConnections_V_3_3_0(ruleChainMetaData.getNodes(), ruleChainMetaData.getConnections(), removedNodeIndexes); + + List ruleChainConnections = new ArrayList<>(); + if (ruleChainMetaData.getRuleChainConnections() != null) { + ruleChainConnections.addAll(ruleChainMetaData.getRuleChainConnections()); + } + ruleChainConnections.addAll(addRuleChainConnections_V_3_3_0(ruleChainMetaData.getNodes(), ruleChainMetaData.getConnections())); + builder.setRuleChainIdMSB(ruleChainMetaData.getRuleChainId().getId().getMostSignificantBits()) + .setRuleChainIdLSB(ruleChainMetaData.getRuleChainId().getId().getLeastSignificantBits()) + .addAllNodes(constructNodes(supportedNodes)) + .addAllConnections(constructConnections(connections)) + .addAllRuleChainConnections(constructRuleChainConnections(ruleChainConnections, removedNodeIndexes)); + if (ruleChainMetaData.getFirstNodeIndex() != null) { + Integer firstNodeIndex = ruleChainMetaData.getFirstNodeIndex(); + // decrease index because of removed nodes + for (Integer removedIndex : removedNodeIndexes) { + if (firstNodeIndex > removedIndex) { + firstNodeIndex = firstNodeIndex - 1; + } + } + builder.setFirstNodeIndex(firstNodeIndex); + } else { + builder.setFirstNodeIndex(-1); + } + } + + private List filterConnections_V_3_3_0(List nodes, List connections, NavigableSet removedNodeIndexes) { + List result = new ArrayList<>(); + if (connections != null) { + result = connections.stream().filter(conn -> { + for (int i = 0; i < nodes.size(); i++) { + RuleNode node = nodes.get(i); + if (node.getType().equalsIgnoreCase(RULE_CHAIN_INPUT_NODE) + || node.getType().equalsIgnoreCase(TB_RULE_CHAIN_OUTPUT_NODE)) { + if (conn.getFromIndex() == i || conn.getToIndex() == i) { + return false; + } + } + } + return true; + }).map(conn -> { + NodeConnectionInfo newConn = new NodeConnectionInfo(); + newConn.setFromIndex(conn.getFromIndex()); + newConn.setToIndex(conn.getToIndex()); + newConn.setType(conn.getType()); + return newConn; + }).collect(Collectors.toList()); + } + + // decrease index because of removed nodes + for (Integer removedIndex : removedNodeIndexes) { + for (NodeConnectionInfo newConn : result) { + if (newConn.getToIndex() > removedIndex) { + newConn.setToIndex(newConn.getToIndex() - 1); + } + if (newConn.getFromIndex() > removedIndex) { + newConn.setFromIndex(newConn.getFromIndex() - 1); + } + } + } + + return result; + } + + private NavigableSet getRemovedNodeIndexes(List nodes, List connections) { + TreeSet removedIndexes = new TreeSet<>(); + for (NodeConnectionInfo connection : connections) { + for (int i = 0; i < nodes.size(); i++) { + RuleNode node = nodes.get(i); + if (node.getType().equalsIgnoreCase(RULE_CHAIN_INPUT_NODE) + || node.getType().equalsIgnoreCase(TB_RULE_CHAIN_OUTPUT_NODE)) { + if (connection.getFromIndex() == i || connection.getToIndex() == i) { + removedIndexes.add(i); + } + } + } + } + return removedIndexes.descendingSet(); + } + private List constructConnections(List connections) { List result = new ArrayList<>(); if (connections != null && !connections.isEmpty()) { @@ -99,6 +207,21 @@ public class RuleChainMsgConstructor { .build(); } + private List filterNodes_V_3_3_0(List nodes) { + List result = new ArrayList<>(); + for (RuleNode node : nodes) { + switch (node.getType()) { + case RULE_CHAIN_INPUT_NODE: + case TB_RULE_CHAIN_OUTPUT_NODE: + log.trace("Skipping not supported rule node {}", node); + break; + default: + result.add(node); + } + } + return result; + } + private List constructNodes(List nodes) throws JsonProcessingException { List result = new ArrayList<>(); if (nodes != null && !nodes.isEmpty()) { @@ -109,23 +232,55 @@ public class RuleChainMsgConstructor { return result; } - private List constructRuleChainConnections(List ruleChainConnections) throws JsonProcessingException { + private List addRuleChainConnections_V_3_3_0(List nodes, List connections) throws JsonProcessingException { + List result = new ArrayList<>(); + for (int i = 0; i < nodes.size(); i++) { + RuleNode node = nodes.get(i); + if (node.getType().equalsIgnoreCase(RULE_CHAIN_INPUT_NODE)) { + for (NodeConnectionInfo connection : connections) { + if (connection.getToIndex() == i) { + RuleChainConnectionInfo e = new RuleChainConnectionInfo(); + e.setFromIndex(connection.getFromIndex()); + TbRuleChainInputNodeConfiguration configuration = JacksonUtil.treeToValue(node.getConfiguration(), TbRuleChainInputNodeConfiguration.class); + e.setTargetRuleChainId(new RuleChainId(UUID.fromString(configuration.getRuleChainId()))); + e.setAdditionalInfo(node.getAdditionalInfo()); + e.setType(connection.getType()); + result.add(e); + } + } + } + } + return result; + } + + private List constructRuleChainConnections(List ruleChainConnections, NavigableSet removedNodeIndexes) throws JsonProcessingException { List result = new ArrayList<>(); if (ruleChainConnections != null && !ruleChainConnections.isEmpty()) { for (RuleChainConnectionInfo ruleChainConnectionInfo : ruleChainConnections) { - result.add(constructRuleChainConnection(ruleChainConnectionInfo)); + result.add(constructRuleChainConnection(ruleChainConnectionInfo, removedNodeIndexes)); } } return result; } - private RuleChainConnectionInfoProto constructRuleChainConnection(RuleChainConnectionInfo ruleChainConnectionInfo) throws JsonProcessingException { + private RuleChainConnectionInfoProto constructRuleChainConnection(RuleChainConnectionInfo ruleChainConnectionInfo, NavigableSet removedNodeIndexes) throws JsonProcessingException { + int fromIndex = ruleChainConnectionInfo.getFromIndex(); + // decrease index because of removed nodes + for (Integer removedIndex : removedNodeIndexes) { + if (fromIndex > removedIndex) { + fromIndex = fromIndex - 1; + } + } + ObjectNode additionalInfo = (ObjectNode) ruleChainConnectionInfo.getAdditionalInfo(); + if (additionalInfo.get("ruleChainNodeId") == null) { + additionalInfo.put("ruleChainNodeId", "rule-chain-node-UNDEFINED"); + } return RuleChainConnectionInfoProto.newBuilder() - .setFromIndex(ruleChainConnectionInfo.getFromIndex()) + .setFromIndex(fromIndex) .setTargetRuleChainIdMSB(ruleChainConnectionInfo.getTargetRuleChainId().getId().getMostSignificantBits()) .setTargetRuleChainIdLSB(ruleChainConnectionInfo.getTargetRuleChainId().getId().getLeastSignificantBits()) .setType(ruleChainConnectionInfo.getType()) - .setAdditionalInfo(objectMapper.writeValueAsString(ruleChainConnectionInfo.getAdditionalInfo())) + .setAdditionalInfo(objectMapper.writeValueAsString(additionalInfo)) .build(); } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/RuleChainEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/RuleChainEdgeProcessor.java index 5ead8c5623..eb32340e87 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/RuleChainEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/RuleChainEdgeProcessor.java @@ -25,6 +25,7 @@ import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.rule.RuleChain; import org.thingsboard.server.common.data.rule.RuleChainMetaData; import org.thingsboard.server.gen.edge.v1.DownlinkMsg; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.gen.edge.v1.RuleChainMetadataUpdateMsg; import org.thingsboard.server.gen.edge.v1.RuleChainUpdateMsg; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; @@ -63,14 +64,14 @@ public class RuleChainEdgeProcessor extends BaseEdgeProcessor { return downlinkMsg; } - public DownlinkMsg processRuleChainMetadataToEdge(EdgeEvent edgeEvent, UpdateMsgType msgType) { + public DownlinkMsg processRuleChainMetadataToEdge(EdgeEvent edgeEvent, UpdateMsgType msgType, EdgeVersion edgeVersion) { RuleChainId ruleChainId = new RuleChainId(edgeEvent.getEntityId()); RuleChain ruleChain = ruleChainService.findRuleChainById(edgeEvent.getTenantId(), ruleChainId); DownlinkMsg downlinkMsg = null; if (ruleChain != null) { RuleChainMetaData ruleChainMetaData = ruleChainService.loadRuleChainMetaData(edgeEvent.getTenantId(), ruleChainId); RuleChainMetadataUpdateMsg ruleChainMetadataUpdateMsg = - ruleChainMsgConstructor.constructRuleChainMetadataUpdatedMsg(msgType, ruleChainMetaData); + ruleChainMsgConstructor.constructRuleChainMetadataUpdatedMsg(msgType, ruleChainMetaData, edgeVersion); if (ruleChainMetadataUpdateMsg != null) { downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) diff --git a/application/src/test/java/org/thingsboard/server/service/edge/rpc/constructor/RuleChainMsgConstructorTest.java b/application/src/test/java/org/thingsboard/server/service/edge/rpc/constructor/RuleChainMsgConstructorTest.java new file mode 100644 index 0000000000..4e8a21a8fa --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/service/edge/rpc/constructor/RuleChainMsgConstructorTest.java @@ -0,0 +1,358 @@ +/** + * Copyright © 2016-2021 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.service.edge.rpc.constructor; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.extern.slf4j.Slf4j; +import org.jetbrains.annotations.NotNull; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.junit.MockitoJUnitRunner; +import org.thingsboard.server.common.data.id.RuleChainId; +import org.thingsboard.server.common.data.id.RuleNodeId; +import org.thingsboard.server.common.data.rule.NodeConnectionInfo; +import org.thingsboard.server.common.data.rule.RuleChainMetaData; +import org.thingsboard.server.common.data.rule.RuleNode; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; +import org.thingsboard.server.gen.edge.v1.RuleChainConnectionInfoProto; +import org.thingsboard.server.gen.edge.v1.RuleChainMetadataUpdateMsg; +import org.thingsboard.server.gen.edge.v1.UpdateMsgType; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +@Slf4j +@RunWith(MockitoJUnitRunner.class) +public class RuleChainMsgConstructorTest { + + private static final ObjectMapper mapper = new ObjectMapper(); + + @Test + public void testConstructRuleChainMetadataUpdatedMsg_V_3_3_3() throws JsonProcessingException { + RuleChainId ruleChainId = new RuleChainId(UUID.randomUUID()); + RuleChainMsgConstructor constructor = new RuleChainMsgConstructor(); + RuleChainMetaData ruleChainMetaData = createRuleChainMetaData(ruleChainId, 3, createRuleNodes(ruleChainId), createConnections()); + RuleChainMetadataUpdateMsg ruleChainMetadataUpdateMsg = + constructor.constructRuleChainMetadataUpdatedMsg(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, ruleChainMetaData, EdgeVersion.V_3_3_3); + + Assert.assertEquals("First rule node index incorrect!", 3, ruleChainMetadataUpdateMsg.getFirstNodeIndex()); + Assert.assertEquals("Nodes count incorrect!", 12, ruleChainMetadataUpdateMsg.getNodesCount()); + Assert.assertEquals("Connections count incorrect!", 13, ruleChainMetadataUpdateMsg.getConnectionsCount()); + Assert.assertEquals("Rule chain connections count incorrect!", 0, ruleChainMetadataUpdateMsg.getRuleChainConnectionsCount()); + + compareNodeConnectionInfoAndProto(createNodeConnectionInfo(3, 6, "Success"), ruleChainMetadataUpdateMsg.getConnections(0)); + compareNodeConnectionInfoAndProto(createNodeConnectionInfo(3, 10, "Success"), ruleChainMetadataUpdateMsg.getConnections(1)); + compareNodeConnectionInfoAndProto(createNodeConnectionInfo(3, 0, "Success"), ruleChainMetadataUpdateMsg.getConnections(2)); + compareNodeConnectionInfoAndProto(createNodeConnectionInfo(4, 11, "Success"), ruleChainMetadataUpdateMsg.getConnections(3)); + compareNodeConnectionInfoAndProto(createNodeConnectionInfo(5, 11, "Success"), ruleChainMetadataUpdateMsg.getConnections(4)); + compareNodeConnectionInfoAndProto(createNodeConnectionInfo(6, 11, "Attributes Updated"), ruleChainMetadataUpdateMsg.getConnections(5)); + compareNodeConnectionInfoAndProto(createNodeConnectionInfo(6, 7, "RPC Request from Device"), ruleChainMetadataUpdateMsg.getConnections(6)); + compareNodeConnectionInfoAndProto(createNodeConnectionInfo(6, 4, "Post telemetry"), ruleChainMetadataUpdateMsg.getConnections(7)); + compareNodeConnectionInfoAndProto(createNodeConnectionInfo(6, 5, "Post attributes"), ruleChainMetadataUpdateMsg.getConnections(8)); + compareNodeConnectionInfoAndProto(createNodeConnectionInfo(6, 8, "Other"), ruleChainMetadataUpdateMsg.getConnections(9)); + compareNodeConnectionInfoAndProto(createNodeConnectionInfo(6, 9, "RPC Request to Device"), ruleChainMetadataUpdateMsg.getConnections(10)); + compareNodeConnectionInfoAndProto(createNodeConnectionInfo(7, 11, "Success"), ruleChainMetadataUpdateMsg.getConnections(11)); + compareNodeConnectionInfoAndProto(createNodeConnectionInfo(10, 9, "RPC"), ruleChainMetadataUpdateMsg.getConnections(12)); + } + + @Test + public void testConstructRuleChainMetadataUpdatedMsg_V_3_3_0() throws JsonProcessingException { + RuleChainId ruleChainId = new RuleChainId(UUID.randomUUID()); + RuleChainMsgConstructor constructor = new RuleChainMsgConstructor(); + RuleChainMetaData ruleChainMetaData = createRuleChainMetaData(ruleChainId, 3, createRuleNodes(ruleChainId), createConnections()); + RuleChainMetadataUpdateMsg ruleChainMetadataUpdateMsg = + constructor.constructRuleChainMetadataUpdatedMsg(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, ruleChainMetaData, EdgeVersion.V_3_3_0); + + Assert.assertEquals("First rule node index incorrect!", 2, ruleChainMetadataUpdateMsg.getFirstNodeIndex()); + Assert.assertEquals("Nodes count incorrect!", 10, ruleChainMetadataUpdateMsg.getNodesCount()); + Assert.assertEquals("Connections count incorrect!", 10, ruleChainMetadataUpdateMsg.getConnectionsCount()); + Assert.assertEquals("Rule chain connections count incorrect!", 1, ruleChainMetadataUpdateMsg.getRuleChainConnectionsCount()); + + compareNodeConnectionInfoAndProto(createNodeConnectionInfo(2, 5, "Success"), ruleChainMetadataUpdateMsg.getConnections(0)); + compareNodeConnectionInfoAndProto(createNodeConnectionInfo(3, 9, "Success"), ruleChainMetadataUpdateMsg.getConnections(1)); + compareNodeConnectionInfoAndProto(createNodeConnectionInfo(4, 9, "Success"), ruleChainMetadataUpdateMsg.getConnections(2)); + compareNodeConnectionInfoAndProto(createNodeConnectionInfo(5, 9, "Attributes Updated"), ruleChainMetadataUpdateMsg.getConnections(3)); + compareNodeConnectionInfoAndProto(createNodeConnectionInfo(5, 6, "RPC Request from Device"), ruleChainMetadataUpdateMsg.getConnections(4)); + compareNodeConnectionInfoAndProto(createNodeConnectionInfo(5, 3, "Post telemetry"), ruleChainMetadataUpdateMsg.getConnections(5)); + compareNodeConnectionInfoAndProto(createNodeConnectionInfo(5, 4, "Post attributes"), ruleChainMetadataUpdateMsg.getConnections(6)); + compareNodeConnectionInfoAndProto(createNodeConnectionInfo(5, 7, "Other"), ruleChainMetadataUpdateMsg.getConnections(7)); + compareNodeConnectionInfoAndProto(createNodeConnectionInfo(5, 8, "RPC Request to Device"), ruleChainMetadataUpdateMsg.getConnections(8)); + compareNodeConnectionInfoAndProto(createNodeConnectionInfo(6, 9, "Success"), ruleChainMetadataUpdateMsg.getConnections(9)); + + RuleChainConnectionInfoProto ruleChainConnection = ruleChainMetadataUpdateMsg.getRuleChainConnections(0); + Assert.assertEquals("From index incorrect!", 2, ruleChainConnection.getFromIndex()); + Assert.assertEquals("Type index incorrect!", "Success", ruleChainConnection.getType()); + Assert.assertEquals("Additional info incorrect!", + "{\"description\":\"\",\"layoutX\":477,\"layoutY\":560,\"ruleChainNodeId\":\"rule-chain-node-UNDEFINED\"}", + ruleChainConnection.getAdditionalInfo()); + Assert.assertTrue("Target rule chain id MSB incorrect!", ruleChainConnection.getTargetRuleChainIdMSB() != 0); + Assert.assertTrue("Target rule chain id LSB incorrect!", ruleChainConnection.getTargetRuleChainIdLSB() != 0); + } + + @Test + public void testConstructRuleChainMetadataUpdatedMsg_V_3_3_0_inDifferentOrder() throws JsonProcessingException { + // same rule chain metadata, but different order of rule nodes + RuleChainId ruleChainId = new RuleChainId(UUID.randomUUID()); + RuleChainMsgConstructor constructor = new RuleChainMsgConstructor(); + RuleChainMetaData ruleChainMetaData1 = createRuleChainMetaData(ruleChainId, 8, createRuleNodesInDifferentOrder(ruleChainId), createConnectionsInDifferentOrder()); + RuleChainMetadataUpdateMsg ruleChainMetadataUpdateMsg = + constructor.constructRuleChainMetadataUpdatedMsg(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, ruleChainMetaData1, EdgeVersion.V_3_3_0); + + Assert.assertEquals("First rule node index incorrect!", 7, ruleChainMetadataUpdateMsg.getFirstNodeIndex()); + Assert.assertEquals("Nodes count incorrect!", 10, ruleChainMetadataUpdateMsg.getNodesCount()); + Assert.assertEquals("Connections count incorrect!", 10, ruleChainMetadataUpdateMsg.getConnectionsCount()); + Assert.assertEquals("Rule chain connections count incorrect!", 1, ruleChainMetadataUpdateMsg.getRuleChainConnectionsCount()); + + compareNodeConnectionInfoAndProto(createNodeConnectionInfo(3, 0, "Success"), ruleChainMetadataUpdateMsg.getConnections(0)); + compareNodeConnectionInfoAndProto(createNodeConnectionInfo(4, 0, "Attributes Updated"), ruleChainMetadataUpdateMsg.getConnections(1)); + compareNodeConnectionInfoAndProto(createNodeConnectionInfo(4, 3, "RPC Request from Device"), ruleChainMetadataUpdateMsg.getConnections(2)); + compareNodeConnectionInfoAndProto(createNodeConnectionInfo(4, 6, "Post telemetry"), ruleChainMetadataUpdateMsg.getConnections(3)); + compareNodeConnectionInfoAndProto(createNodeConnectionInfo(4, 5, "Post attributes"), ruleChainMetadataUpdateMsg.getConnections(4)); + compareNodeConnectionInfoAndProto(createNodeConnectionInfo(4, 2, "Other"), ruleChainMetadataUpdateMsg.getConnections(5)); + compareNodeConnectionInfoAndProto(createNodeConnectionInfo(4, 1, "RPC Request to Device"), ruleChainMetadataUpdateMsg.getConnections(6)); + compareNodeConnectionInfoAndProto(createNodeConnectionInfo(5, 0, "Success"), ruleChainMetadataUpdateMsg.getConnections(7)); + compareNodeConnectionInfoAndProto(createNodeConnectionInfo(6, 0, "Success"), ruleChainMetadataUpdateMsg.getConnections(8)); + compareNodeConnectionInfoAndProto(createNodeConnectionInfo(7, 4, "Success"), ruleChainMetadataUpdateMsg.getConnections(9)); + + RuleChainConnectionInfoProto ruleChainConnection = ruleChainMetadataUpdateMsg.getRuleChainConnections(0); + Assert.assertEquals("From index incorrect!", 7, ruleChainConnection.getFromIndex()); + Assert.assertEquals("Type index incorrect!", "Success", ruleChainConnection.getType()); + Assert.assertEquals("Additional info incorrect!", + "{\"description\":\"\",\"layoutX\":477,\"layoutY\":560,\"ruleChainNodeId\":\"rule-chain-node-UNDEFINED\"}", + ruleChainConnection.getAdditionalInfo()); + Assert.assertTrue("Target rule chain id MSB incorrect!", ruleChainConnection.getTargetRuleChainIdMSB() != 0); + Assert.assertTrue("Target rule chain id LSB incorrect!", ruleChainConnection.getTargetRuleChainIdLSB() != 0); + } + + private void compareNodeConnectionInfoAndProto(NodeConnectionInfo expected, org.thingsboard.server.gen.edge.v1.NodeConnectionInfoProto actual) { + Assert.assertEquals(expected.getFromIndex(), actual.getFromIndex()); + Assert.assertEquals(expected.getToIndex(), actual.getToIndex()); + Assert.assertEquals(expected.getType(), actual.getType()); + } + + private RuleChainMetaData createRuleChainMetaData(RuleChainId ruleChainId, Integer firstNodeIndex, List nodes, List connections) { + RuleChainMetaData ruleChainMetaData = new RuleChainMetaData(); + ruleChainMetaData.setRuleChainId(ruleChainId); + ruleChainMetaData.setFirstNodeIndex(firstNodeIndex); + ruleChainMetaData.setNodes(nodes); + ruleChainMetaData.setConnections(connections); + ruleChainMetaData.setRuleChainConnections(null); + return ruleChainMetaData; + } + + private List createConnections() { + List result = new ArrayList<>(); + result.add(createNodeConnectionInfo(3, 6, "Success")); + result.add(createNodeConnectionInfo(3, 10, "Success")); + result.add(createNodeConnectionInfo(3, 0, "Success")); + result.add(createNodeConnectionInfo(4, 11, "Success")); + result.add(createNodeConnectionInfo(5, 11, "Success")); + result.add(createNodeConnectionInfo(6, 11, "Attributes Updated")); + result.add(createNodeConnectionInfo(6, 7, "RPC Request from Device")); + result.add(createNodeConnectionInfo(6, 4, "Post telemetry")); + result.add(createNodeConnectionInfo(6, 5, "Post attributes")); + result.add(createNodeConnectionInfo(6, 8, "Other")); + result.add(createNodeConnectionInfo(6, 9, "RPC Request to Device")); + result.add(createNodeConnectionInfo(7, 11, "Success")); + result.add(createNodeConnectionInfo(10, 9, "RPC")); + return result; + } + + private NodeConnectionInfo createNodeConnectionInfo(int fromIndex, int toIndex, String type) { + NodeConnectionInfo result = new NodeConnectionInfo(); + result.setFromIndex(fromIndex); + result.setToIndex(toIndex); + result.setType(type); + return result; + } + + private List createRuleNodes(RuleChainId ruleChainId) throws JsonProcessingException { + List result = new ArrayList<>(); + result.add(getOutputNode(ruleChainId)); + result.add(getAcknowledgeNode(ruleChainId)); + result.add(getCheckpointNode(ruleChainId)); + result.add(getDeviceProfileNode(ruleChainId)); + result.add(getSaveTimeSeriesNode(ruleChainId)); + result.add(getSaveClientAttributesNode(ruleChainId)); + result.add(getMessageTypeSwitchNode(ruleChainId)); + result.add(getLogRpcFromDeviceNode(ruleChainId)); + result.add(getLogOtherNode(ruleChainId)); + result.add(getRpcCallRequestNode(ruleChainId)); + result.add(getPushToAnalyticsNode(ruleChainId)); + result.add(getPushToCloudNode(ruleChainId)); + return result; + } + + private RuleNode createRuleNode(RuleChainId ruleChainId, String type, String name, JsonNode configuration, JsonNode additionalInfo) { + RuleNode e = new RuleNode(); + e.setRuleChainId(ruleChainId); + e.setType(type); + e.setName(name); + e.setDebugMode(false); + e.setConfiguration(configuration); + e.setAdditionalInfo(additionalInfo); + e.setId(new RuleNodeId(UUID.randomUUID())); + return e; + } + + private List createConnectionsInDifferentOrder() { + List result = new ArrayList<>(); + result.add(createNodeConnectionInfo(0, 2, "RPC")); + result.add(createNodeConnectionInfo(4, 1, "Success")); + result.add(createNodeConnectionInfo(5, 1, "Attributes Updated")); + result.add(createNodeConnectionInfo(5, 4, "RPC Request from Device")); + result.add(createNodeConnectionInfo(5, 7, "Post telemetry")); + result.add(createNodeConnectionInfo(5, 6, "Post attributes")); + result.add(createNodeConnectionInfo(5, 3, "Other")); + result.add(createNodeConnectionInfo(5, 2, "RPC Request to Device")); + result.add(createNodeConnectionInfo(6, 1, "Success")); + result.add(createNodeConnectionInfo(7, 1, "Success")); + result.add(createNodeConnectionInfo(8, 11, "Success")); + result.add(createNodeConnectionInfo(8, 5, "Success")); + result.add(createNodeConnectionInfo(8, 0, "Success")); + return result; + } + + private List createRuleNodesInDifferentOrder(RuleChainId ruleChainId) throws JsonProcessingException { + List result = new ArrayList<>(); + result.add(getPushToAnalyticsNode(ruleChainId)); + result.add(getPushToCloudNode(ruleChainId)); + result.add(getRpcCallRequestNode(ruleChainId)); + result.add(getLogOtherNode(ruleChainId)); + result.add(getLogRpcFromDeviceNode(ruleChainId)); + result.add(getMessageTypeSwitchNode(ruleChainId)); + result.add(getSaveClientAttributesNode(ruleChainId)); + result.add(getSaveTimeSeriesNode(ruleChainId)); + result.add(getDeviceProfileNode(ruleChainId)); + result.add(getCheckpointNode(ruleChainId)); + result.add(getAcknowledgeNode(ruleChainId)); + result.add(getOutputNode(ruleChainId)); + return result; + } + + + @NotNull + private RuleNode getOutputNode(RuleChainId ruleChainId) throws JsonProcessingException { + return createRuleNode(ruleChainId, + "org.thingsboard.rule.engine.flow.TbRuleChainOutputNode", + "Output node", + mapper.readTree("{\"version\":0}"), + mapper.readTree("{\"description\":\"\",\"layoutX\":178,\"layoutY\":592}")); + } + + @NotNull + private RuleNode getCheckpointNode(RuleChainId ruleChainId) throws JsonProcessingException { + return createRuleNode(ruleChainId, + "org.thingsboard.rule.engine.flow.TbCheckpointNode", + "Checkpoint node", + mapper.readTree("{\"queueName\":\"HighPriority\"}"), + mapper.readTree("{\"description\":\"\",\"layoutX\":178,\"layoutY\":647}")); + } + + @NotNull + private RuleNode getSaveTimeSeriesNode(RuleChainId ruleChainId) throws JsonProcessingException { + return createRuleNode(ruleChainId, + "org.thingsboard.rule.engine.telemetry.TbMsgTimeseriesNode", + "Save Timeseries", + mapper.readTree("{\"defaultTTL\":0}"), + mapper.readTree("{\"layoutX\":823,\"layoutY\":157}")); + } + + @NotNull + private RuleNode getMessageTypeSwitchNode(RuleChainId ruleChainId) throws JsonProcessingException { + return createRuleNode(ruleChainId, + "org.thingsboard.rule.engine.filter.TbMsgTypeSwitchNode", + "Message Type Switch", + mapper.readTree("{\"version\":0}"), + mapper.readTree("{\"layoutX\":347,\"layoutY\":149}")); + } + + @NotNull + private RuleNode getLogOtherNode(RuleChainId ruleChainId) throws JsonProcessingException { + return createRuleNode(ruleChainId, + "org.thingsboard.rule.engine.action.TbLogNode", + "Log Other", + mapper.readTree("{\"jsScript\":\"return '\\\\nIncoming message:\\\\n' + JSON.stringify(msg) + '\\\\nIncoming metadata:\\\\n' + JSON.stringify(metadata);\"}"), + mapper.readTree("{\"layoutX\":824,\"layoutY\":378}")); + } + + @NotNull + private RuleNode getPushToCloudNode(RuleChainId ruleChainId) throws JsonProcessingException { + return createRuleNode(ruleChainId, + "org.thingsboard.rule.engine.edge.TbMsgPushToCloudNode", + "Push to cloud", + mapper.readTree("{\"scope\":\"SERVER_SCOPE\"}"), + mapper.readTree("{\"layoutX\":1129,\"layoutY\":52}")); + } + + @NotNull + private RuleNode getAcknowledgeNode(RuleChainId ruleChainId) throws JsonProcessingException { + return createRuleNode(ruleChainId, + "org.thingsboard.rule.engine.flow.TbAckNode", + "Acknowledge node", + mapper.readTree("{\"version\":0}"), + mapper.readTree("{\"description\":\"\",\"layoutX\":177,\"layoutY\":703}")); + } + + @NotNull + private RuleNode getDeviceProfileNode(RuleChainId ruleChainId) throws JsonProcessingException { + return createRuleNode(ruleChainId, + "org.thingsboard.rule.engine.profile.TbDeviceProfileNode", + "Device Profile Node", + mapper.readTree("{\"persistAlarmRulesState\":false,\"fetchAlarmRulesStateOnStart\":false}"), + mapper.readTree("{\"description\":\"Process incoming messages from devices with the alarm rules defined in the device profile. Dispatch all incoming messages with \\\"Success\\\" relation type.\",\"layoutX\":187,\"layoutY\":468}")); + } + + @NotNull + private RuleNode getSaveClientAttributesNode(RuleChainId ruleChainId) throws JsonProcessingException { + return createRuleNode(ruleChainId, + "org.thingsboard.rule.engine.telemetry.TbMsgAttributesNode", + "Save Client Attributes", + mapper.readTree("{\"scope\":\"CLIENT_SCOPE\"}"), + mapper.readTree("{\"layoutX\":824,\"layoutY\":52}")); + } + + @NotNull + private RuleNode getLogRpcFromDeviceNode(RuleChainId ruleChainId) throws JsonProcessingException { + return createRuleNode(ruleChainId, + "org.thingsboard.rule.engine.action.TbLogNode", + "Log RPC from Device", + mapper.readTree("{\"jsScript\":\"return '\\\\nIncoming message:\\\\n' + JSON.stringify(msg) + '\\\\nIncoming metadata:\\\\n' + JSON.stringify(metadata);\"}"), + mapper.readTree("{\"layoutX\":825,\"layoutY\":266}")); + } + + @NotNull + private RuleNode getRpcCallRequestNode(RuleChainId ruleChainId) throws JsonProcessingException { + return createRuleNode(ruleChainId, + "org.thingsboard.rule.engine.rpc.TbSendRPCRequestNode", + "RPC Call Request", + mapper.readTree("{\"timeoutInSeconds\":60}"), + mapper.readTree("{\"layoutX\":824,\"layoutY\":466}")); + } + + @NotNull + private RuleNode getPushToAnalyticsNode(RuleChainId ruleChainId) throws JsonProcessingException { + return createRuleNode(ruleChainId, + "org.thingsboard.rule.engine.flow.TbRuleChainInputNode", + "Push to Analytics", + mapper.readTree("{\"ruleChainId\":\"af588000-6c7c-11ec-bafd-c9a47a5c8d99\"}"), + mapper.readTree("{\"description\":\"\",\"layoutX\":477,\"layoutY\":560}")); + } +} \ No newline at end of file diff --git a/common/edge-api/src/main/proto/edge.proto b/common/edge-api/src/main/proto/edge.proto index 8586d9b920..e54b5d5765 100644 --- a/common/edge-api/src/main/proto/edge.proto +++ b/common/edge-api/src/main/proto/edge.proto @@ -30,6 +30,11 @@ service EdgeRpcService { } +enum EdgeVersion { + V_3_3_0 = 0; + V_3_3_3 = 1; +} + /** * Data Structures; */ @@ -61,6 +66,7 @@ message EdgeUpdateMsg { message ConnectRequestMsg { string edgeRoutingKey = 1; string edgeSecret = 2; + EdgeVersion edgeVersion = 3; } enum ConnectResponseCode { From f115f3ac00bb0826ce60a2d812af80cd59014e7b Mon Sep 17 00:00:00 2001 From: Volodymyr Babak Date: Wed, 12 Jan 2022 15:53:16 +0200 Subject: [PATCH 099/798] Added edge version to connect msg --- .../main/java/org/thingsboard/edge/rpc/EdgeGrpcClient.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/common/edge-api/src/main/java/org/thingsboard/edge/rpc/EdgeGrpcClient.java b/common/edge-api/src/main/java/org/thingsboard/edge/rpc/EdgeGrpcClient.java index ec6670f807..f1f36ab4a4 100644 --- a/common/edge-api/src/main/java/org/thingsboard/edge/rpc/EdgeGrpcClient.java +++ b/common/edge-api/src/main/java/org/thingsboard/edge/rpc/EdgeGrpcClient.java @@ -33,6 +33,7 @@ import org.thingsboard.server.gen.edge.v1.DownlinkMsg; import org.thingsboard.server.gen.edge.v1.DownlinkResponseMsg; import org.thingsboard.server.gen.edge.v1.EdgeConfiguration; import org.thingsboard.server.gen.edge.v1.EdgeRpcServiceGrpc; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.gen.edge.v1.RequestMsg; import org.thingsboard.server.gen.edge.v1.RequestMsgType; import org.thingsboard.server.gen.edge.v1.ResponseMsg; @@ -97,7 +98,11 @@ public class EdgeGrpcClient implements EdgeRpcClient { this.inputStream = stub.withCompression("gzip").handleMsgs(initOutputStream(edgeKey, onUplinkResponse, onEdgeUpdate, onDownlink, onError)); this.inputStream.onNext(RequestMsg.newBuilder() .setMsgType(RequestMsgType.CONNECT_RPC_MESSAGE) - .setConnectRequestMsg(ConnectRequestMsg.newBuilder().setEdgeRoutingKey(edgeKey).setEdgeSecret(edgeSecret).build()) + .setConnectRequestMsg(ConnectRequestMsg.newBuilder() + .setEdgeRoutingKey(edgeKey) + .setEdgeSecret(edgeSecret) + .setEdgeVersion(EdgeVersion.V_3_3_3) + .build()) .build()); } From 926642bf8a5eb32a61d412adb833c8fddbfe37e9 Mon Sep 17 00:00:00 2001 From: YevhenBondarenko Date: Wed, 12 Jan 2022 16:20:46 +0200 Subject: [PATCH 100/798] fixed column name in upgrade query --- .../server/service/install/SqlDatabaseUpgradeService.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java b/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java index 4a6d64caea..51741fad5b 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java +++ b/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java @@ -476,9 +476,9 @@ public class SqlDatabaseUpgradeService implements DatabaseEntitiesUpgradeService schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "3.3.2", SCHEMA_UPDATE_SQL); loadSql(schemaUpdateFile, conn); try { - conn.createStatement().execute("insert into entity_alarm(tenant_id, entity_id, created_time, type, customer_id, alarm_id)" + + conn.createStatement().execute("insert into entity_alarm(tenant_id, entity_id, created_time, alarm_type, customer_id, alarm_id)" + " select tenant_id, originator_id, created_time, type, customer_id, id from alarm;"); - conn.createStatement().execute("insert into entity_alarm(tenant_id, entity_id, created_time, type, customer_id, alarm_id)" + + conn.createStatement().execute("insert into entity_alarm(tenant_id, entity_id, created_time, alarm_type, customer_id, alarm_id)" + " select a.tenant_id, r.from_id, created_time, type, customer_id, id" + " from alarm a inner join relation r on r.relation_type_group = 'ALARM' and r.relation_type = 'ANY' and a.id = r.to_id ON CONFLICT DO NOTHING;"); conn.createStatement().execute("delete from relation r where r.relation_type_group = 'ALARM';"); From 7e71e909e39f669ea612dc9d4763fcd767c013a5 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Wed, 12 Jan 2022 17:31:30 +0200 Subject: [PATCH 101/798] UI: Rename variable persistentPageLinkMode --- .../app/modules/home/components/alarm/alarm-table-config.ts | 2 +- .../home/components/audit-log/audit-log-table-config.ts | 4 ++-- .../home/components/audit-log/audit-log-table.component.ts | 4 ++-- .../home/components/entity/entities-table.component.ts | 6 +++--- .../app/modules/home/components/event/event-table-config.ts | 2 +- .../home/models/entity/entities-table-config.models.ts | 2 +- .../home/models/entity/entity-table-component.models.ts | 2 +- 7 files changed, 11 insertions(+), 11 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/alarm/alarm-table-config.ts b/ui-ngx/src/app/modules/home/components/alarm/alarm-table-config.ts index 6c4e0a22e0..9a200b8a3d 100644 --- a/ui-ngx/src/app/modules/home/components/alarm/alarm-table-config.ts +++ b/ui-ngx/src/app/modules/home/components/alarm/alarm-table-config.ts @@ -60,7 +60,7 @@ export class AlarmTableConfig extends EntityTableConfig this.loadDataOnInit = false; this.tableTitle = ''; this.useTimePageLink = true; - this.persistentPageLinkMode = false; + this.pageMode = false; this.defaultTimewindowInterval = historyInterval(DAY * 30); this.detailsPanelEnabled = false; this.selectionEnabled = false; diff --git a/ui-ngx/src/app/modules/home/components/audit-log/audit-log-table-config.ts b/ui-ngx/src/app/modules/home/components/audit-log/audit-log-table-config.ts index a61d0c54ab..2a5d339230 100644 --- a/ui-ngx/src/app/modules/home/components/audit-log/audit-log-table-config.ts +++ b/ui-ngx/src/app/modules/home/components/audit-log/audit-log-table-config.ts @@ -53,12 +53,12 @@ export class AuditLogTableConfig extends EntityTableConfig>; @@ -206,7 +206,7 @@ export class EntitiesTableComponent extends PageComponent implements IEntitiesTa this.displayPagination = this.entitiesTableConfig.displayPagination; this.defaultPageSize = this.entitiesTableConfig.defaultPageSize; - this.persistentPageLinkMode = this.entitiesTableConfig.persistentPageLinkMode; + this.pageMode = this.entitiesTableConfig.pageMode; this.pageSizeOptions = [this.defaultPageSize, this.defaultPageSize * 2, this.defaultPageSize * 3]; if (this.entitiesTableConfig.useTimePageLink) { @@ -640,7 +640,7 @@ export class EntitiesTableComponent extends PageComponent implements IEntitiesTa } protected updatedRouterParamsAndData(queryParams: object, queryParamsHandling: QueryParamsHandling = 'merge') { - if (this.persistentPageLinkMode) { + if (this.pageMode) { this.router.navigate([], { relativeTo: this.route, queryParams, diff --git a/ui-ngx/src/app/modules/home/components/event/event-table-config.ts b/ui-ngx/src/app/modules/home/components/event/event-table-config.ts index d6eedb7ff6..a10200060a 100644 --- a/ui-ngx/src/app/modules/home/components/event/event-table-config.ts +++ b/ui-ngx/src/app/modules/home/components/event/event-table-config.ts @@ -92,7 +92,7 @@ export class EventTableConfig extends EntityTableConfig { this.searchEnabled = false; this.addEnabled = false; this.entitiesDeleteEnabled = false; - this.persistentPageLinkMode = false; + this.pageMode = false; this.headerComponent = EventTableHeaderComponent; diff --git a/ui-ngx/src/app/modules/home/models/entity/entities-table-config.models.ts b/ui-ngx/src/app/modules/home/models/entity/entities-table-config.models.ts index d6ddf40735..d5866cbd74 100644 --- a/ui-ngx/src/app/modules/home/models/entity/entities-table-config.models.ts +++ b/ui-ngx/src/app/modules/home/models/entity/entities-table-config.models.ts @@ -160,7 +160,7 @@ export class EntityTableConfig, P extends PageLink = P addDialogStyle = {}; defaultSortOrder: SortOrder = {property: 'createdTime', direction: Direction.DESC}; displayPagination = true; - persistentPageLinkMode = true; + pageMode = true; defaultPageSize = 10; columns: Array> = []; cellActionDescriptors: Array> = []; diff --git a/ui-ngx/src/app/modules/home/models/entity/entity-table-component.models.ts b/ui-ngx/src/app/modules/home/models/entity/entity-table-component.models.ts index 4cf41f0fed..3c1a7428ab 100644 --- a/ui-ngx/src/app/modules/home/models/entity/entity-table-component.models.ts +++ b/ui-ngx/src/app/modules/home/models/entity/entity-table-component.models.ts @@ -50,7 +50,7 @@ export interface IEntitiesTableComponent { displayPagination: boolean; pageSizeOptions: number[]; pageLink: PageLink; - persistentPageLinkMode: boolean; + pageMode: boolean; textSearchMode: boolean; timewindow: Timewindow; dataSource: EntitiesDataSource>; From 104357b7b9afcdb6b7337a589e436465bcdc8bc4 Mon Sep 17 00:00:00 2001 From: Volodymyr Babak Date: Wed, 12 Jan 2022 17:42:12 +0200 Subject: [PATCH 102/798] Use widgettypedetails instead of widgettype in edge processor --- .../constructor/WidgetTypeMsgConstructor.java | 32 +++++++++++-------- .../processor/WidgetTypeEdgeProcessor.java | 8 ++--- common/edge-api/src/main/proto/edge.proto | 2 ++ 3 files changed, 25 insertions(+), 17 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/WidgetTypeMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/WidgetTypeMsgConstructor.java index ed911297f7..6f45f46327 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/WidgetTypeMsgConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/WidgetTypeMsgConstructor.java @@ -19,7 +19,7 @@ import org.springframework.stereotype.Component; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.WidgetTypeId; -import org.thingsboard.server.common.data.widget.WidgetType; +import org.thingsboard.server.common.data.widget.WidgetTypeDetails; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; import org.thingsboard.server.gen.edge.v1.WidgetTypeUpdateMsg; import org.thingsboard.server.queue.util.TbCoreComponent; @@ -28,26 +28,32 @@ import org.thingsboard.server.queue.util.TbCoreComponent; @TbCoreComponent public class WidgetTypeMsgConstructor { - public WidgetTypeUpdateMsg constructWidgetTypeUpdateMsg(UpdateMsgType msgType, WidgetType widgetType) { + public WidgetTypeUpdateMsg constructWidgetTypeUpdateMsg(UpdateMsgType msgType, WidgetTypeDetails widgetTypeDetails) { WidgetTypeUpdateMsg.Builder builder = WidgetTypeUpdateMsg.newBuilder() .setMsgType(msgType) - .setIdMSB(widgetType.getId().getId().getMostSignificantBits()) - .setIdLSB(widgetType.getId().getId().getLeastSignificantBits()); - if (widgetType.getBundleAlias() != null) { - builder.setBundleAlias(widgetType.getBundleAlias()); + .setIdMSB(widgetTypeDetails.getId().getId().getMostSignificantBits()) + .setIdLSB(widgetTypeDetails.getId().getId().getLeastSignificantBits()); + if (widgetTypeDetails.getBundleAlias() != null) { + builder.setBundleAlias(widgetTypeDetails.getBundleAlias()); } - if (widgetType.getAlias() != null) { - builder.setAlias(widgetType.getAlias()); + if (widgetTypeDetails.getAlias() != null) { + builder.setAlias(widgetTypeDetails.getAlias()); } - if (widgetType.getName() != null) { - builder.setName(widgetType.getName()); + if (widgetTypeDetails.getName() != null) { + builder.setName(widgetTypeDetails.getName()); } - if (widgetType.getDescriptor() != null) { - builder.setDescriptorJson(JacksonUtil.toString(widgetType.getDescriptor())); + if (widgetTypeDetails.getDescriptor() != null) { + builder.setDescriptorJson(JacksonUtil.toString(widgetTypeDetails.getDescriptor())); } - if (widgetType.getTenantId().equals(TenantId.SYS_TENANT_ID)) { + if (widgetTypeDetails.getTenantId().equals(TenantId.SYS_TENANT_ID)) { builder.setIsSystem(true); } + if (widgetTypeDetails.getImage() != null) { + builder.setImage(widgetTypeDetails.getImage()); + } + if (widgetTypeDetails.getDescription() != null) { + builder.setDescription(widgetTypeDetails.getDescription()); + } return builder.build(); } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/WidgetTypeEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/WidgetTypeEdgeProcessor.java index de4e0e9a06..6e481de21b 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/WidgetTypeEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/WidgetTypeEdgeProcessor.java @@ -21,7 +21,7 @@ import org.thingsboard.server.common.data.EdgeUtils; import org.thingsboard.server.common.data.edge.EdgeEvent; import org.thingsboard.server.common.data.edge.EdgeEventActionType; import org.thingsboard.server.common.data.id.WidgetTypeId; -import org.thingsboard.server.common.data.widget.WidgetType; +import org.thingsboard.server.common.data.widget.WidgetTypeDetails; import org.thingsboard.server.gen.edge.v1.DownlinkMsg; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; import org.thingsboard.server.gen.edge.v1.WidgetTypeUpdateMsg; @@ -38,10 +38,10 @@ public class WidgetTypeEdgeProcessor extends BaseEdgeProcessor { switch (edgeEdgeEventActionType) { case ADDED: case UPDATED: - WidgetType widgetType = widgetTypeService.findWidgetTypeById(edgeEvent.getTenantId(), widgetTypeId); - if (widgetType != null) { + WidgetTypeDetails widgetTypeDetails = widgetTypeService.findWidgetTypeDetailsById(edgeEvent.getTenantId(), widgetTypeId); + if (widgetTypeDetails != null) { WidgetTypeUpdateMsg widgetTypeUpdateMsg = - widgetTypeMsgConstructor.constructWidgetTypeUpdateMsg(msgType, widgetType); + widgetTypeMsgConstructor.constructWidgetTypeUpdateMsg(msgType, widgetTypeDetails); downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addWidgetTypeUpdateMsg(widgetTypeUpdateMsg) diff --git a/common/edge-api/src/main/proto/edge.proto b/common/edge-api/src/main/proto/edge.proto index e54b5d5765..3d4cdc7fe8 100644 --- a/common/edge-api/src/main/proto/edge.proto +++ b/common/edge-api/src/main/proto/edge.proto @@ -333,6 +333,8 @@ message WidgetTypeUpdateMsg { optional string name = 6; optional string descriptorJson = 7; bool isSystem = 8; + string image = 9; + string description = 10; } message AdminSettingsUpdateMsg { From 7820cb22de4c9cfb1d1b19b36945d69531428eb7 Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Wed, 12 Jan 2022 17:51:27 +0200 Subject: [PATCH 103/798] lwm2m: preparing for certificate validation in bootstrap mode --- .../secure/TbLwM2MCertificateVerifier.java | 91 +++++++++++++++++++ .../TbLwM2MDtlsCertificateVerifier.java | 54 +---------- 2 files changed, 93 insertions(+), 52 deletions(-) create mode 100644 common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/TbLwM2MCertificateVerifier.java diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/TbLwM2MCertificateVerifier.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/TbLwM2MCertificateVerifier.java new file mode 100644 index 0000000000..620d7243c4 --- /dev/null +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/TbLwM2MCertificateVerifier.java @@ -0,0 +1,91 @@ +/** + * Copyright © 2016-2021 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.transport.lwm2m.secure; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.StringUtils; +import org.thingsboard.server.queue.util.TbLwM2mTransportComponent; +import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig; +import org.thingsboard.server.transport.lwm2m.server.client.LwM2MAuthException; +import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mTypeServer; + +import java.security.InvalidAlgorithmParameterException; +import java.security.NoSuchAlgorithmException; +import java.security.cert.CertPath; +import java.security.cert.CertPathValidator; +import java.security.cert.CertPathValidatorException; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.security.cert.PKIXParameters; +import java.security.cert.TrustAnchor; +import java.security.cert.X509Certificate; +import java.util.Arrays; +import java.util.Collections; + +@Slf4j +@Component +@TbLwM2mTransportComponent +@RequiredArgsConstructor +public class TbLwM2MCertificateVerifier { + + private final LwM2MTransportServerConfig config; + private final LwM2mCredentialsSecurityInfoValidator securityInfoValidator; + + public TbLwM2MSecurityInfo verifyCertificate(X509Certificate cert, String sha3Hash, LwM2mTypeServer lwM2mTypeServer) { + TbLwM2MSecurityInfo securityInfo = null; + // verify if trust + if (config.getTrustSslCredentials() != null && config.getTrustSslCredentials().getTrustedCertificates().length > 0) { + if (verifyTrust(cert, config.getTrustSslCredentials().getTrustedCertificates()) != null) { + String endpoint = config.getTrustSslCredentials().getValueFromSubjectNameByKey(cert.getSubjectX500Principal().getName(), "CN"); + securityInfo = StringUtils.isNotEmpty(endpoint) ? securityInfoValidator.getEndpointSecurityInfoByCredentialsId(endpoint, lwM2mTypeServer) : null; + } + } + // if not trust or cert trust securityInfo == null + if (securityInfo == null) { + try { + securityInfo = securityInfoValidator.getEndpointSecurityInfoByCredentialsId(sha3Hash, lwM2mTypeServer); + } catch (LwM2MAuthException e) { + log.trace("Failed find security info: {}", sha3Hash, e); + } + } + return securityInfo; + } + + private X509Certificate verifyTrust(X509Certificate certificate, X509Certificate[] certificates) { + try { + CertificateFactory cf = CertificateFactory.getInstance("X.509"); + CertPath cp = cf.generateCertPath(Arrays.asList(new X509Certificate[]{certificate})); + for (int index = 0; index < certificates.length; ++index) { + X509Certificate caCert = certificates[index]; + try { + TrustAnchor trustAnchor = new TrustAnchor(caCert, null); + CertPathValidator cpv = CertPathValidator.getInstance("PKIX"); + PKIXParameters pkixParams = new PKIXParameters( + Collections.singleton(trustAnchor)); + pkixParams.setRevocationEnabled(false); + if (cpv.validate(cp, pkixParams) != null) return certificate; + } catch (NoSuchAlgorithmException | InvalidAlgorithmParameterException | CertPathValidatorException e) { + log.trace("[{}]. [{}]", certificate.getSubjectDN(), e.getMessage()); + } + } + } catch (CertificateException e) { + log.trace("[{}] certPath not valid. [{}]", certificate.getSubjectDN(), e.getMessage()); + } + return null; + } +} diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/TbLwM2MDtlsCertificateVerifier.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/TbLwM2MDtlsCertificateVerifier.java index babf385bc8..5703f5b366 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/TbLwM2MDtlsCertificateVerifier.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/TbLwM2MDtlsCertificateVerifier.java @@ -29,7 +29,6 @@ import org.eclipse.californium.scandium.dtls.HandshakeResultHandler; import org.eclipse.californium.scandium.dtls.x509.NewAdvancedCertificateVerifier; import org.eclipse.californium.scandium.dtls.x509.StaticCertificateVerifier; import org.eclipse.californium.scandium.util.ServerNames; -import org.eclipse.leshan.core.util.SecurityUtil; import org.eclipse.leshan.server.security.NonUniqueSecurityInfoException; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; @@ -44,28 +43,18 @@ import org.thingsboard.server.common.transport.util.SslUtil; import org.thingsboard.server.queue.util.TbLwM2mTransportComponent; import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig; import org.thingsboard.server.transport.lwm2m.secure.credentials.LwM2MClientCredentials; -import org.thingsboard.server.transport.lwm2m.server.client.LwM2MAuthException; import org.thingsboard.server.transport.lwm2m.server.store.TbLwM2MDtlsSessionStore; import org.thingsboard.server.transport.lwm2m.server.store.TbMainSecurityStore; import javax.annotation.PostConstruct; import javax.security.auth.x500.X500Principal; -import java.security.InvalidAlgorithmParameterException; -import java.security.NoSuchAlgorithmException; import java.security.PublicKey; import java.security.cert.CertPath; -import java.security.cert.CertPathValidator; -import java.security.cert.CertPathValidatorException; import java.security.cert.CertificateEncodingException; -import java.security.cert.CertificateException; import java.security.cert.CertificateExpiredException; -import java.security.cert.CertificateFactory; import java.security.cert.CertificateNotYetValidException; -import java.security.cert.PKIXParameters; -import java.security.cert.TrustAnchor; import java.security.cert.X509Certificate; import java.util.Arrays; -import java.util.Collections; import java.util.List; import static org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mTypeServer.CLIENT; @@ -80,6 +69,7 @@ public class TbLwM2MDtlsCertificateVerifier implements NewAdvancedCertificateVer private final LwM2MTransportServerConfig config; private final LwM2mCredentialsSecurityInfoValidator securityInfoValidator; private final TbMainSecurityStore securityStore; + private final TbLwM2MCertificateVerifier certificateVerifier; @SuppressWarnings("deprecation") private StaticCertificateVerifier staticCertificateVerifier; @@ -124,26 +114,9 @@ public class TbLwM2MDtlsCertificateVerifier implements NewAdvancedCertificateVer if (!skipValidityCheckForClientCert) { cert.checkValidity(); } - - - TbLwM2MSecurityInfo securityInfo = null; - // verify if trust - if (config.getTrustSslCredentials() != null && config.getTrustSslCredentials().getTrustedCertificates().length > 0) { - if (verifyTrust(cert, config.getTrustSslCredentials().getTrustedCertificates()) != null) { - String endpoint = config.getTrustSslCredentials().getValueFromSubjectNameByKey(cert.getSubjectX500Principal().getName(), "CN"); - securityInfo = StringUtils.isNotEmpty(endpoint) ? securityInfoValidator.getEndpointSecurityInfoByCredentialsId(endpoint, CLIENT) : null; - } - } - // if not trust or cert trust securityInfo == null String strCert = SslUtil.getCertificateString(cert); String sha3Hash = EncryptionUtil.getSha3Hash(strCert); - if (securityInfo == null) { - try { - securityInfo = securityInfoValidator.getEndpointSecurityInfoByCredentialsId(sha3Hash, CLIENT); - } catch (LwM2MAuthException e) { - log.trace("Failed find security info: {}", sha3Hash, e); - } - } + TbLwM2MSecurityInfo securityInfo = certificateVerifier.verifyCertificate(cert, sha3Hash, CLIENT); ValidateDeviceCredentialsResponse msg = securityInfo != null ? securityInfo.getMsg() : null; if (msg != null && org.thingsboard.server.common.data.StringUtils.isNotEmpty(msg.getCredentials())) { LwM2MClientCredentials credentials = JacksonUtil.fromString(msg.getCredentials(), LwM2MClientCredentials.class); @@ -201,27 +174,4 @@ public class TbLwM2MDtlsCertificateVerifier implements NewAdvancedCertificateVer public void setResultHandler(HandshakeResultHandler resultHandler) { } - - private X509Certificate verifyTrust(X509Certificate certificate, X509Certificate[] certificates) { - try { - CertificateFactory cf = CertificateFactory.getInstance("X.509"); - CertPath cp = cf.generateCertPath(Arrays.asList(new X509Certificate[]{certificate})); - for (int index = 0; index < certificates.length; ++index) { - X509Certificate caCert = certificates[index]; - try { - TrustAnchor trustAnchor = new TrustAnchor(caCert, null); - CertPathValidator cpv = CertPathValidator.getInstance("PKIX"); - PKIXParameters pkixParams = new PKIXParameters( - Collections.singleton(trustAnchor)); - pkixParams.setRevocationEnabled(false); - if (cpv.validate(cp, pkixParams) != null) return certificate; - } catch (NoSuchAlgorithmException | InvalidAlgorithmParameterException | CertPathValidatorException e) { - log.trace("[{}]. [{}]", certificate.getSubjectDN(), e.getMessage()); - } - } - } catch (CertificateException e) { - log.trace("[{}] certPath not valid. [{}]", certificate.getSubjectDN(), e.getMessage()); - } - return null; - } } From c3c23e079680669f3068b036eec9d65167a311e0 Mon Sep 17 00:00:00 2001 From: Volodymyr Babak Date: Wed, 12 Jan 2022 17:54:28 +0200 Subject: [PATCH 104/798] Added optional for widgettype image and description --- common/edge-api/src/main/proto/edge.proto | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/common/edge-api/src/main/proto/edge.proto b/common/edge-api/src/main/proto/edge.proto index 3d4cdc7fe8..243a77bd6b 100644 --- a/common/edge-api/src/main/proto/edge.proto +++ b/common/edge-api/src/main/proto/edge.proto @@ -333,8 +333,8 @@ message WidgetTypeUpdateMsg { optional string name = 6; optional string descriptorJson = 7; bool isSystem = 8; - string image = 9; - string description = 10; + optional string image = 9; + optional string description = 10; } message AdminSettingsUpdateMsg { From b4a1c0f83607c723fce2451c5d7a6212f89d33eb Mon Sep 17 00:00:00 2001 From: Volodymyr Babak Date: Wed, 12 Jan 2022 19:01:11 +0200 Subject: [PATCH 105/798] Increased timeout for timeseries with failures test --- .../src/test/java/org/thingsboard/server/edge/BaseEdgeTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/src/test/java/org/thingsboard/server/edge/BaseEdgeTest.java b/application/src/test/java/org/thingsboard/server/edge/BaseEdgeTest.java index c412e86ec6..1a3e4eef0d 100644 --- a/application/src/test/java/org/thingsboard/server/edge/BaseEdgeTest.java +++ b/application/src/test/java/org/thingsboard/server/edge/BaseEdgeTest.java @@ -1137,7 +1137,7 @@ abstract public class BaseEdgeTest extends AbstractControllerTest { clusterService.onEdgeEventUpdate(tenantId, edge.getId()); } - Assert.assertTrue(edgeImitator.waitForMessages(60)); + Assert.assertTrue(edgeImitator.waitForMessages(120)); List allTelemetryMsgs = edgeImitator.findAllMessagesByType(EntityDataProto.class); Assert.assertEquals(numberOfTimeseriesToSend, allTelemetryMsgs.size()); From 5bf75acccedca62b2f628d7a95ce203822c51a68 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Wed, 12 Jan 2022 19:01:47 +0200 Subject: [PATCH 106/798] Update material icons font --- ui-ngx/angular.json | 5 - ui-ngx/package.json | 1 - .../services/material-icons-codepoints.raw | 2142 +++++++++++++++++ ui-ngx/src/app/core/services/utils.service.ts | 2 +- .../assets/fonts/MaterialIcons-Regular.ttf | Bin 0 -> 337868 bytes ui-ngx/src/assets/fonts/material-icons.css | 33 + ui-ngx/src/index.html | 2 +- ui-ngx/yarn.lock | 5 - 8 files changed, 2177 insertions(+), 13 deletions(-) create mode 100644 ui-ngx/src/app/core/services/material-icons-codepoints.raw create mode 100644 ui-ngx/src/assets/fonts/MaterialIcons-Regular.ttf create mode 100644 ui-ngx/src/assets/fonts/material-icons.css diff --git a/ui-ngx/angular.json b/ui-ngx/angular.json index 7565ae770c..0691b25b4a 100644 --- a/ui-ngx/angular.json +++ b/ui-ngx/angular.json @@ -63,11 +63,6 @@ "glob": "marker-shadow.png", "input": "node_modules/leaflet/dist/images/", "output": "/" - }, - { - "glob": "**/*", - "input": "node_modules/material-design-icons/iconfont/", - "output": "assets/fonts" } ], "styles": [ diff --git a/ui-ngx/package.json b/ui-ngx/package.json index 1153524a26..d3174bfc4d 100644 --- a/ui-ngx/package.json +++ b/ui-ngx/package.json @@ -61,7 +61,6 @@ "leaflet-providers": "^1.13.0", "leaflet.gridlayer.googlemutant": "^0.13.4", "leaflet.markercluster": "^1.5.3", - "material-design-icons": "^3.0.1", "messageformat": "^2.3.0", "moment": "^2.29.1", "moment-timezone": "^0.5.34", diff --git a/ui-ngx/src/app/core/services/material-icons-codepoints.raw b/ui-ngx/src/app/core/services/material-icons-codepoints.raw new file mode 100644 index 0000000000..a1f9b8bbc7 --- /dev/null +++ b/ui-ngx/src/app/core/services/material-icons-codepoints.raw @@ -0,0 +1,2142 @@ +10k e951 +10mp e952 +11mp e953 +123 eb8d +12mp e954 +13mp e955 +14mp e956 +15mp e957 +16mp e958 +17mp e959 +18mp e95a +19mp e95b +1k e95c +1k_plus e95d +1x_mobiledata efcd +20mp e95e +21mp e95f +22mp e960 +23mp e961 +24mp e962 +2k e963 +2k_plus e964 +2mp e965 +30fps efce +30fps_select efcf +360 e577 +3d_rotation e84d +3g_mobiledata efd0 +3k e966 +3k_plus e967 +3mp e968 +3p efd1 +4g_mobiledata efd2 +4g_plus_mobiledata efd3 +4k e072 +4k_plus e969 +4mp e96a +5g ef38 +5k e96b +5k_plus e96c +5mp e96d +60fps efd4 +60fps_select efd5 +6_ft_apart f21e +6k e96e +6k_plus e96f +6mp e970 +7k e971 +7k_plus e972 +7mp e973 +8k e974 +8k_plus e975 +8mp e976 +9k e977 +9k_plus e978 +9mp e979 +abc eb94 +ac_unit eb3b +access_alarm e190 +access_alarms e191 +access_time e192 +access_time_filled efd6 +accessibility e84e +accessibility_new e92c +accessible e914 +accessible_forward e934 +account_balance e84f +account_balance_wallet e850 +account_box e851 +account_circle e853 +account_tree e97a +ad_units ef39 +adb e60e +add e145 +add_a_photo e439 +add_alarm e193 +add_alert e003 +add_box e146 +add_business e729 +add_call e0e8 +add_card eb86 +add_chart e97b +add_circle e147 +add_circle_outline e148 +add_comment e266 +add_ic_call e97c +add_link e178 +add_location e567 +add_location_alt ef3a +add_moderator e97d +add_photo_alternate e43e +add_reaction e1d3 +add_road ef3b +add_shopping_cart e854 +add_task f23a +add_to_drive e65c +add_to_home_screen e1fe +add_to_photos e39d +add_to_queue e05c +addchart ef3c +adf_scanner eada +adjust e39e +admin_panel_settings ef3d +adobe ea96 +ads_click e762 +agriculture ea79 +air efd8 +airline_seat_flat e630 +airline_seat_flat_angled e631 +airline_seat_individual_suite e632 +airline_seat_legroom_extra e633 +airline_seat_legroom_normal e634 +airline_seat_legroom_reduced e635 +airline_seat_recline_extra e636 +airline_seat_recline_normal e637 +airline_stops e7d0 +airlines e7ca +airplane_ticket efd9 +airplanemode_active e195 +airplanemode_inactive e194 +airplanemode_off e194 +airplanemode_on e195 +airplay e055 +airport_shuttle eb3c +alarm e855 +alarm_add e856 +alarm_off e857 +alarm_on e858 +album e019 +align_horizontal_center e00f +align_horizontal_left e00d +align_horizontal_right e010 +align_vertical_bottom e015 +align_vertical_center e011 +align_vertical_top e00c +all_inbox e97f +all_inclusive eb3d +all_out e90b +alt_route f184 +alternate_email e0e6 +amp_stories ea13 +analytics ef3e +anchor f1cd +android e859 +animation e71c +announcement e85a +aod efda +apartment ea40 +api f1b7 +app_blocking ef3f +app_registration ef40 +app_settings_alt ef41 +app_shortcut eae4 +apple ea80 +approval e982 +apps e5c3 +apps_outage e7cc +architecture ea3b +archive e149 +area_chart e770 +arrow_back e5c4 +arrow_back_ios e5e0 +arrow_back_ios_new e2ea +arrow_circle_down f181 +arrow_circle_left eaa7 +arrow_circle_right eaaa +arrow_circle_up f182 +arrow_downward e5db +arrow_drop_down e5c5 +arrow_drop_down_circle e5c6 +arrow_drop_up e5c7 +arrow_forward e5c8 +arrow_forward_ios e5e1 +arrow_left e5de +arrow_right e5df +arrow_right_alt e941 +arrow_upward e5d8 +art_track e060 +article ef42 +aspect_ratio e85b +assessment e85c +assignment e85d +assignment_ind e85e +assignment_late e85f +assignment_return e860 +assignment_returned e861 +assignment_turned_in e862 +assistant e39f +assistant_direction e988 +assistant_navigation e989 +assistant_photo e3a0 +assured_workload eb6f +atm e573 +attach_email ea5e +attach_file e226 +attach_money e227 +attachment e2bc +attractions ea52 +attribution efdb +audio_file eb82 +audiotrack e3a1 +auto_awesome e65f +auto_awesome_mosaic e660 +auto_awesome_motion e661 +auto_delete ea4c +auto_fix_high e663 +auto_fix_normal e664 +auto_fix_off e665 +auto_graph e4fb +auto_stories e666 +autofps_select efdc +autorenew e863 +av_timer e01b +baby_changing_station f19b +back_hand e764 +backpack f19c +backspace e14a +backup e864 +backup_table ef43 +badge ea67 +bakery_dining ea53 +balance eaf6 +balcony e58f +ballot e172 +bar_chart e26b +batch_prediction f0f5 +bathroom efdd +bathtub ea41 +battery_0_bar ebdc +battery_1_bar ebd9 +battery_2_bar ebe0 +battery_3_bar ebdd +battery_4_bar ebe2 +battery_5_bar ebd4 +battery_6_bar ebd2 +battery_alert e19c +battery_charging_full e1a3 +battery_full e1a4 +battery_saver efde +battery_std e1a5 +battery_unknown e1a6 +beach_access eb3e +bed efdf +bedroom_baby efe0 +bedroom_child efe1 +bedroom_parent efe2 +bedtime ef44 +bedtime_off eb76 +beenhere e52d +bento f1f4 +bike_scooter ef45 +biotech ea3a +blender efe3 +block e14b +block_flipped ef46 +bloodtype efe4 +bluetooth e1a7 +bluetooth_audio e60f +bluetooth_connected e1a8 +bluetooth_disabled e1a9 +bluetooth_drive efe5 +bluetooth_searching e1aa +blur_circular e3a2 +blur_linear e3a3 +blur_off e3a4 +blur_on e3a5 +bolt ea0b +book e865 +book_online f217 +bookmark e866 +bookmark_add e598 +bookmark_added e599 +bookmark_border e867 +bookmark_outline e867 +bookmark_remove e59a +bookmarks e98b +border_all e228 +border_bottom e229 +border_clear e22a +border_color e22b +border_horizontal e22c +border_inner e22d +border_left e22e +border_outer e22f +border_right e230 +border_style e231 +border_top e232 +border_vertical e233 +boy eb67 +branding_watermark e06b +breakfast_dining ea54 +brightness_1 e3a6 +brightness_2 e3a7 +brightness_3 e3a8 +brightness_4 e3a9 +brightness_5 e3aa +brightness_6 e3ab +brightness_7 e3ac +brightness_auto e1ab +brightness_high e1ac +brightness_low e1ad +brightness_medium e1ae +broken_image e3ad +browse_gallery ebd1 +browser_not_supported ef47 +browser_updated e7cf +brunch_dining ea73 +brush e3ae +bubble_chart e6dd +bug_report e868 +build e869 +build_circle ef48 +bungalow e591 +burst_mode e43c +bus_alert e98f +business e0af +business_center eb3f +cabin e589 +cable efe6 +cached e86a +cake e7e9 +calculate ea5f +calendar_month ebcc +calendar_today e935 +calendar_view_day e936 +calendar_view_month efe7 +calendar_view_week efe8 +call e0b0 +call_end e0b1 +call_made e0b2 +call_merge e0b3 +call_missed e0b4 +call_missed_outgoing e0e4 +call_received e0b5 +call_split e0b6 +call_to_action e06c +camera e3af +camera_alt e3b0 +camera_enhance e8fc +camera_front e3b1 +camera_indoor efe9 +camera_outdoor efea +camera_rear e3b2 +camera_roll e3b3 +cameraswitch efeb +campaign ef49 +cancel e5c9 +cancel_presentation e0e9 +cancel_schedule_send ea39 +candlestick_chart ead4 +car_crash ebf2 +car_rental ea55 +car_repair ea56 +card_giftcard e8f6 +card_membership e8f7 +card_travel e8f8 +carpenter f1f8 +cases e992 +casino eb40 +cast e307 +cast_connected e308 +cast_for_education efec +castle eab1 +catching_pokemon e508 +category e574 +celebration ea65 +cell_tower ebba +cell_wifi e0ec +center_focus_strong e3b4 +center_focus_weak e3b5 +chair efed +chair_alt efee +chalet e585 +change_circle e2e7 +change_history e86b +charging_station f19d +chat e0b7 +chat_bubble e0ca +chat_bubble_outline e0cb +check e5ca +check_box e834 +check_box_outline_blank e835 +check_circle e86c +check_circle_outline e92d +checklist e6b1 +checklist_rtl e6b3 +checkroom f19e +chevron_left e5cb +chevron_right e5cc +child_care eb41 +child_friendly eb42 +chrome_reader_mode e86d +church eaae +circle ef4a +circle_notifications e994 +class e86e +clean_hands f21f +cleaning_services f0ff +clear e14c +clear_all e0b8 +close e5cd +close_fullscreen f1cf +closed_caption e01c +closed_caption_disabled f1dc +closed_caption_off e996 +cloud e2bd +cloud_circle e2be +cloud_done e2bf +cloud_download e2c0 +cloud_off e2c1 +cloud_queue e2c2 +cloud_sync eb5a +cloud_upload e2c3 +cloudy_snowing e810 +co2 e7b0 +co_present eaf0 +code e86f +code_off e4f3 +coffee efef +coffee_maker eff0 +collections e3b6 +collections_bookmark e431 +color_lens e3b7 +colorize e3b8 +comment e0b9 +comment_bank ea4e +comments_disabled e7a2 +commit eaf5 +commute e940 +compare e3b9 +compare_arrows e915 +compass_calibration e57c +compost e761 +compress e94d +computer e30a +confirmation_num e638 +confirmation_number e638 +connect_without_contact f223 +connected_tv e998 +connecting_airports e7c9 +construction ea3c +contact_mail e0d0 +contact_page f22e +contact_phone e0cf +contact_support e94c +contactless ea71 +contacts e0ba +content_copy e14d +content_cut e14e +content_paste e14f +content_paste_go ea8e +content_paste_off e4f8 +content_paste_search ea9b +contrast eb37 +control_camera e074 +control_point e3ba +control_point_duplicate e3bb +cookie eaac +copy_all e2ec +copyright e90c +coronavirus f221 +corporate_fare f1d0 +cottage e587 +countertops f1f7 +create e150 +create_new_folder e2cc +credit_card e870 +credit_card_off e4f4 +credit_score eff1 +crib e588 +crisis_alert ebe9 +crop e3be +crop_16_9 e3bc +crop_3_2 e3bd +crop_5_4 e3bf +crop_7_5 e3c0 +crop_din e3c1 +crop_free e3c2 +crop_landscape e3c3 +crop_original e3c4 +crop_portrait e3c5 +crop_rotate e437 +crop_square e3c6 +cruelty_free e799 +css eb93 +currency_bitcoin ebc5 +currency_exchange eb70 +currency_franc eafa +currency_lira eaef +currency_pound eaf1 +currency_ruble eaec +currency_rupee eaf7 +currency_yen eafb +currency_yuan eaf9 +cyclone ebd5 +dangerous e99a +dark_mode e51c +dashboard e871 +dashboard_customize e99b +data_array ead1 +data_exploration e76f +data_object ead3 +data_saver_off eff2 +data_saver_on eff3 +data_thresholding eb9f +data_usage e1af +date_range e916 +deblur eb77 +deck ea42 +dehaze e3c7 +delete e872 +delete_forever e92b +delete_outline e92e +delete_sweep e16c +delivery_dining ea72 +density_large eba9 +density_medium eb9e +density_small eba8 +departure_board e576 +description e873 +deselect ebb6 +design_services f10a +desktop_access_disabled e99d +desktop_mac e30b +desktop_windows e30c +details e3c8 +developer_board e30d +developer_board_off e4ff +developer_mode e1b0 +device_hub e335 +device_thermostat e1ff +device_unknown e339 +devices e1b1 +devices_fold ebde +devices_other e337 +dialer_sip e0bb +dialpad e0bc +diamond ead5 +difference eb7d +dining eff4 +dinner_dining ea57 +directions e52e +directions_bike e52f +directions_boat e532 +directions_boat_filled eff5 +directions_bus e530 +directions_bus_filled eff6 +directions_car e531 +directions_car_filled eff7 +directions_ferry e532 +directions_off f10f +directions_railway e534 +directions_railway_filled eff8 +directions_run e566 +directions_subway e533 +directions_subway_filled eff9 +directions_train e534 +directions_transit e535 +directions_transit_filled effa +directions_walk e536 +dirty_lens ef4b +disabled_by_default f230 +disabled_visible e76e +disc_full e610 +discord ea6c +discount ebc9 +display_settings eb97 +dnd_forwardslash e611 +dns e875 +do_disturb f08c +do_disturb_alt f08d +do_disturb_off f08e +do_disturb_on f08f +do_not_disturb e612 +do_not_disturb_alt e611 +do_not_disturb_off e643 +do_not_disturb_on e644 +do_not_disturb_on_total_silence effb +do_not_step f19f +do_not_touch f1b0 +dock e30e +document_scanner e5fa +domain e7ee +domain_add eb62 +domain_disabled e0ef +domain_verification ef4c +done e876 +done_all e877 +done_outline e92f +donut_large e917 +donut_small e918 +door_back effc +door_front effd +door_sliding effe +doorbell efff +double_arrow ea50 +downhill_skiing e509 +download f090 +download_done f091 +download_for_offline f000 +downloading f001 +drafts e151 +drag_handle e25d +drag_indicator e945 +draw e746 +drive_eta e613 +drive_file_move e675 +drive_file_move_outline e9a1 +drive_file_move_rtl e76d +drive_file_rename_outline e9a2 +drive_folder_upload e9a3 +dry f1b3 +dry_cleaning ea58 +duo e9a5 +dvr e1b2 +dynamic_feed ea14 +dynamic_form f1bf +e_mobiledata f002 +earbuds f003 +earbuds_battery f004 +east f1df +eco ea35 +edgesensor_high f005 +edgesensor_low f006 +edit e3c9 +edit_attributes e578 +edit_calendar e742 +edit_location e568 +edit_location_alt e1c5 +edit_note e745 +edit_notifications e525 +edit_off e950 +edit_road ef4d +egg eacc +egg_alt eac8 +eject e8fb +elderly f21a +elderly_woman eb69 +electric_bike eb1b +electric_car eb1c +electric_moped eb1d +electric_rickshaw eb1e +electric_scooter eb1f +electrical_services f102 +elevator f1a0 +email e0be +emergency e1eb +emergency_recording ebf4 +emergency_share ebf6 +emoji_emotions ea22 +emoji_events ea23 +emoji_flags ea1a +emoji_food_beverage ea1b +emoji_nature ea1c +emoji_objects ea24 +emoji_people ea1d +emoji_symbols ea1e +emoji_transportation ea1f +engineering ea3d +enhance_photo_translate e8fc +enhanced_encryption e63f +equalizer e01d +error e000 +error_outline e001 +escalator f1a1 +escalator_warning f1ac +euro ea15 +euro_symbol e926 +ev_station e56d +event e878 +event_available e614 +event_busy e615 +event_note e616 +event_repeat eb7b +event_seat e903 +exit_to_app e879 +expand e94f +expand_circle_down e7cd +expand_less e5ce +expand_more e5cf +explicit e01e +explore e87a +explore_off e9a8 +exposure e3ca +exposure_minus_1 e3cb +exposure_minus_2 e3cc +exposure_neg_1 e3cb +exposure_neg_2 e3cc +exposure_plus_1 e3cd +exposure_plus_2 e3ce +exposure_zero e3cf +extension e87b +extension_off e4f5 +face e87c +face_retouching_natural ef4e +face_retouching_off f007 +facebook f234 +fact_check f0c5 +factory ebbc +family_restroom f1a2 +fast_forward e01f +fast_rewind e020 +fastfood e57a +favorite e87d +favorite_border e87e +favorite_outline e87e +fax ead8 +featured_play_list e06d +featured_video e06e +feed f009 +feedback e87f +female e590 +fence f1f6 +festival ea68 +fiber_dvr e05d +fiber_manual_record e061 +fiber_new e05e +fiber_pin e06a +fiber_smart_record e062 +file_copy e173 +file_download e2c4 +file_download_done e9aa +file_download_off e4fe +file_open eaf3 +file_present ea0e +file_upload e2c6 +filter e3d3 +filter_1 e3d0 +filter_2 e3d1 +filter_3 e3d2 +filter_4 e3d4 +filter_5 e3d5 +filter_6 e3d6 +filter_7 e3d7 +filter_8 e3d8 +filter_9 e3d9 +filter_9_plus e3da +filter_alt ef4f +filter_alt_off eb32 +filter_b_and_w e3db +filter_center_focus e3dc +filter_drama e3dd +filter_frames e3de +filter_hdr e3df +filter_list e152 +filter_list_alt e94e +filter_list_off eb57 +filter_none e3e0 +filter_tilt_shift e3e2 +filter_vintage e3e3 +find_in_page e880 +find_replace e881 +fingerprint e90d +fire_extinguisher f1d8 +fire_hydrant f1a3 +fireplace ea43 +first_page e5dc +fit_screen ea10 +fitbit e82b +fitness_center eb43 +flag e153 +flag_circle eaf8 +flaky ef50 +flare e3e4 +flash_auto e3e5 +flash_off e3e6 +flash_on e3e7 +flashlight_off f00a +flashlight_on f00b +flatware f00c +flight e539 +flight_class e7cb +flight_land e904 +flight_takeoff e905 +flip e3e8 +flip_camera_android ea37 +flip_camera_ios ea38 +flip_to_back e882 +flip_to_front e883 +flood ebe6 +flourescent f00d +flutter_dash e00b +fmd_bad f00e +fmd_good f00f +foggy e818 +folder e2c7 +folder_copy ebbd +folder_delete eb34 +folder_off eb83 +folder_open e2c8 +folder_shared e2c9 +folder_special e617 +folder_zip eb2c +follow_the_signs f222 +font_download e167 +font_download_off e4f9 +food_bank f1f2 +forest ea99 +fork_left eba0 +fork_right ebac +format_align_center e234 +format_align_justify e235 +format_align_left e236 +format_align_right e237 +format_bold e238 +format_clear e239 +format_color_fill e23a +format_color_reset e23b +format_color_text e23c +format_indent_decrease e23d +format_indent_increase e23e +format_italic e23f +format_line_spacing e240 +format_list_bulleted e241 +format_list_numbered e242 +format_list_numbered_rtl e267 +format_overline eb65 +format_paint e243 +format_quote e244 +format_shapes e25e +format_size e245 +format_strikethrough e246 +format_textdirection_l_to_r e247 +format_textdirection_r_to_l e248 +format_underline e249 +format_underlined e249 +fort eaad +forum e0bf +forward e154 +forward_10 e056 +forward_30 e057 +forward_5 e058 +forward_to_inbox f187 +foundation f200 +free_breakfast eb44 +free_cancellation e748 +front_hand e769 +fullscreen e5d0 +fullscreen_exit e5d1 +functions e24a +g_mobiledata f010 +g_translate e927 +gamepad e30f +games e021 +garage f011 +gavel e90e +generating_tokens e749 +gesture e155 +get_app e884 +gif e908 +gif_box e7a3 +girl eb68 +gite e58b +goat 10fffd +golf_course eb45 +gpp_bad f012 +gpp_good f013 +gpp_maybe f014 +gps_fixed e1b3 +gps_not_fixed e1b4 +gps_off e1b5 +grade e885 +gradient e3e9 +grading ea4f +grain e3ea +graphic_eq e1b8 +grass f205 +grid_3x3 f015 +grid_4x4 f016 +grid_goldenratio f017 +grid_off e3eb +grid_on e3ec +grid_view e9b0 +group e7ef +group_add e7f0 +group_off e747 +group_remove e7ad +group_work e886 +groups f233 +h_mobiledata f018 +h_plus_mobiledata f019 +hail e9b1 +handshake ebcb +handyman f10b +hardware ea59 +hd e052 +hdr_auto f01a +hdr_auto_select f01b +hdr_enhanced_select ef51 +hdr_off e3ed +hdr_off_select f01c +hdr_on e3ee +hdr_on_select f01d +hdr_plus f01e +hdr_strong e3f1 +hdr_weak e3f2 +headphones f01f +headphones_battery f020 +headset e310 +headset_mic e311 +headset_off e33a +healing e3f3 +health_and_safety e1d5 +hearing e023 +hearing_disabled f104 +heart_broken eac2 +height ea16 +help e887 +help_center f1c0 +help_outline e8fd +hevc f021 +hexagon eb39 +hide_image f022 +hide_source f023 +high_quality e024 +highlight e25f +highlight_alt ef52 +highlight_off e888 +highlight_remove e888 +hiking e50a +history e889 +history_edu ea3e +history_toggle_off f17d +hive eaa6 +hls eb8a +hls_off eb8c +holiday_village e58a +home e88a +home_filled e9b2 +home_max f024 +home_mini f025 +home_repair_service f100 +home_work ea09 +horizontal_distribute e014 +horizontal_rule f108 +horizontal_split e947 +hot_tub eb46 +hotel e53a +hotel_class e743 +hourglass_bottom ea5c +hourglass_disabled ef53 +hourglass_empty e88b +hourglass_full e88c +hourglass_top ea5b +house ea44 +house_siding f202 +houseboat e584 +how_to_reg e174 +how_to_vote e175 +html eb7e +http e902 +https e88d +hub e9f4 +hvac f10e +ice_skating e50b +icecream ea69 +image e3f4 +image_aspect_ratio e3f5 +image_not_supported f116 +image_search e43f +imagesearch_roller e9b4 +import_contacts e0e0 +import_export e0c3 +important_devices e912 +inbox e156 +incomplete_circle e79b +indeterminate_check_box e909 +info e88e +info_outline e88f +input e890 +insert_chart e24b +insert_chart_outlined e26a +insert_comment e24c +insert_drive_file e24d +insert_emoticon e24e +insert_invitation e24f +insert_link e250 +insert_page_break eaca +insert_photo e251 +insights f092 +install_desktop eb71 +install_mobile eb72 +integration_instructions ef54 +interests e7c8 +interpreter_mode e83b +inventory e179 +inventory_2 e1a1 +invert_colors e891 +invert_colors_off e0c4 +invert_colors_on e891 +ios_share e6b8 +iron e583 +iso e3f6 +javascript eb7c +join_full eaeb +join_inner eaf4 +join_left eaf2 +join_right eaea +kayaking e50c +kebab_dining e842 +key e73c +key_off eb84 +keyboard e312 +keyboard_alt f028 +keyboard_arrow_down e313 +keyboard_arrow_left e314 +keyboard_arrow_right e315 +keyboard_arrow_up e316 +keyboard_backspace e317 +keyboard_capslock e318 +keyboard_command eae0 +keyboard_command_key eae7 +keyboard_control e5d3 +keyboard_control_key eae6 +keyboard_double_arrow_down ead0 +keyboard_double_arrow_left eac3 +keyboard_double_arrow_right eac9 +keyboard_double_arrow_up eacf +keyboard_hide e31a +keyboard_option eadf +keyboard_option_key eae8 +keyboard_return e31b +keyboard_tab e31c +keyboard_voice e31d +king_bed ea45 +kitchen eb47 +kitesurfing e50d +label e892 +label_important e937 +label_important_outline e948 +label_off e9b6 +label_outline e893 +lan eb2f +landscape e3f7 +landslide ebd7 +language e894 +laptop e31e +laptop_chromebook e31f +laptop_mac e320 +laptop_windows e321 +last_page e5dd +launch e895 +layers e53b +layers_clear e53c +leaderboard f20c +leak_add e3f8 +leak_remove e3f9 +leave_bags_at_home f21b +legend_toggle f11b +lens e3fa +lens_blur f029 +library_add e02e +library_add_check e9b7 +library_books e02f +library_music e030 +light f02a +light_mode e518 +lightbulb e0f0 +lightbulb_outline e90f +line_axis ea9a +line_style e919 +line_weight e91a +linear_scale e260 +link e157 +link_off e16f +linked_camera e438 +liquor ea60 +list e896 +list_alt e0ee +live_help e0c6 +live_tv e639 +living f02b +local_activity e53f +local_airport e53d +local_atm e53e +local_attraction e53f +local_bar e540 +local_cafe e541 +local_car_wash e542 +local_convenience_store e543 +local_dining e556 +local_drink e544 +local_fire_department ef55 +local_florist e545 +local_gas_station e546 +local_grocery_store e547 +local_hospital e548 +local_hotel e549 +local_laundry_service e54a +local_library e54b +local_mall e54c +local_movies e54d +local_offer e54e +local_parking e54f +local_pharmacy e550 +local_phone e551 +local_pizza e552 +local_play e553 +local_police ef56 +local_post_office e554 +local_print_shop e555 +local_printshop e555 +local_restaurant e556 +local_see e557 +local_shipping e558 +local_taxi e559 +location_city e7f1 +location_disabled e1b6 +location_history e55a +location_off e0c7 +location_on e0c8 +location_pin f1db +location_searching e1b7 +lock e897 +lock_clock ef57 +lock_open e898 +lock_outline e899 +lock_reset eade +login ea77 +logo_dev ead6 +logout e9ba +looks e3fc +looks_3 e3fb +looks_4 e3fd +looks_5 e3fe +looks_6 e3ff +looks_one e400 +looks_two e401 +loop e028 +loupe e402 +low_priority e16d +loyalty e89a +lte_mobiledata f02c +lte_plus_mobiledata f02d +luggage f235 +lunch_dining ea61 +mail e158 +mail_outline e0e1 +male e58e +man e4eb +manage_accounts f02e +manage_history ebe7 +manage_search f02f +map e55b +maps_home_work f030 +maps_ugc ef58 +margin e9bb +mark_as_unread e9bc +mark_chat_read f18b +mark_chat_unread f189 +mark_email_read f18c +mark_email_unread f18a +mark_unread_chat_alt eb9d +markunread e159 +markunread_mailbox e89b +masks f218 +maximize e930 +media_bluetooth_off f031 +media_bluetooth_on f032 +mediation efa7 +medical_information ebed +medical_services f109 +medication f033 +medication_liquid ea87 +meeting_room eb4f +memory e322 +menu e5d2 +menu_book ea19 +menu_open e9bd +merge eb98 +merge_type e252 +message e0c9 +messenger e0ca +messenger_outline e0cb +mic e029 +mic_external_off ef59 +mic_external_on ef5a +mic_none e02a +mic_off e02b +microwave f204 +military_tech ea3f +minimize e931 +minor_crash ebf1 +miscellaneous_services f10c +missed_video_call e073 +mms e618 +mobile_friendly e200 +mobile_off e201 +mobile_screen_share e0e7 +mobiledata_off f034 +mode f097 +mode_comment e253 +mode_edit e254 +mode_edit_outline f035 +mode_night f036 +mode_of_travel e7ce +mode_standby f037 +model_training f0cf +monetization_on e263 +money e57d +money_off e25c +money_off_csred f038 +monitor ef5b +monitor_heart eaa2 +monitor_weight f039 +monochrome_photos e403 +mood e7f2 +mood_bad e7f3 +moped eb28 +more e619 +more_horiz e5d3 +more_time ea5d +more_vert e5d4 +mosque eab2 +motion_photos_auto f03a +motion_photos_off e9c0 +motion_photos_on e9c1 +motion_photos_pause f227 +motion_photos_paused e9c2 +motorcycle e91b +mouse e323 +move_down eb61 +move_to_inbox e168 +move_up eb64 +movie e02c +movie_creation e404 +movie_filter e43a +moving e501 +mp e9c3 +multiline_chart e6df +multiple_stop f1b9 +multitrack_audio e1b8 +museum ea36 +music_note e405 +music_off e440 +music_video e063 +my_library_add e02e +my_library_books e02f +my_library_music e030 +my_location e55c +nat ef5c +nature e406 +nature_people e407 +navigate_before e408 +navigate_next e409 +navigation e55d +near_me e569 +near_me_disabled f1ef +nearby_error f03b +nearby_off f03c +network_cell e1b9 +network_check e640 +network_locked e61a +network_ping ebca +network_wifi e1ba +network_wifi_1_bar ebe4 +network_wifi_2_bar ebd6 +network_wifi_3_bar ebe1 +new_label e609 +new_releases e031 +newspaper eb81 +next_plan ef5d +next_week e16a +nfc e1bb +night_shelter f1f1 +nightlife ea62 +nightlight f03d +nightlight_round ef5e +nights_stay ea46 +no_accounts f03e +no_backpack f237 +no_cell f1a4 +no_crash ebf0 +no_drinks f1a5 +no_encryption e641 +no_encryption_gmailerrorred f03f +no_flash f1a6 +no_food f1a7 +no_luggage f23b +no_meals f1d6 +no_meals_ouline f229 +no_meeting_room eb4e +no_photography f1a8 +no_sim e0cc +no_stroller f1af +no_transfer f1d5 +noise_aware ebec +noise_control_off ebf3 +nordic_walking e50e +north f1e0 +north_east f1e1 +north_west f1e2 +not_accessible f0fe +not_interested e033 +not_listed_location e575 +not_started f0d1 +note e06f +note_add e89c +note_alt f040 +notes e26c +notification_add e399 +notification_important e004 +notifications e7f4 +notifications_active e7f7 +notifications_none e7f5 +notifications_off e7f6 +notifications_on e7f7 +notifications_paused e7f8 +now_wallpaper e1bc +now_widgets e1bd +numbers eac7 +offline_bolt e932 +offline_pin e90a +offline_share e9c5 +ondemand_video e63a +online_prediction f0eb +opacity e91c +open_in_browser e89d +open_in_full f1ce +open_in_new e89e +open_in_new_off e4f6 +open_with e89f +other_houses e58c +outbond f228 +outbound e1ca +outbox ef5f +outdoor_grill ea47 +outgoing_mail f0d2 +outlet f1d4 +outlined_flag e16e +output ebbe +padding e9c8 +pages e7f9 +pageview e8a0 +paid f041 +palette e40a +pan_tool e925 +pan_tool_alt ebb9 +panorama e40b +panorama_fish_eye e40c +panorama_fisheye e40c +panorama_horizontal e40d +panorama_horizontal_select ef60 +panorama_photosphere e9c9 +panorama_photosphere_select e9ca +panorama_vertical e40e +panorama_vertical_select ef61 +panorama_wide_angle e40f +panorama_wide_angle_select ef62 +paragliding e50f +park ea63 +party_mode e7fa +password f042 +pattern f043 +pause e034 +pause_circle e1a2 +pause_circle_filled e035 +pause_circle_outline e036 +pause_presentation e0ea +payment e8a1 +payments ef63 +paypal ea8d +pedal_bike eb29 +pending ef64 +pending_actions f1bb +pentagon eb50 +people e7fb +people_alt ea21 +people_outline e7fc +percent eb58 +perm_camera_mic e8a2 +perm_contact_cal e8a3 +perm_contact_calendar e8a3 +perm_data_setting e8a4 +perm_device_info e8a5 +perm_device_information e8a5 +perm_identity e8a6 +perm_media e8a7 +perm_phone_msg e8a8 +perm_scan_wifi e8a9 +person e7fd +person_add e7fe +person_add_alt ea4d +person_add_alt_1 ef65 +person_add_disabled e9cb +person_off e510 +person_outline e7ff +person_pin e55a +person_pin_circle e56a +person_remove ef66 +person_remove_alt_1 ef67 +person_search f106 +personal_injury e6da +personal_video e63b +pest_control f0fa +pest_control_rodent f0fd +pets e91d +phishing ead7 +phone e0cd +phone_android e324 +phone_bluetooth_speaker e61b +phone_callback e649 +phone_disabled e9cc +phone_enabled e9cd +phone_forwarded e61c +phone_in_talk e61d +phone_iphone e325 +phone_locked e61e +phone_missed e61f +phone_paused e620 +phonelink e326 +phonelink_erase e0db +phonelink_lock e0dc +phonelink_off e327 +phonelink_ring e0dd +phonelink_setup e0de +photo e410 +photo_album e411 +photo_camera e412 +photo_camera_back ef68 +photo_camera_front ef69 +photo_filter e43b +photo_library e413 +photo_size_select_actual e432 +photo_size_select_large e433 +photo_size_select_small e434 +php eb8f +piano e521 +piano_off e520 +picture_as_pdf e415 +picture_in_picture e8aa +picture_in_picture_alt e911 +pie_chart e6c4 +pie_chart_outline f044 +pie_chart_outlined e6c5 +pin f045 +pin_drop e55e +pin_end e767 +pin_invoke e763 +pinch eb38 +pivot_table_chart e9ce +pix eaa3 +place e55f +plagiarism ea5a +play_arrow e037 +play_circle e1c4 +play_circle_fill e038 +play_circle_filled e038 +play_circle_outline e039 +play_disabled ef6a +play_for_work e906 +play_lesson f047 +playlist_add e03b +playlist_add_check e065 +playlist_add_check_circle e7e6 +playlist_add_circle e7e5 +playlist_play e05f +playlist_remove eb80 +plumbing f107 +plus_one e800 +podcasts f048 +point_of_sale f17e +policy ea17 +poll e801 +polyline ebbb +polymer e8ab +pool eb48 +portable_wifi_off e0ce +portrait e416 +post_add ea20 +power e63c +power_input e336 +power_off e646 +power_settings_new e8ac +precision_manufacturing f049 +pregnant_woman e91e +present_to_all e0df +preview f1c5 +price_change f04a +price_check f04b +print e8ad +print_disabled e9cf +priority_high e645 +privacy_tip f0dc +private_connectivity e744 +production_quantity_limits e1d1 +psychology ea4a +public e80b +public_off f1ca +publish e255 +published_with_changes f232 +punch_clock eaa8 +push_pin f10d +qr_code ef6b +qr_code_2 e00a +qr_code_scanner f206 +query_builder e8ae +query_stats e4fc +question_answer e8af +question_mark eb8b +queue e03c +queue_music e03d +queue_play_next e066 +quick_contacts_dialer e0cf +quick_contacts_mail e0d0 +quickreply ef6c +quiz f04c +quora ea98 +r_mobiledata f04d +radar f04e +radio e03e +radio_button_checked e837 +radio_button_off e836 +radio_button_on e837 +radio_button_unchecked e836 +railway_alert e9d1 +ramen_dining ea64 +ramp_left eb9c +ramp_right eb96 +rate_review e560 +raw_off f04f +raw_on f050 +read_more ef6d +real_estate_agent e73a +receipt e8b0 +receipt_long ef6e +recent_actors e03f +recommend e9d2 +record_voice_over e91f +rectangle eb54 +recycling e760 +reddit eaa0 +redeem e8b1 +redo e15a +reduce_capacity f21c +refresh e5d5 +remember_me f051 +remove e15b +remove_circle e15c +remove_circle_outline e15d +remove_done e9d3 +remove_from_queue e067 +remove_moderator e9d4 +remove_red_eye e417 +remove_shopping_cart e928 +reorder e8fe +repeat e040 +repeat_on e9d6 +repeat_one e041 +repeat_one_on e9d7 +replay e042 +replay_10 e059 +replay_30 e05a +replay_5 e05b +replay_circle_filled e9d8 +reply e15e +reply_all e15f +report e160 +report_gmailerrorred f052 +report_off e170 +report_problem e8b2 +request_page f22c +request_quote f1b6 +reset_tv e9d9 +restart_alt f053 +restaurant e56c +restaurant_menu e561 +restore e8b3 +restore_from_trash e938 +restore_page e929 +reviews f054 +rice_bowl f1f5 +ring_volume e0d1 +rocket eba5 +rocket_launch eb9b +roller_skating ebcd +roofing f201 +room e8b4 +room_preferences f1b8 +room_service eb49 +rotate_90_degrees_ccw e418 +rotate_90_degrees_cw eaab +rotate_left e419 +rotate_right e41a +roundabout_left eb99 +roundabout_right eba3 +rounded_corner e920 +route eacd +router e328 +rowing e921 +rss_feed e0e5 +rsvp f055 +rtt e9ad +rule f1c2 +rule_folder f1c9 +run_circle ef6f +running_with_errors e51d +rv_hookup e642 +safety_check ebef +safety_divider e1cc +sailing e502 +sanitizer f21d +satellite e562 +satellite_alt eb3a +save e161 +save_alt e171 +save_as eb60 +saved_search ea11 +savings e2eb +scale eb5f +scanner e329 +scatter_plot e268 +schedule e8b5 +schedule_send ea0a +schema e4fd +school e80c +science ea4b +score e269 +scoreboard ebd0 +screen_lock_landscape e1be +screen_lock_portrait e1bf +screen_lock_rotation e1c0 +screen_rotation e1c1 +screen_rotation_alt ebee +screen_search_desktop ef70 +screen_share e0e2 +screenshot f056 +scuba_diving ebce +sd e9dd +sd_card e623 +sd_card_alert f057 +sd_storage e1c2 +search e8b6 +search_off ea76 +security e32a +security_update f058 +security_update_good f059 +security_update_warning f05a +segment e94b +select_all e162 +self_improvement ea78 +sell f05b +send e163 +send_and_archive ea0c +send_time_extension eadb +send_to_mobile f05c +sensor_door f1b5 +sensor_window f1b4 +sensors e51e +sensors_off e51f +sentiment_dissatisfied e811 +sentiment_neutral e812 +sentiment_satisfied e813 +sentiment_satisfied_alt e0ed +sentiment_very_dissatisfied e814 +sentiment_very_satisfied e815 +set_meal f1ea +settings e8b8 +settings_accessibility f05d +settings_applications e8b9 +settings_backup_restore e8ba +settings_bluetooth e8bb +settings_brightness e8bd +settings_cell e8bc +settings_display e8bd +settings_ethernet e8be +settings_input_antenna e8bf +settings_input_component e8c0 +settings_input_composite e8c1 +settings_input_hdmi e8c2 +settings_input_svideo e8c3 +settings_overscan e8c4 +settings_phone e8c5 +settings_power e8c6 +settings_remote e8c7 +settings_suggest f05e +settings_system_daydream e1c3 +settings_voice e8c8 +severe_cold ebd3 +share e80d +share_arrival_time e524 +share_location f05f +shield e9e0 +shield_moon eaa9 +shop e8c9 +shop_2 e19e +shop_two e8ca +shopify ea9d +shopping_bag f1cc +shopping_basket e8cb +shopping_cart e8cc +shopping_cart_checkout eb88 +short_text e261 +shortcut f060 +show_chart e6e1 +shower f061 +shuffle e043 +shuffle_on e9e1 +shutter_speed e43d +sick f220 +sign_language ebe5 +signal_cellular_0_bar f0a8 +signal_cellular_4_bar e1c8 +signal_cellular_alt e202 +signal_cellular_alt_1_bar ebdf +signal_cellular_alt_2_bar ebe3 +signal_cellular_connected_no_internet_0_bar f0ac +signal_cellular_connected_no_internet_4_bar e1cd +signal_cellular_no_sim e1ce +signal_cellular_nodata f062 +signal_cellular_null e1cf +signal_cellular_off e1d0 +signal_wifi_0_bar f0b0 +signal_wifi_4_bar e1d8 +signal_wifi_4_bar_lock e1d9 +signal_wifi_bad f063 +signal_wifi_connected_no_internet_4 f064 +signal_wifi_off e1da +signal_wifi_statusbar_4_bar f065 +signal_wifi_statusbar_connected_no_internet_4 f066 +signal_wifi_statusbar_null f067 +signpost eb91 +sim_card e32b +sim_card_alert e624 +sim_card_download f068 +single_bed ea48 +sip f069 +skateboarding e511 +skip_next e044 +skip_previous e045 +sledding e512 +slideshow e41b +slow_motion_video e068 +smart_button f1c1 +smart_display f06a +smart_screen f06b +smart_toy f06c +smartphone e32c +smoke_free eb4a +smoking_rooms eb4b +sms e625 +sms_failed e626 +snapchat ea6e +snippet_folder f1c7 +snooze e046 +snowboarding e513 +snowing e80f +snowmobile e503 +snowshoeing e514 +soap f1b2 +social_distance e1cb +sort e164 +sort_by_alpha e053 +sos ebf7 +soup_kitchen e7d3 +source f1c4 +south f1e3 +south_america e7e4 +south_east f1e4 +south_west f1e5 +spa eb4c +space_bar e256 +space_dashboard e66b +spatial_audio ebeb +spatial_audio_off ebe8 +spatial_tracking ebea +speaker e32d +speaker_group e32e +speaker_notes e8cd +speaker_notes_off e92a +speaker_phone e0d2 +speed e9e4 +spellcheck e8ce +splitscreen f06d +spoke e9a7 +sports ea30 +sports_bar f1f3 +sports_baseball ea51 +sports_basketball ea26 +sports_cricket ea27 +sports_esports ea28 +sports_football ea29 +sports_golf ea2a +sports_gymnastics ebc4 +sports_handball ea33 +sports_hockey ea2b +sports_kabaddi ea34 +sports_martial_arts eae9 +sports_mma ea2c +sports_motorsports ea2d +sports_rugby ea2e +sports_score f06e +sports_soccer ea2f +sports_tennis ea32 +sports_volleyball ea31 +square eb36 +square_foot ea49 +ssid_chart eb66 +stacked_bar_chart e9e6 +stacked_line_chart f22b +stadium eb90 +stairs f1a9 +star e838 +star_border e83a +star_border_purple500 f099 +star_half e839 +star_outline f06f +star_purple500 f09a +star_rate f0ec +stars e8d0 +start e089 +stay_current_landscape e0d3 +stay_current_portrait e0d4 +stay_primary_landscape e0d5 +stay_primary_portrait e0d6 +sticky_note_2 f1fc +stop e047 +stop_circle ef71 +stop_screen_share e0e3 +storage e1db +store e8d1 +store_mall_directory e563 +storefront ea12 +storm f070 +straight eb95 +straighten e41c +stream e9e9 +streetview e56e +strikethrough_s e257 +stroller f1ae +style e41d +subdirectory_arrow_left e5d9 +subdirectory_arrow_right e5da +subject e8d2 +subscript f111 +subscriptions e064 +subtitles e048 +subtitles_off ef72 +subway e56f +summarize f071 +sunny e81a +sunny_snowing e819 +superscript f112 +supervised_user_circle e939 +supervisor_account e8d3 +support ef73 +support_agent f0e2 +surfing e515 +surround_sound e049 +swap_calls e0d7 +swap_horiz e8d4 +swap_horizontal_circle e933 +swap_vert e8d5 +swap_vert_circle e8d6 +swap_vertical_circle e8d6 +swipe e9ec +swipe_down eb53 +swipe_down_alt eb30 +swipe_left eb59 +swipe_left_alt eb33 +swipe_right eb52 +swipe_right_alt eb56 +swipe_up eb2e +swipe_up_alt eb35 +swipe_vertical eb51 +switch_access_shortcut e7e1 +switch_access_shortcut_add e7e2 +switch_account e9ed +switch_camera e41e +switch_left f1d1 +switch_right f1d2 +switch_video e41f +synagogue eab0 +sync e627 +sync_alt ea18 +sync_disabled e628 +sync_lock eaee +sync_problem e629 +system_security_update f072 +system_security_update_good f073 +system_security_update_warning f074 +system_update e62a +system_update_alt e8d7 +system_update_tv e8d7 +tab e8d8 +tab_unselected e8d9 +table_bar ead2 +table_chart e265 +table_restaurant eac6 +table_rows f101 +table_view f1be +tablet e32f +tablet_android e330 +tablet_mac e331 +tag e9ef +tag_faces e420 +takeout_dining ea74 +tap_and_play e62b +tapas f1e9 +task f075 +task_alt e2e6 +taxi_alert ef74 +telegram ea6b +temple_buddhist eab3 +temple_hindu eaaf +terminal eb8e +terrain e564 +text_decrease eadd +text_fields e262 +text_format e165 +text_increase eae2 +text_rotate_up e93a +text_rotate_vertical e93b +text_rotation_angledown e93c +text_rotation_angleup e93d +text_rotation_down e93e +text_rotation_none e93f +text_snippet f1c6 +textsms e0d8 +texture e421 +theater_comedy ea66 +theaters e8da +thermostat f076 +thermostat_auto f077 +thumb_down e8db +thumb_down_alt e816 +thumb_down_off_alt e9f2 +thumb_up e8dc +thumb_up_alt e817 +thumb_up_off_alt e9f3 +thumbs_up_down e8dd +thunderstorm ebdb +tiktok ea7e +time_to_leave e62c +timelapse e422 +timeline e922 +timer e425 +timer_10 e423 +timer_10_select f07a +timer_3 e424 +timer_3_select f07b +timer_off e426 +tips_and_updates e79a +tire_repair ebc8 +title e264 +toc e8de +today e8df +toggle_off e9f5 +toggle_on e9f6 +token ea25 +toll e8e0 +tonality e427 +topic f1c8 +touch_app e913 +tour ef75 +toys e332 +track_changes e8e1 +traffic e565 +train e570 +tram e571 +transfer_within_a_station e572 +transform e428 +transgender e58d +transit_enterexit e579 +translate e8e2 +travel_explore e2db +trending_down e8e3 +trending_flat e8e4 +trending_neutral e8e4 +trending_up e8e5 +trip_origin e57b +try f07c +tsunami ebd8 +tty f1aa +tune e429 +tungsten f07d +turn_left eba6 +turn_right ebab +turn_sharp_left eba7 +turn_sharp_right ebaa +turn_slight_left eba4 +turn_slight_right eb9a +turned_in e8e6 +turned_in_not e8e7 +tv e333 +tv_off e647 +two_wheeler e9f9 +u_turn_left eba1 +u_turn_right eba2 +umbrella f1ad +unarchive e169 +undo e166 +unfold_less e5d6 +unfold_more e5d7 +unpublished f236 +unsubscribe e0eb +upcoming f07e +update e923 +update_disabled e075 +upgrade f0fb +upload f09b +upload_file e9fc +usb e1e0 +usb_off e4fa +vaccines e138 +vape_free ebc6 +vaping_rooms ebcf +verified ef76 +verified_user e8e8 +vertical_align_bottom e258 +vertical_align_center e259 +vertical_align_top e25a +vertical_distribute e076 +vertical_split e949 +vibration e62d +video_call e070 +video_camera_back f07f +video_camera_front f080 +video_collection e04a +video_file eb87 +video_label e071 +video_library e04a +video_settings ea75 +video_stable f081 +videocam e04b +videocam_off e04c +videogame_asset e338 +videogame_asset_off e500 +view_agenda e8e9 +view_array e8ea +view_carousel e8eb +view_column e8ec +view_comfortable e42a +view_comfy e42a +view_comfy_alt eb73 +view_compact e42b +view_compact_alt eb74 +view_cozy eb75 +view_day e8ed +view_headline e8ee +view_in_ar e9fe +view_kanban eb7f +view_list e8ef +view_module e8f0 +view_quilt e8f1 +view_sidebar f114 +view_stream e8f2 +view_timeline eb85 +view_week e8f3 +vignette e435 +villa e586 +visibility e8f4 +visibility_off e8f5 +voice_chat e62e +voice_over_off e94a +voicemail e0d9 +volcano ebda +volume_down e04d +volume_down_alt e79c +volume_mute e04e +volume_off e04f +volume_up e050 +volunteer_activism ea70 +vpn_key e0da +vpn_key_off eb7a +vpn_lock e62f +vrpano f082 +wallet_giftcard e8f6 +wallet_membership e8f7 +wallet_travel e8f8 +wallpaper e1bc +warehouse ebb8 +warning e002 +warning_amber f083 +wash f1b1 +watch e334 +watch_later e924 +watch_off eae3 +water f084 +water_damage f203 +water_drop e798 +waterfall_chart ea00 +waves e176 +waving_hand e766 +wb_auto e42c +wb_cloudy e42d +wb_incandescent e42e +wb_iridescent e436 +wb_shade ea01 +wb_sunny e430 +wb_twighlight ea02 +wb_twilight e1c6 +wc e63d +web e051 +web_asset e069 +web_asset_off e4f7 +web_stories e595 +webhook eb92 +wechat ea81 +weekend e16b +west f1e6 +whatsapp ea9c +whatshot e80e +wheelchair_pickup f1ab +where_to_vote e177 +widgets e1bd +wifi e63e +wifi_1_bar e4ca +wifi_2_bar e4d9 +wifi_calling ef77 +wifi_calling_3 f085 +wifi_channel eb6a +wifi_find eb31 +wifi_lock e1e1 +wifi_off e648 +wifi_password eb6b +wifi_protected_setup f0fc +wifi_tethering e1e2 +wifi_tethering_error ead9 +wifi_tethering_error_rounded f086 +wifi_tethering_off f087 +window f088 +wine_bar f1e8 +woman e13e +woo_commerce ea6d +wordpress ea9f +work e8f9 +work_off e942 +work_outline e943 +workspace_premium e7af +workspaces e1a0 +workspaces_filled ea0d +workspaces_outline ea0f +wrap_text e25b +wrong_location ef78 +wysiwyg f1c3 +yard f089 +youtube_searched_for e8fa +zoom_in e8ff +zoom_in_map eb2d +zoom_out e900 +zoom_out_map e56b diff --git a/ui-ngx/src/app/core/services/utils.service.ts b/ui-ngx/src/app/core/services/utils.service.ts index f8a4ad279b..bb74a5f2e3 100644 --- a/ui-ngx/src/app/core/services/utils.service.ts +++ b/ui-ngx/src/app/core/services/utils.service.ts @@ -41,7 +41,7 @@ import { alarmFields } from '@shared/models/alarm.models'; import { materialColors } from '@app/shared/models/material.models'; import { WidgetInfo } from '@home/models/widget-component.models'; import jsonSchemaDefaults from 'json-schema-defaults'; -import materialIconsCodepoints from '!raw-loader!material-design-icons/iconfont/codepoints'; +import materialIconsCodepoints from '!raw-loader!./material-icons-codepoints.raw'; import { Observable, of, ReplaySubject } from 'rxjs'; const i18nRegExp = new RegExp(`{${i18nPrefix}:[^{}]+}`, 'g'); diff --git a/ui-ngx/src/assets/fonts/MaterialIcons-Regular.ttf b/ui-ngx/src/assets/fonts/MaterialIcons-Regular.ttf new file mode 100644 index 0000000000000000000000000000000000000000..be4be29c8664ae8199bd0fa6c8df9e8e140d354f GIT binary patch literal 337868 zcmb@v2Y3}#*SEiB=Iq%+Z%Kgk(3Bzyf>JElP_Ro;5d{P*h$xB`#fG4$h=7P8mTd(BP+pZEFR|Mgwhmvi0u&FtA_?X}kInKS1E#1oMk zGC~3wa_-sZbT*Zwn~0Z0Y5KXBT-;5Pr84^w`&G~F*5l0N%l14hazhJ|f_fKsJHB=0 zp$+}}5R@f<(tMhb$%euDiW>^HjcmOJh)x@^xCBEefC zHRlbwwST|rkIsEcB&n}R&W*S9yL(79$GepJCs03oaKBrwODk(TLd3^j@7^J|4!z^> zNNLUXC3?KvHV>Qp*rdwLmD$GIRZ;!%c?EI|WydBCxO&l*UbfULKdQBLI-R6tT*B=1 z=kOys&VTu#t{)fsa`;fZ9XsQwnnB#3m*}W`SpIffKN_}}$$1>&Cam=f&4d9=j5pu%e!xgs0rAJ$q zEyP8v^uilnq7vJEWl{OIC+RY+eA}1mGO>I+kjsR~#`P0N)iIJP&C1I|wnQ$JesYIg zC%4N@Yz>kw(qC?s!7@~iqmS$42Dy{cejM)^*BVMaJ)SMC@cRk;K3O`)C3nPnJ1wr` zXzTcBwsc_oKgY;EY7|$aEu)Yfs|HsJxTl=#Zdx+c<+kb=H$Xs-6wW}Rl=W_H` zEET|Kz7RQB9Uk z(gW=jt8TabXK$bkq*QCUn$2ZIyfyQmGCit2XuBR9)#I*?_Sl^E6XK(~*MOQ@tKyZ+ zeslH|x2=68<0IFrZflKB{MHfFhEB}8hunn@j9XTeYA-sPt2Z#-Yfd!vxcj+^UIxZ! zO^N%|R?XPbQS`@oLVMA&PSTz7PU6!}%PPj#aoo0+IZIA0Ki*t&{yD1s>C4qQ(n3db zeN@cL)pEV+v5MCOyyspzeh;Wo{!*Xm9FDwnoG5Oo*6U0?>bS8^q&%6UmEsz@HmFS# zI6JecIYAz0PhV25Ztv*d6KJ8HcQr=Pt}P`!Xsxw076Xa9+R!8JTl>E&-qK@gyL(-Z zCd>J8J?%l;>uj}7+wwiN?#Pe&zQP|mTQ_T$WjeNQ&6JPh(MQBJb@mlMIMcorNQ?v3cwp?NQ5gd}oETC|PQY zMnwCm7uV3^I*%*S%(%q$ND+0kmkNt7i0x6Ej<&+J)*f{&8}lCa^sLZO%HZ zd83h5PiY@LZ2m4)HNJg6mJWHTx^d)H)r-mAm#@=JuU8owY}7uGshD zTFwp~uemggd)E=P^vFxI&YeL$xU##tI+nK4+39#)SXW$M75%GSdc@7&m1z< zxHkG*YrAq+s&(B65|1I&Q9oBfD~>qt4P?I)*K9qmkxb?~>AX5HR_(i290ga}T-wAf zbTnt3D_6fe?{-49Z$0kLauR~Huf6JdnoM1_Lyzcht*icbedrq2i5|6;*6+kBIxt=bI&t^Yg?+W42PK-{6?Ujr zc-NKvzx-V>1829h*_G*bCoEPwwM6Hx+ZAzkwX{UbbgnvEUEQ>m`Y(#hbj$UnTW

?Ybv9RcK)s_@ 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 G{l5W@A9e8n literal 0 HcmV?d00001 diff --git a/ui-ngx/src/assets/fonts/material-icons.css b/ui-ngx/src/assets/fonts/material-icons.css new file mode 100644 index 0000000000..fe6f64850d --- /dev/null +++ b/ui-ngx/src/assets/fonts/material-icons.css @@ -0,0 +1,33 @@ +@font-face { + font-family: 'Material Icons'; + font-style: normal; + font-weight: 400; + src: local('Material Icons'), + local('MaterialIcons-Regular'), + url(MaterialIcons-Regular.ttf) format('truetype'); +} + +.material-icons { + font-family: 'Material Icons'; + font-weight: normal; + font-style: normal; + font-size: 24px; /* Preferred icon size */ + display: inline-block; + line-height: 1; + text-transform: none; + letter-spacing: normal; + word-wrap: normal; + white-space: nowrap; + direction: ltr; + + /* Support for all WebKit browsers. */ + -webkit-font-smoothing: antialiased; + /* Support for Safari and Chrome. */ + text-rendering: optimizeLegibility; + + /* Support for Firefox. */ + -moz-osx-font-smoothing: grayscale; + + /* Support for IE. */ + font-feature-settings: 'liga'; +} diff --git a/ui-ngx/src/index.html b/ui-ngx/src/index.html index 946d96f1b7..da8d9f6568 100644 --- a/ui-ngx/src/index.html +++ b/ui-ngx/src/index.html @@ -24,7 +24,7 @@ - + + + + + +
+ + + + + +
+ + + + + + + + + + + + +
Your 2FA verification code: ${verificationCode}
— The ThingsBoard
+
+ + + + + + +
This email was sent to ${targetEmail} by ThingsBoard.
+ + diff --git a/application/src/test/java/org/thingsboard/server/controller/TwoFactorAuthConfigTest.java b/application/src/test/java/org/thingsboard/server/controller/TwoFactorAuthConfigTest.java index f9dc02e8d3..2c4bd9fdb9 100644 --- a/application/src/test/java/org/thingsboard/server/controller/TwoFactorAuthConfigTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/TwoFactorAuthConfigTest.java @@ -54,6 +54,7 @@ import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.verify; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @@ -67,11 +68,12 @@ public abstract class TwoFactorAuthConfigTest extends AbstractControllerTest { private CacheManager cacheManager; @Autowired private TwoFaConfigManager twoFaConfigManager; - @Autowired + @SpyBean private TwoFactorAuthService twoFactorAuthService; @Before public void beforeEach() throws Exception { + doNothing().when(twoFactorAuthService).checkProvider(any(), any()); loginSysAdmin(); } diff --git a/application/src/test/java/org/thingsboard/server/controller/TwoFactorAuthTest.java b/application/src/test/java/org/thingsboard/server/controller/TwoFactorAuthTest.java index 1ad340d54b..74b95284aa 100644 --- a/application/src/test/java/org/thingsboard/server/controller/TwoFactorAuthTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/TwoFactorAuthTest.java @@ -26,6 +26,7 @@ import org.junit.Test; import org.mockito.ArgumentCaptor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.boot.test.mock.mockito.SpyBean; import org.thingsboard.rule.engine.api.SmsService; import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.audit.ActionStatus; @@ -68,6 +69,7 @@ import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.fail; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.verify; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @@ -75,7 +77,7 @@ public abstract class TwoFactorAuthTest extends AbstractControllerTest { @Autowired private TwoFaConfigManager twoFaConfigManager; - @Autowired + @SpyBean private TwoFactorAuthService twoFactorAuthService; @MockBean private SmsService smsService; @@ -100,6 +102,7 @@ public abstract class TwoFactorAuthTest extends AbstractControllerTest { loginSysAdmin(); user = createUser(user, password); + doNothing().when(twoFactorAuthService).checkProvider(any(), any()); } @After diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/StringUtils.java b/common/data/src/main/java/org/thingsboard/server/common/data/StringUtils.java index f1391555b0..17f3713b74 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/StringUtils.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/StringUtils.java @@ -15,6 +15,8 @@ */ package org.thingsboard.server.common.data; +import static org.apache.commons.lang3.StringUtils.repeat; + public class StringUtils { public static boolean isEmpty(String source) { @@ -32,4 +34,20 @@ public class StringUtils { public static boolean isNotBlank(String source) { return source != null && !source.isEmpty() && !source.trim().isEmpty(); } + + public static String obfuscate(String input, int seenMargin, char obfuscationChar, + int startIndexInclusive, int endIndexExclusive) { + + String part = input.substring(startIndexInclusive, endIndexExclusive); + String obfuscatedPart; + if (part.length() <= seenMargin * 2) { + obfuscatedPart = repeat(obfuscationChar, part.length()); + } else { + obfuscatedPart = part.substring(0, seenMargin) + + repeat(obfuscationChar, part.length() - seenMargin * 2) + + part.substring(part.length() - seenMargin); + } + return input.substring(0, startIndexInclusive) + obfuscatedPart + input.substring(endIndexExclusive); + } + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/UserAuthSettingsEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/UserAuthSettingsEntity.java index 1728fca936..bebddf5aa0 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/UserAuthSettingsEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/UserAuthSettingsEntity.java @@ -42,7 +42,7 @@ import java.util.UUID; @NoArgsConstructor @TypeDef(name = "json", typeClass = JsonStringType.class) @Entity -@Table(name = ModelConstants.USER_AUTH_SETTINGS_COLUMN_FAMILY_NAME) // FIXME [viacheslav]: add to upgrade script +@Table(name = ModelConstants.USER_AUTH_SETTINGS_COLUMN_FAMILY_NAME) public class UserAuthSettingsEntity extends BaseSqlEntity implements BaseEntity { @Column(name = ModelConstants.USER_AUTH_SETTINGS_USER_ID_PROPERTY, nullable = false, unique = true) diff --git a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/MailService.java b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/MailService.java index 10c37da564..236391ba16 100644 --- a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/MailService.java +++ b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/MailService.java @@ -28,6 +28,8 @@ public interface MailService { void updateMailConfiguration(); + boolean isConfigured(TenantId tenantId); + void sendEmail(TenantId tenantId, String email, String subject, String message) throws ThingsboardException; void sendTestMail(JsonNode config, String email) throws ThingsboardException; diff --git a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/SmsService.java b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/SmsService.java index d9578783b0..c38f99ecb2 100644 --- a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/SmsService.java +++ b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/SmsService.java @@ -24,6 +24,8 @@ public interface SmsService { void updateSmsConfiguration(); + boolean isConfigured(TenantId tenantId); + void sendSms(TenantId tenantId, CustomerId customerId, String[] numbersTo, String message) throws ThingsboardException;; void sendTestSms(TestSmsRequest testSmsRequest) throws ThingsboardException; From d3863de0948063c19c41271e742cb790d211b645 Mon Sep 17 00:00:00 2001 From: Viacheslav Klimov Date: Tue, 17 May 2022 18:45:11 +0300 Subject: [PATCH 651/798] Remove platform 2FA settings for tenant authority --- .../controller/TwoFaConfigController.java | 7 +- .../mfa/config/DefaultTwoFaConfigManager.java | 62 ++++---------- .../controller/TwoFactorAuthConfigTest.java | 81 ++----------------- .../server/controller/TwoFactorAuthTest.java | 7 +- .../model/mfa/PlatformTwoFaSettings.java | 1 - 5 files changed, 23 insertions(+), 135 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/controller/TwoFaConfigController.java b/application/src/main/java/org/thingsboard/server/controller/TwoFaConfigController.java index 907618c26f..2dec65ed93 100644 --- a/application/src/main/java/org/thingsboard/server/controller/TwoFaConfigController.java +++ b/application/src/main/java/org/thingsboard/server/controller/TwoFaConfigController.java @@ -206,15 +206,13 @@ public class TwoFaConfigController extends BaseController { "If 2FA is not configured, then an empty response will be returned." + ControllerConstants.SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH) @GetMapping("/settings") - @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") + @PreAuthorize("hasAnyAuthority('SYS_ADMIN')") public PlatformTwoFaSettings getPlatformTwoFaSettings() throws ThingsboardException { return twoFaConfigManager.getPlatformTwoFaSettings(getTenantId(), false).orElse(null); } @ApiOperation(value = "Save platform 2FA settings (savePlatformTwoFaSettings)", notes = "Save 2FA settings for platform. The settings have following properties:\n" + - "- `useSystemTwoFactorAuthSettings` - option for tenant admins to use 2FA settings configured by sysadmin. " + - "If this param is set to true, then the settings will not be validated for constraints (if it is a tenant admin; for sysadmin this param is ignored).\n" + "- `providers` - the list of 2FA providers' configs. Users will only be allowed to use 2FA providers from this list. \n\n" + "- `verificationCodeSendRateLimit` - rate limit configuration for verification code sending. " + "The format is standard: 'amountOfRequests:periodInSeconds'. The value of '1:60' would limit verification " + @@ -233,7 +231,6 @@ public class TwoFaConfigController extends BaseController { "- `verificationCodeLifetime` - the same as for SMS." + NEW_LINE + "Example of the settings:\n" + "```\n{\n" + - " \"useSystemTwoFactorAuthSettings\": false,\n" + " \"providers\": [\n" + " {\n" + " \"providerType\": \"TOTP\",\n" + @@ -256,7 +253,7 @@ public class TwoFaConfigController extends BaseController { "}\n```" + ControllerConstants.SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH) @PostMapping("/settings") - @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") + @PreAuthorize("hasAnyAuthority('SYS_ADMIN')") public void savePlatformTwoFaSettings(@ApiParam(value = "Settings value", required = true) @RequestBody PlatformTwoFaSettings twoFaSettings) throws ThingsboardException { twoFaConfigManager.savePlatformTwoFaSettings(getTenantId(), twoFaSettings); diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/mfa/config/DefaultTwoFaConfigManager.java b/application/src/main/java/org/thingsboard/server/service/security/auth/mfa/config/DefaultTwoFaConfigManager.java index 8227a3596a..e27d3967ad 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/mfa/config/DefaultTwoFaConfigManager.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/mfa/config/DefaultTwoFaConfigManager.java @@ -16,18 +16,14 @@ package org.thingsboard.server.service.security.auth.mfa.config; import lombok.RequiredArgsConstructor; -import lombok.SneakyThrows; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.AdminSettings; -import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.UserId; -import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; -import org.thingsboard.server.common.data.kv.JsonDataEntry; import org.thingsboard.server.common.data.security.UserAuthSettings; import org.thingsboard.server.common.data.security.model.mfa.PlatformTwoFaSettings; import org.thingsboard.server.common.data.security.model.mfa.account.AccountTwoFaSettings; @@ -41,11 +37,9 @@ import org.thingsboard.server.dao.settings.AdminSettingsService; import org.thingsboard.server.dao.user.UserAuthSettingsDao; import org.thingsboard.server.service.security.auth.mfa.TwoFactorAuthService; -import java.util.Collections; import java.util.Comparator; import java.util.LinkedHashMap; import java.util.Optional; -import java.util.concurrent.ExecutionException; @Service @RequiredArgsConstructor @@ -54,7 +48,6 @@ public class DefaultTwoFaConfigManager implements TwoFaConfigManager { private final UserAuthSettingsDao userAuthSettingsDao; private final AdminSettingsService adminSettingsService; private final AdminSettingsDao adminSettingsDao; - private final AttributesService attributesService; @Autowired @Lazy private TwoFactorAuthService twoFactorAuthService; @@ -132,60 +125,33 @@ public class DefaultTwoFaConfigManager implements TwoFaConfigManager { .flatMap(twoFaSettings -> twoFaSettings.getProviderConfig(providerType)); } - @SneakyThrows({InterruptedException.class, ExecutionException.class}) @Override public Optional getPlatformTwoFaSettings(TenantId tenantId, boolean sysadminSettingsAsDefault) { - if (tenantId.equals(TenantId.SYS_TENANT_ID)) { - return Optional.ofNullable(adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, TWO_FACTOR_AUTH_SETTINGS_KEY)) - .map(adminSettings -> JacksonUtil.treeToValue(adminSettings.getJsonValue(), PlatformTwoFaSettings.class)); - } else { - Optional tenantTwoFaSettings = attributesService.find(TenantId.SYS_TENANT_ID, tenantId, - DataConstants.SERVER_SCOPE, TWO_FACTOR_AUTH_SETTINGS_KEY).get() - .map(adminSettingsAttribute -> JacksonUtil.fromString(adminSettingsAttribute.getJsonValue().get(), PlatformTwoFaSettings.class)); - if (sysadminSettingsAsDefault) { - if (tenantTwoFaSettings.isEmpty() || tenantTwoFaSettings.get().isUseSystemTwoFactorAuthSettings()) { - return getPlatformTwoFaSettings(TenantId.SYS_TENANT_ID, false); - } - } - return tenantTwoFaSettings; - } + return Optional.ofNullable(adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, TWO_FACTOR_AUTH_SETTINGS_KEY)) + .map(adminSettings -> JacksonUtil.treeToValue(adminSettings.getJsonValue(), PlatformTwoFaSettings.class)); } - @SneakyThrows({InterruptedException.class, ExecutionException.class}) @Override public void savePlatformTwoFaSettings(TenantId tenantId, PlatformTwoFaSettings twoFactorAuthSettings) throws ThingsboardException { - if (tenantId.equals(TenantId.SYS_TENANT_ID) || !twoFactorAuthSettings.isUseSystemTwoFactorAuthSettings()) { - ConstraintValidator.validateFields(twoFactorAuthSettings); - } + ConstraintValidator.validateFields(twoFactorAuthSettings); for (TwoFaProviderConfig providerConfig : twoFactorAuthSettings.getProviders()) { twoFactorAuthService.checkProvider(tenantId, providerConfig.getProviderType()); } - if (tenantId.equals(TenantId.SYS_TENANT_ID)) { - AdminSettings settings = Optional.ofNullable(adminSettingsService.findAdminSettingsByKey(tenantId, TWO_FACTOR_AUTH_SETTINGS_KEY)) - .orElseGet(() -> { - AdminSettings newSettings = new AdminSettings(); - newSettings.setKey(TWO_FACTOR_AUTH_SETTINGS_KEY); - return newSettings; - }); - settings.setJsonValue(JacksonUtil.valueToTree(twoFactorAuthSettings)); - adminSettingsService.saveAdminSettings(tenantId, settings); - } else { - attributesService.save(TenantId.SYS_TENANT_ID, tenantId, DataConstants.SERVER_SCOPE, Collections.singletonList( - new BaseAttributeKvEntry(new JsonDataEntry(TWO_FACTOR_AUTH_SETTINGS_KEY, JacksonUtil.toString(twoFactorAuthSettings)), System.currentTimeMillis()) - )).get(); - } + + AdminSettings settings = Optional.ofNullable(adminSettingsService.findAdminSettingsByKey(tenantId, TWO_FACTOR_AUTH_SETTINGS_KEY)) + .orElseGet(() -> { + AdminSettings newSettings = new AdminSettings(); + newSettings.setKey(TWO_FACTOR_AUTH_SETTINGS_KEY); + return newSettings; + }); + settings.setJsonValue(JacksonUtil.valueToTree(twoFactorAuthSettings)); + adminSettingsService.saveAdminSettings(tenantId, settings); } - @SneakyThrows({InterruptedException.class, ExecutionException.class}) @Override public void deletePlatformTwoFaSettings(TenantId tenantId) { - if (tenantId.equals(TenantId.SYS_TENANT_ID)) { - Optional.ofNullable(adminSettingsService.findAdminSettingsByKey(tenantId, TWO_FACTOR_AUTH_SETTINGS_KEY)) - .ifPresent(adminSettings -> adminSettingsDao.removeById(tenantId, adminSettings.getId().getId())); - } else { - attributesService.removeAll(TenantId.SYS_TENANT_ID, tenantId, DataConstants.SERVER_SCOPE, - Collections.singletonList(TWO_FACTOR_AUTH_SETTINGS_KEY)).get(); - } + Optional.ofNullable(adminSettingsService.findAdminSettingsByKey(tenantId, TWO_FACTOR_AUTH_SETTINGS_KEY)) + .ifPresent(adminSettings -> adminSettingsDao.removeById(tenantId, adminSettings.getId().getId())); } } diff --git a/application/src/test/java/org/thingsboard/server/controller/TwoFactorAuthConfigTest.java b/application/src/test/java/org/thingsboard/server/controller/TwoFactorAuthConfigTest.java index 2c4bd9fdb9..d6fc640878 100644 --- a/application/src/test/java/org/thingsboard/server/controller/TwoFactorAuthConfigTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/TwoFactorAuthConfigTest.java @@ -30,10 +30,8 @@ import org.springframework.web.util.UriComponentsBuilder; import org.thingsboard.rule.engine.api.SmsService; import org.thingsboard.server.common.data.CacheConstants; import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.security.model.mfa.account.AccountTwoFaSettings; -import org.thingsboard.server.service.security.auth.mfa.TwoFactorAuthService; -import org.thingsboard.server.service.security.auth.mfa.config.TwoFaConfigManager; import org.thingsboard.server.common.data.security.model.mfa.PlatformTwoFaSettings; +import org.thingsboard.server.common.data.security.model.mfa.account.AccountTwoFaSettings; import org.thingsboard.server.common.data.security.model.mfa.account.SmsTwoFaAccountConfig; import org.thingsboard.server.common.data.security.model.mfa.account.TotpTwoFaAccountConfig; import org.thingsboard.server.common.data.security.model.mfa.account.TwoFaAccountConfig; @@ -41,6 +39,8 @@ import org.thingsboard.server.common.data.security.model.mfa.provider.SmsTwoFaPr import org.thingsboard.server.common.data.security.model.mfa.provider.TotpTwoFaProviderConfig; import org.thingsboard.server.common.data.security.model.mfa.provider.TwoFaProviderConfig; import org.thingsboard.server.common.data.security.model.mfa.provider.TwoFaProviderType; +import org.thingsboard.server.service.security.auth.mfa.TwoFactorAuthService; +import org.thingsboard.server.service.security.auth.mfa.config.TwoFaConfigManager; import org.thingsboard.server.service.security.auth.mfa.provider.impl.OtpBasedTwoFaProvider; import org.thingsboard.server.service.security.auth.mfa.provider.impl.TotpTwoFaProvider; @@ -85,15 +85,9 @@ public abstract class TwoFactorAuthConfigTest extends AbstractControllerTest { @Test - public void testSavePlatformTwoFaSettingsForDifferentAuthorities() throws Exception { + public void testSavePlatformTwoFaSettings() throws Exception { loginSysAdmin(); - testSavePlatformTwoFaSettings(); - - loginTenantAdmin(); - testSavePlatformTwoFaSettings(); - } - private void testSavePlatformTwoFaSettings() throws Exception { TotpTwoFaProviderConfig totpTwoFaProviderConfig = new TotpTwoFaProviderConfig(); totpTwoFaProviderConfig.setIssuerName("tb"); SmsTwoFaProviderConfig smsTwoFaProviderConfig = new SmsTwoFaProviderConfig(); @@ -117,7 +111,7 @@ public abstract class TwoFactorAuthConfigTest extends AbstractControllerTest { @Test public void testSavePlatformTwoFaSettings_validationError() throws Exception { - loginTenantAdmin(); + loginSysAdmin(); PlatformTwoFaSettings twoFaSettings = new PlatformTwoFaSettings(); twoFaSettings.setProviders(Collections.emptyList()); @@ -135,71 +129,6 @@ public abstract class TwoFactorAuthConfigTest extends AbstractControllerTest { "maximum number of verification failure before user lockout must be positive", "total amount of time allotted for verification must be greater than 0" ); - - twoFaSettings.setUseSystemTwoFactorAuthSettings(true); - doPost("/api/2fa/settings", twoFaSettings) - .andExpect(status().isOk()); - - twoFaSettings.setVerificationCodeSendRateLimit(null); - twoFaSettings.setVerificationCodeCheckRateLimit(null); - twoFaSettings.setMaxVerificationFailuresBeforeUserLockout(0); - twoFaSettings.setTotalAllowedTimeForVerification(null); - - doPost("/api/2fa/settings", twoFaSettings) - .andExpect(status().isOk()); - } - - @Test - public void testGetPlatformTwoFaSettings_useSysadminSettingsAsDefault() throws Exception { - loginSysAdmin(); - PlatformTwoFaSettings sysadminTwoFaSettings = new PlatformTwoFaSettings(); - TotpTwoFaProviderConfig totpTwoFaProviderConfig = new TotpTwoFaProviderConfig(); - totpTwoFaProviderConfig.setIssuerName("tb"); - sysadminTwoFaSettings.setProviders(Collections.singletonList(totpTwoFaProviderConfig)); - sysadminTwoFaSettings.setMaxVerificationFailuresBeforeUserLockout(25); - doPost("/api/2fa/settings", sysadminTwoFaSettings).andExpect(status().isOk()); - - loginTenantAdmin(); - PlatformTwoFaSettings tenantTwoFaSettings = new PlatformTwoFaSettings(); - tenantTwoFaSettings.setUseSystemTwoFactorAuthSettings(true); - tenantTwoFaSettings.setProviders(Collections.emptyList()); - doPost("/api/2fa/settings", tenantTwoFaSettings).andExpect(status().isOk()); - PlatformTwoFaSettings twoFaSettings = readResponse(doGet("/api/2fa/settings").andExpect(status().isOk()), PlatformTwoFaSettings.class); - assertThat(twoFaSettings).isEqualTo(tenantTwoFaSettings); - - doPost("/api/2fa/account/config/generate?providerType=TOTP") - .andExpect(status().isOk()); - - tenantTwoFaSettings.setUseSystemTwoFactorAuthSettings(false); - tenantTwoFaSettings.setProviders(Collections.emptyList()); - tenantTwoFaSettings.setMaxVerificationFailuresBeforeUserLockout(10); - doPost("/api/2fa/settings", tenantTwoFaSettings).andExpect(status().isOk()); - twoFaSettings = readResponse(doGet("/api/2fa/settings").andExpect(status().isOk()), PlatformTwoFaSettings.class); - assertThat(twoFaSettings).isEqualTo(tenantTwoFaSettings); - - assertThat(getErrorMessage(doPost("/api/2fa/account/config/generate?providerType=TOTP") - .andExpect(status().isBadRequest()))).containsIgnoringCase("provider is not configured"); - - loginSysAdmin(); - sysadminTwoFaSettings.setProviders(Collections.emptyList()); - doPost("/api/2fa/settings", sysadminTwoFaSettings).andExpect(status().isOk()); - loginTenantAdmin(); - tenantTwoFaSettings.setUseSystemTwoFactorAuthSettings(true); - tenantTwoFaSettings.setProviders(Collections.singletonList(totpTwoFaProviderConfig)); - doPost("/api/2fa/settings", tenantTwoFaSettings).andExpect(status().isOk()); - - assertThat(getErrorMessage(doPost("/api/2fa/account/config/generate?providerType=TOTP") - .andExpect(status().isBadRequest()))).containsIgnoringCase("provider is not configured"); - - tenantTwoFaSettings.setUseSystemTwoFactorAuthSettings(false); - doPost("/api/2fa/settings", tenantTwoFaSettings).andExpect(status().isOk()); - - doPost("/api/2fa/account/config/generate?providerType=TOTP") - .andExpect(status().isOk()); - - loginSysAdmin(); - twoFaSettings = readResponse(doGet("/api/2fa/settings").andExpect(status().isOk()), PlatformTwoFaSettings.class); - assertThat(twoFaSettings).isEqualTo(sysadminTwoFaSettings); } @Test diff --git a/application/src/test/java/org/thingsboard/server/controller/TwoFactorAuthTest.java b/application/src/test/java/org/thingsboard/server/controller/TwoFactorAuthTest.java index 74b95284aa..40070b0106 100644 --- a/application/src/test/java/org/thingsboard/server/controller/TwoFactorAuthTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/TwoFactorAuthTest.java @@ -345,7 +345,6 @@ public abstract class TwoFactorAuthTest extends AbstractControllerTest { @Test public void testTwoFa_multipleProviders() throws Exception { PlatformTwoFaSettings platformTwoFaSettings = new PlatformTwoFaSettings(); - platformTwoFaSettings.setUseSystemTwoFactorAuthSettings(true); TotpTwoFaProviderConfig totpTwoFaProviderConfig = new TotpTwoFaProviderConfig(); totpTwoFaProviderConfig.setIssuerName("TB"); @@ -411,10 +410,9 @@ public abstract class TwoFactorAuthTest extends AbstractControllerTest { totpTwoFaProviderConfig.setIssuerName("tb"); PlatformTwoFaSettings twoFaSettings = new PlatformTwoFaSettings(); - twoFaSettings.setUseSystemTwoFactorAuthSettings(false); twoFaSettings.setProviders(Arrays.stream(new TwoFaProviderConfig[]{totpTwoFaProviderConfig}).collect(Collectors.toList())); Arrays.stream(customizer).forEach(c -> c.accept(twoFaSettings)); - twoFaConfigManager.savePlatformTwoFaSettings(tenantId, twoFaSettings); + twoFaConfigManager.savePlatformTwoFaSettings(TenantId.SYS_TENANT_ID, twoFaSettings); TotpTwoFaAccountConfig totpTwoFaAccountConfig = (TotpTwoFaAccountConfig) twoFactorAuthService.generateNewAccountConfig(user, TwoFaProviderType.TOTP); twoFaConfigManager.saveTwoFaAccountConfig(tenantId, user.getId(), totpTwoFaAccountConfig); @@ -428,9 +426,8 @@ public abstract class TwoFactorAuthTest extends AbstractControllerTest { Arrays.stream(customizer).forEach(c -> c.accept(smsTwoFaProviderConfig)); PlatformTwoFaSettings twoFaSettings = new PlatformTwoFaSettings(); - twoFaSettings.setUseSystemTwoFactorAuthSettings(false); twoFaSettings.setProviders(Arrays.stream(new TwoFaProviderConfig[]{smsTwoFaProviderConfig}).collect(Collectors.toList())); - twoFaConfigManager.savePlatformTwoFaSettings(tenantId, twoFaSettings); + twoFaConfigManager.savePlatformTwoFaSettings(TenantId.SYS_TENANT_ID, twoFaSettings); SmsTwoFaAccountConfig smsTwoFaAccountConfig = new SmsTwoFaAccountConfig(); smsTwoFaAccountConfig.setPhoneNumber("+38050505050"); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/security/model/mfa/PlatformTwoFaSettings.java b/common/data/src/main/java/org/thingsboard/server/common/data/security/model/mfa/PlatformTwoFaSettings.java index 8d017cab20..39ca0349fb 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/security/model/mfa/PlatformTwoFaSettings.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/security/model/mfa/PlatformTwoFaSettings.java @@ -28,7 +28,6 @@ import java.util.Optional; @Data public class PlatformTwoFaSettings { - private boolean useSystemTwoFactorAuthSettings; @Valid private List providers; From c61a50afe5ea6a94b46699d2e32f0023cd8a52ba Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Tue, 17 May 2022 23:45:07 +0300 Subject: [PATCH 652/798] refactoring: - dashboard comments3 --- .../entitiy/DefaultTbNotificationEntityService.java | 12 ++++++------ .../service/entitiy/TbNotificationEntityService.java | 8 ++++---- .../service/entitiy/alarm/DefaultTbAlarmService.java | 2 +- .../service/entitiy/asset/DefaultTbAssetService.java | 4 ++-- .../entitiy/customer/DefaultTbCustomerService.java | 5 +++-- .../entitiy/dashboard/DefaultTbDashboardService.java | 2 +- .../service/entitiy/edge/DefaultTbEdgeService.java | 4 ++-- 7 files changed, 19 insertions(+), 18 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/DefaultTbNotificationEntityService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/DefaultTbNotificationEntityService.java index 1f32063299..e421677e84 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/DefaultTbNotificationEntityService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/DefaultTbNotificationEntityService.java @@ -75,11 +75,11 @@ public class DefaultTbNotificationEntityService implements TbNotificationEntityS sendDeleteNotificationMsg(tenantId, entityId, entity, relatedEdgeIds); } - public void notifyDeleteEntityAlarm(TenantId tenantId, I entityId, E entity, EntityId originatorId, - CustomerId customerId, ActionType actionType, - List relatedEdgeIds, - SecurityUser user, - String body, Object... additionalInfo) { + public void notifyDeleteAlarm(TenantId tenantId, I entityId, E entity, EntityId originatorId, + CustomerId customerId, ActionType actionType, + List relatedEdgeIds, + SecurityUser user, + String body, Object... additionalInfo) { logEntityAction(tenantId, originatorId, entity, customerId, actionType, user, additionalInfo); sendAlarmDeleteNotificationMsg(tenantId, entityId, entity, relatedEdgeIds, body); } @@ -134,7 +134,7 @@ public class DefaultTbNotificationEntityService implements TbNotificationEntityS gatewayNotificationsService.onDeviceDeleted(device); tbClusterService.onDeviceDeleted(device, null); - notifyDeleteEntity(tenantId, deviceId, device, customerId, ActionType.DELETED, relatedEdgeIds, user,null, additionalInfo); + notifyDeleteEntity(tenantId, deviceId, device, customerId, ActionType.DELETED, relatedEdgeIds, user, additionalInfo); } @Override diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/TbNotificationEntityService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/TbNotificationEntityService.java index 93a8b66749..ec8717ef7b 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/TbNotificationEntityService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/TbNotificationEntityService.java @@ -48,10 +48,10 @@ public interface TbNotificationEntityService { List relatedEdgeIds, SecurityUser user, Object... additionalInfo); - void notifyDeleteEntityAlarm(TenantId tenantId, I entityId, E entity, EntityId originatorId, - CustomerId customerId, ActionType actionType, - List relatedEdgeIds, - SecurityUser user, String body, Object... additionalInfo); + void notifyDeleteAlarm(TenantId tenantId, I entityId, E entity, EntityId originatorId, + CustomerId customerId, ActionType actionType, + List relatedEdgeIds, + SecurityUser user, String body, Object... additionalInfo); void notifyAssignOrUnassignEntityToCustomer(TenantId tenantId, I entityId, CustomerId customerId, E entity, diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/alarm/DefaultTbAlarmService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/alarm/DefaultTbAlarmService.java index 44f84a5e3b..bcf0215bac 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/alarm/DefaultTbAlarmService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/alarm/DefaultTbAlarmService.java @@ -80,7 +80,7 @@ public class DefaultTbAlarmService extends AbstractTbEntityService implements Tb public Boolean delete(Alarm alarm, SecurityUser user) throws ThingsboardException { try { List relatedEdgeIds = findRelatedEdgeIds(user.getTenantId(), alarm.getOriginator()); - notificationEntityService.notifyDeleteEntityAlarm(user.getTenantId(), alarm.getId(), alarm, alarm.getOriginator(), user.getCustomerId(), + notificationEntityService.notifyDeleteAlarm(user.getTenantId(), alarm.getId(), alarm, alarm.getOriginator(), user.getCustomerId(), ActionType.DELETED, relatedEdgeIds, user, JacksonUtil.OBJECT_MAPPER.writeValueAsString(alarm)); return alarmService.deleteAlarm(user.getTenantId(), alarm.getId()).isSuccessful(); } catch (Exception e) { diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/asset/DefaultTbAssetService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/asset/DefaultTbAssetService.java index 5d1556fd7d..4be68b75f9 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/asset/DefaultTbAssetService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/asset/DefaultTbAssetService.java @@ -62,12 +62,12 @@ public class DefaultTbAssetService extends AbstractTbEntityService implements Tb List relatedEdgeIds = findRelatedEdgeIds(tenantId, assetId); assetService.deleteAsset(tenantId, assetId); notificationEntityService.notifyDeleteEntity(tenantId, assetId, asset, asset.getCustomerId(), ActionType.DELETED, - relatedEdgeIds, user, null, asset.toString()); + relatedEdgeIds, user, assetId.toString()); return removeAlarmsByEntityId(tenantId, assetId); } catch (Exception e) { notificationEntityService.notifyEntity(tenantId, emptyId(EntityType.ASSET), null, null, - ActionType.DELETED, user, e, asset.toString()); + ActionType.DELETED, user, e, assetId.toString()); throw handleException(e); } } diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/customer/DefaultTbCustomerService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/customer/DefaultTbCustomerService.java index af27daa5db..8f13b10cc5 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/customer/DefaultTbCustomerService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/customer/DefaultTbCustomerService.java @@ -62,10 +62,11 @@ public class DefaultTbCustomerService extends AbstractTbEntityService implements List relatedEdgeIds = findRelatedEdgeIds(tenantId, customerId); customerService.deleteCustomer(tenantId, customerId); notificationEntityService.notifyDeleteEntity(tenantId, customerId, customer, customerId, - ActionType.DELETED, relatedEdgeIds, user, null); + ActionType.DELETED, relatedEdgeIds, user, customerId.toString()); tbClusterService.broadcastEntityStateChangeEvent(tenantId, customerId, ComponentLifecycleEvent.DELETED); } catch (Exception e) { - notificationEntityService.notifyEntity(tenantId, emptyId(EntityType.CUSTOMER), null, null, ActionType.DELETED, user, e); + notificationEntityService.notifyEntity(tenantId, emptyId(EntityType.CUSTOMER), null, null, + ActionType.DELETED, user, e, customerId.toString()); throw handleException(e); } } diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/dashboard/DefaultTbDashboardService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/dashboard/DefaultTbDashboardService.java index 5ea27f9edc..a1bf4baf6b 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/dashboard/DefaultTbDashboardService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/dashboard/DefaultTbDashboardService.java @@ -65,7 +65,7 @@ public class DefaultTbDashboardService extends AbstractTbEntityService implement List relatedEdgeIds = findRelatedEdgeIds(tenantId, dashboardId); dashboardService.deleteDashboard(tenantId, dashboardId); notificationEntityService.notifyDeleteEntity(tenantId, dashboardId, dashboard, user.getCustomerId(), - ActionType.DELETED, relatedEdgeIds, user, null); + ActionType.DELETED, relatedEdgeIds, user, dashboardId.toString()); } catch (Exception e) { notificationEntityService.notifyEntity(tenantId, emptyId(EntityType.DASHBOARD), null, null, ActionType.DELETED, user, e, dashboardId.toString()); diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/edge/DefaultTbEdgeService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/edge/DefaultTbEdgeService.java index 5547c63375..dca6869cb9 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/edge/DefaultTbEdgeService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/edge/DefaultTbEdgeService.java @@ -69,9 +69,9 @@ public class DefaultTbEdgeService extends AbstractTbEntityService implements TbE try { edgeService.deleteEdge(tenantId, edgeId); notificationEntityService.notifyEdge(tenantId, edgeId, edge.getCustomerId(), edge, actionType, user, edgeId.toString()); - } catch (Exception e) { - notificationEntityService.notifyEntity(tenantId, emptyId(EntityType.EDGE), edge, null, actionType, user, e); + notificationEntityService.notifyEntity(tenantId, emptyId(EntityType.EDGE), edge, null, actionType, + user, e, edgeId.toString()); throw handleException(e); } } From 4e8be657ea1bafb1775515c0d577bd95bfb2f57e Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Wed, 18 May 2022 10:27:59 +0300 Subject: [PATCH 653/798] UI: Add mail template - Email verification code; refactoring --- .../templates/2fa.verification.code.ftl | 120 ++++++++---------- .../two-factor-auth-settings.component.html | 2 +- .../assets/locale/locale.constant-en_US.json | 2 +- 3 files changed, 53 insertions(+), 71 deletions(-) diff --git a/application/src/main/resources/templates/2fa.verification.code.ftl b/application/src/main/resources/templates/2fa.verification.code.ftl index 2b205ecb5d..0aba62f90b 100644 --- a/application/src/main/resources/templates/2fa.verification.code.ftl +++ b/application/src/main/resources/templates/2fa.verification.code.ftl @@ -20,81 +20,42 @@ - - - Thingsboard - Api Usage State + + + Email verification code @@ -103,33 +64,54 @@ style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; width: 100% !important; height: 100%; line-height: 1.6em; background-color: #f6f6f6; margin: 0;" bgcolor="#f6f6f6"> - - - -
- - - - - - - - - - - - -
Your 2FA verification code: ${verificationCode}
— The ThingsBoard
+ + + - -
+
+ + +
+ + + + + + + + + + + + + + + + + + +
+

Email verification code:

+
+

${verificationCode}

+
+ Please verify your access using the code above. +
+ This code will expire in ${expiredTime} minutes. To get a new one, click Resend code on the Thingsboard platform. +
+ If you didn't request for this code, then you can just ignore this email; access won't provided. +
+ — The Thingsboard +
+ +
- - - - - -
This email was sent to ${targetEmail} by ThingsBoard.
diff --git a/ui-ngx/src/app/modules/home/pages/admin/two-factor-auth-settings.component.html b/ui-ngx/src/app/modules/home/pages/admin/two-factor-auth-settings.component.html index 656f6d7c3b..f3e850a8d7 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/two-factor-auth-settings.component.html +++ b/ui-ngx/src/app/modules/home/pages/admin/two-factor-auth-settings.component.html @@ -32,7 +32,7 @@
- admin.2fa.general-setting + admin.2fa.verification-limitations
admin.2fa.total-allowed-time-for-verification diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index 80064c88f9..98b4df1c2b 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -315,7 +315,6 @@ "2fa": { "2fa": "Two-factor authentication", "available-providers": "Available providers", - "general-setting": "General setting", "issuer-name": "Issuer name", "issuer-name-required": "Issuer name is required.", "max-verification-failures-before-user-lockout": "Max verification failures before user lockout", @@ -338,6 +337,7 @@ "verification-code-lifetime-required": "Verification code lifetime is required.", "verification-code-send-rate-limit": "Verification code send rate limit", "verification-message-template": "Verification message template", + "verification-limitations": "Verification limitations", "verification-message-template-pattern": "Verification message need to contains pattern: ${verificationCode}", "verification-message-template-required": "Verification message template is required.", "within-time": "Within time (sec)", From 647012c2d4bb848365d255fd8a7dcfbe2d9c05cf Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Wed, 18 May 2022 11:14:31 +0300 Subject: [PATCH 654/798] refactoring: - dashboard comments4 --- .../DefaultTbNotificationEntityService.java | 14 +++++++------- .../entitiy/TbNotificationEntityService.java | 2 +- .../entitiy/alarm/DefaultTbAlarmService.java | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/DefaultTbNotificationEntityService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/DefaultTbNotificationEntityService.java index e421677e84..62b020ac32 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/DefaultTbNotificationEntityService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/DefaultTbNotificationEntityService.java @@ -75,13 +75,13 @@ public class DefaultTbNotificationEntityService implements TbNotificationEntityS sendDeleteNotificationMsg(tenantId, entityId, entity, relatedEdgeIds); } - public void notifyDeleteAlarm(TenantId tenantId, I entityId, E entity, EntityId originatorId, - CustomerId customerId, ActionType actionType, - List relatedEdgeIds, - SecurityUser user, - String body, Object... additionalInfo) { - logEntityAction(tenantId, originatorId, entity, customerId, actionType, user, additionalInfo); - sendAlarmDeleteNotificationMsg(tenantId, entityId, entity, relatedEdgeIds, body); + public void notifyDeleteAlarm(TenantId tenantId, Alarm alarm, EntityId originatorId, + CustomerId customerId, ActionType actionType, + List relatedEdgeIds, + SecurityUser user, + String body, Object... additionalInfo) { + logEntityAction(tenantId, originatorId, alarm, customerId, actionType, user, additionalInfo); + sendAlarmDeleteNotificationMsg(tenantId, alarm.getId(), alarm, relatedEdgeIds, body); } @Override diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/TbNotificationEntityService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/TbNotificationEntityService.java index ec8717ef7b..5ac8bbcfbf 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/TbNotificationEntityService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/TbNotificationEntityService.java @@ -48,7 +48,7 @@ public interface TbNotificationEntityService { List relatedEdgeIds, SecurityUser user, Object... additionalInfo); - void notifyDeleteAlarm(TenantId tenantId, I entityId, E entity, EntityId originatorId, + void notifyDeleteAlarm(TenantId tenantId, Alarm alarm, EntityId originatorId, CustomerId customerId, ActionType actionType, List relatedEdgeIds, SecurityUser user, String body, Object... additionalInfo); diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/alarm/DefaultTbAlarmService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/alarm/DefaultTbAlarmService.java index bcf0215bac..f00bd06de3 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/alarm/DefaultTbAlarmService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/alarm/DefaultTbAlarmService.java @@ -80,7 +80,7 @@ public class DefaultTbAlarmService extends AbstractTbEntityService implements Tb public Boolean delete(Alarm alarm, SecurityUser user) throws ThingsboardException { try { List relatedEdgeIds = findRelatedEdgeIds(user.getTenantId(), alarm.getOriginator()); - notificationEntityService.notifyDeleteAlarm(user.getTenantId(), alarm.getId(), alarm, alarm.getOriginator(), user.getCustomerId(), + notificationEntityService.notifyDeleteAlarm(user.getTenantId(), alarm, alarm.getOriginator(), user.getCustomerId(), ActionType.DELETED, relatedEdgeIds, user, JacksonUtil.OBJECT_MAPPER.writeValueAsString(alarm)); return alarmService.deleteAlarm(user.getTenantId(), alarm.getId()).isSuccessful(); } catch (Exception e) { From 866ad82187e076398e4887e0c9da1f6bfee1b7b5 Mon Sep 17 00:00:00 2001 From: Viacheslav Klimov Date: Wed, 18 May 2022 12:50:12 +0300 Subject: [PATCH 655/798] Email 2FA verification message improvements --- .../thingsboard/server/service/mail/DefaultMailService.java | 5 +++-- .../security/auth/mfa/provider/impl/EmailTwoFaProvider.java | 2 +- .../src/main/resources/templates/2fa.verification.code.ftl | 6 +++--- .../data/security/model/mfa/PlatformTwoFaSettings.java | 2 ++ .../java/org/thingsboard/rule/engine/api/MailService.java | 2 +- 5 files changed, 10 insertions(+), 7 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/mail/DefaultMailService.java b/application/src/main/java/org/thingsboard/server/service/mail/DefaultMailService.java index fdb1b579e9..9d2f7b8891 100644 --- a/application/src/main/java/org/thingsboard/server/service/mail/DefaultMailService.java +++ b/application/src/main/java/org/thingsboard/server/service/mail/DefaultMailService.java @@ -322,11 +322,12 @@ public class DefaultMailService implements MailService { } @Override - public void sendTwoFaVerificationEmail(String email, String verificationCode) throws ThingsboardException { + public void sendTwoFaVerificationEmail(String email, String verificationCode, int expirationTimeSeconds) throws ThingsboardException { String subject = messages.getMessage("2fa.verification.code.subject", null, Locale.US); String message = mergeTemplateIntoString("2fa.verification.code.ftl", Map.of( TARGET_EMAIL, email, - "verificationCode", verificationCode + "verificationCode", verificationCode, + "expirationTimeSeconds", expirationTimeSeconds )); sendMail(mailSender, mailFrom, email, subject, message); diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/mfa/provider/impl/EmailTwoFaProvider.java b/application/src/main/java/org/thingsboard/server/service/security/auth/mfa/provider/impl/EmailTwoFaProvider.java index 39af5038ef..2aa5985514 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/mfa/provider/impl/EmailTwoFaProvider.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/mfa/provider/impl/EmailTwoFaProvider.java @@ -55,7 +55,7 @@ public class EmailTwoFaProvider extends OtpBasedTwoFaProvider @@ -86,12 +86,12 @@ diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/security/model/mfa/PlatformTwoFaSettings.java b/common/data/src/main/java/org/thingsboard/server/common/data/security/model/mfa/PlatformTwoFaSettings.java index 39ca0349fb..87d6403b04 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/security/model/mfa/PlatformTwoFaSettings.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/security/model/mfa/PlatformTwoFaSettings.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.common.data.security.model.mfa; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import lombok.Data; import org.thingsboard.server.common.data.security.model.mfa.provider.TwoFaProviderConfig; import org.thingsboard.server.common.data.security.model.mfa.provider.TwoFaProviderType; @@ -26,6 +27,7 @@ import java.util.List; import java.util.Optional; @Data +@JsonIgnoreProperties(ignoreUnknown = true) public class PlatformTwoFaSettings { @Valid diff --git a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/MailService.java b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/MailService.java index 236391ba16..6ac4417934 100644 --- a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/MailService.java +++ b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/MailService.java @@ -46,7 +46,7 @@ public interface MailService { void sendAccountLockoutEmail(String lockoutEmail, String email, Integer maxFailedLoginAttempts) throws ThingsboardException; - void sendTwoFaVerificationEmail(String email, String verificationCode) throws ThingsboardException; + void sendTwoFaVerificationEmail(String email, String verificationCode, int expirationTimeSeconds) throws ThingsboardException; void send(TenantId tenantId, CustomerId customerId, TbEmail tbEmail) throws ThingsboardException; From 7f31069285722b3f630bf873fff1a240fd05e4ad Mon Sep 17 00:00:00 2001 From: YevhenBondarenko Date: Wed, 18 May 2022 13:51:51 +0200 Subject: [PATCH 656/798] added loging and refactored --- .../3.3.4/schema_update_device_profile.sql | 20 +++++++++---------- .../entitiy/queue/DefaultTbQueueService.java | 12 ++++++----- .../DefaultTbRuleEngineConsumerService.java | 3 ++- .../common/data/queue/ProcessingStrategy.java | 2 +- ...leEngineQueueAckStrategyConfiguration.java | 1 + .../TbRuleEngineQueueConfiguration.java | 1 + ...ngineQueueSubmitStrategyConfiguration.java | 1 + .../ThingsboardMqttTransportApplication.java | 4 ---- 8 files changed, 23 insertions(+), 21 deletions(-) diff --git a/application/src/main/data/upgrade/3.3.4/schema_update_device_profile.sql b/application/src/main/data/upgrade/3.3.4/schema_update_device_profile.sql index 5f739e3a98..e0b0cc7f1a 100644 --- a/application/src/main/data/upgrade/3.3.4/schema_update_device_profile.sql +++ b/application/src/main/data/upgrade/3.3.4/schema_update_device_profile.sql @@ -17,16 +17,6 @@ ALTER TABLE device_profile ADD COLUMN IF NOT EXISTS default_queue_id uuid; -DO -$$ - BEGIN - IF NOT EXISTS(SELECT 1 FROM pg_constraint WHERE conname = 'fk_default_queue_device_profile') THEN - ALTER TABLE device_profile - ADD CONSTRAINT fk_default_queue_device_profile FOREIGN KEY (default_queue_id) REFERENCES queue (id); - END IF; - END; -$$; - DO $$ BEGIN @@ -45,5 +35,15 @@ $$ END $$; +DO +$$ + BEGIN + IF NOT EXISTS(SELECT 1 FROM pg_constraint WHERE conname = 'fk_default_queue_device_profile') THEN + ALTER TABLE device_profile + ADD CONSTRAINT fk_default_queue_device_profile FOREIGN KEY (default_queue_id) REFERENCES queue (id); + END IF; + END; +$$; + ALTER TABLE device_profile DROP COLUMN IF EXISTS default_queue_name; diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/queue/DefaultTbQueueService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/queue/DefaultTbQueueService.java index 60788ef02b..38bc7be474 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/queue/DefaultTbQueueService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/queue/DefaultTbQueueService.java @@ -79,14 +79,14 @@ public class DefaultTbQueueService implements TbQueueService { public void deleteQueue(TenantId tenantId, QueueId queueId) { Queue queue = queueService.findQueueById(tenantId, queueId); queueService.deleteQueue(tenantId, queueId); - onQueueDeleted(tenantId, queue); + onQueueDeleted(queue); } @Override public void deleteQueueByQueueName(TenantId tenantId, String queueName) { Queue queue = queueService.findQueueByTenantIdAndNameInternal(tenantId, queueName); queueService.deleteQueue(tenantId, queue.getId()); - onQueueDeleted(tenantId, queue); + onQueueDeleted(queue); } private void onQueueCreated(Queue queue) { @@ -123,8 +123,10 @@ public class DefaultTbQueueService implements TbQueueService { } await(); for (int i = currentPartitions; i < oldPartitions; i++) { + String fullTopicName = new TopicPartitionInfo(queue.getTopic(), queue.getTenantId(), i, false).getFullTopicName(); + log.info("Removed partition [{}]", fullTopicName); tbQueueAdmin.deleteTopic( - new TopicPartitionInfo(queue.getTopic(), queue.getTenantId(), i, false).getFullTopicName()); + fullTopicName); } } } else if (!oldQueue.equals(queue) && tbClusterService != null) { @@ -132,7 +134,7 @@ public class DefaultTbQueueService implements TbQueueService { } } - private void onQueueDeleted(TenantId tenantId, Queue queue) { + private void onQueueDeleted(Queue queue) { if (tbClusterService != null) { tbClusterService.onQueueDelete(queue); await(); @@ -141,7 +143,7 @@ public class DefaultTbQueueService implements TbQueueService { if (tbQueueAdmin != null) { for (int i = 0; i < queue.getPartitions(); i++) { String fullTopicName = new TopicPartitionInfo(queue.getTopic(), queue.getTenantId(), i, false).getFullTopicName(); - log.debug("Deleting queue [{}]", fullTopicName); + log.info("Deleting queue [{}]", fullTopicName); try { tbQueueAdmin.deleteTopic(fullTopicName); } catch (Exception e) { diff --git a/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbRuleEngineConsumerService.java b/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbRuleEngineConsumerService.java index 1fb03a2c47..2108b32992 100644 --- a/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbRuleEngineConsumerService.java +++ b/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbRuleEngineConsumerService.java @@ -133,7 +133,6 @@ public class DefaultTbRuleEngineConsumerService extends AbstractConsumerService< this.statsFactory = statsFactory; this.serviceInfoProvider = serviceInfoProvider; this.queueService = queueService; -// this.tenantId = actorContext.getServiceInfoProvider().getIsolatedTenant().orElse(TenantId.SYS_TENANT_ID); } @PostConstruct @@ -406,6 +405,7 @@ public class DefaultTbRuleEngineConsumerService extends AbstractConsumerService< } private synchronized void updateQueue(TransportProtos.QueueUpdateMsg queueUpdateMsg) { + log.info("Received queue update msg: [{}]", queueUpdateMsg); String queueName = queueUpdateMsg.getQueueName(); TenantId tenantId = new TenantId(new UUID(queueUpdateMsg.getTenantIdMSB(), queueUpdateMsg.getTenantIdLSB())); QueueId queueId = new QueueId(new UUID(queueUpdateMsg.getQueueIdMSB(), queueUpdateMsg.getQueueIdLSB())); @@ -439,6 +439,7 @@ public class DefaultTbRuleEngineConsumerService extends AbstractConsumerService< } private void deleteQueue(TransportProtos.QueueDeleteMsg queueDeleteMsg) { + log.info("Received queue delete msg: [{}]", queueDeleteMsg); TenantId tenantId = new TenantId(new UUID(queueDeleteMsg.getTenantIdMSB(), queueDeleteMsg.getTenantIdLSB())); QueueKey queueKey = new QueueKey(ServiceType.TB_RULE_ENGINE, queueDeleteMsg.getQueueName(), tenantId); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/queue/ProcessingStrategy.java b/common/data/src/main/java/org/thingsboard/server/common/data/queue/ProcessingStrategy.java index 8a315f14ad..3278aadb69 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/queue/ProcessingStrategy.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/queue/ProcessingStrategy.java @@ -24,4 +24,4 @@ public class ProcessingStrategy { private double failurePercentage; private long pauseBetweenRetries; private long maxPauseBetweenRetries; -} \ No newline at end of file +} diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/settings/TbRuleEngineQueueAckStrategyConfiguration.java b/common/queue/src/main/java/org/thingsboard/server/queue/settings/TbRuleEngineQueueAckStrategyConfiguration.java index 45ce5f8547..da46b94ba4 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/settings/TbRuleEngineQueueAckStrategyConfiguration.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/settings/TbRuleEngineQueueAckStrategyConfiguration.java @@ -18,6 +18,7 @@ package org.thingsboard.server.queue.settings; import lombok.Data; @Data +@Deprecated public class TbRuleEngineQueueAckStrategyConfiguration { private String type; diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/settings/TbRuleEngineQueueConfiguration.java b/common/queue/src/main/java/org/thingsboard/server/queue/settings/TbRuleEngineQueueConfiguration.java index 7732854180..b01fc50e00 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/settings/TbRuleEngineQueueConfiguration.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/settings/TbRuleEngineQueueConfiguration.java @@ -18,6 +18,7 @@ package org.thingsboard.server.queue.settings; import lombok.Data; @Data +@Deprecated public class TbRuleEngineQueueConfiguration { private String name; diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/settings/TbRuleEngineQueueSubmitStrategyConfiguration.java b/common/queue/src/main/java/org/thingsboard/server/queue/settings/TbRuleEngineQueueSubmitStrategyConfiguration.java index 97c489ffaa..85c6ff43d7 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/settings/TbRuleEngineQueueSubmitStrategyConfiguration.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/settings/TbRuleEngineQueueSubmitStrategyConfiguration.java @@ -18,6 +18,7 @@ package org.thingsboard.server.queue.settings; import lombok.Data; @Data +@Deprecated public class TbRuleEngineQueueSubmitStrategyConfiguration { private String type; diff --git a/transport/mqtt/src/main/java/org/thingsboard/server/mqtt/ThingsboardMqttTransportApplication.java b/transport/mqtt/src/main/java/org/thingsboard/server/mqtt/ThingsboardMqttTransportApplication.java index 1f64be1efe..f97ca22255 100644 --- a/transport/mqtt/src/main/java/org/thingsboard/server/mqtt/ThingsboardMqttTransportApplication.java +++ b/transport/mqtt/src/main/java/org/thingsboard/server/mqtt/ThingsboardMqttTransportApplication.java @@ -18,12 +18,8 @@ package org.thingsboard.server.mqtt; import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringBootConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration; -import org.springframework.boot.autoconfigure.data.cassandra.CassandraDataAutoConfiguration; -import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration; import org.springframework.context.annotation.ComponentScan; import org.springframework.scheduling.annotation.EnableAsync; -import org.springframework.scheduling.annotation.EnableScheduling; import java.util.Arrays; From 1ab9ed2467de82f59a1985ec2d7a1aa2e5fa2592 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Wed, 18 May 2022 15:44:42 +0300 Subject: [PATCH 657/798] UI: Add 2FA login page --- ui-ngx/src/app/core/auth/auth.service.ts | 32 ++++- ui-ngx/src/app/core/guards/auth.guard.ts | 13 +++ .../email-auth-dialog.component.html | 8 +- .../sms-auth-dialog.component.html | 8 +- .../totp-auth-dialog.component.html | 4 +- .../app/modules/login/login-routing.module.ts | 12 ++ ui-ngx/src/app/modules/login/login.module.ts | 4 +- .../two-factor-auth-login.component.html | 79 +++++++++++++ .../two-factor-auth-login.component.scss | 66 +++++++++++ .../login/two-factor-auth-login.component.ts | 109 ++++++++++++++++++ .../src/app/shared/models/authority.enum.ts | 3 +- ui-ngx/src/app/shared/models/login.models.ts | 3 + .../shared/models/two-factor-auth.models.ts | 31 +++++ .../assets/locale/locale.constant-en_US.json | 9 +- 14 files changed, 365 insertions(+), 16 deletions(-) create mode 100644 ui-ngx/src/app/modules/login/pages/login/two-factor-auth-login.component.html create mode 100644 ui-ngx/src/app/modules/login/pages/login/two-factor-auth-login.component.scss create mode 100644 ui-ngx/src/app/modules/login/pages/login/two-factor-auth-login.component.ts diff --git a/ui-ngx/src/app/core/auth/auth.service.ts b/ui-ngx/src/app/core/auth/auth.service.ts index 6b129da6ad..574bf05419 100644 --- a/ui-ngx/src/app/core/auth/auth.service.ts +++ b/ui-ngx/src/app/core/auth/auth.service.ts @@ -45,7 +45,8 @@ import { ActionNotificationShow } from '@core/notification/notification.actions' import { MatDialog, MatDialogConfig } from '@angular/material/dialog'; import { AlertDialogComponent } from '@shared/components/dialog/alert-dialog.component'; import { OAuth2ClientInfo, PlatformType } from '@shared/models/oauth2.models'; -import { isDefinedAndNotNull, isMobileApp } from '@core/utils'; +import { isMobileApp } from '@core/utils'; +import { TwoFactorAuthProviderType, TwoFaProviderInfo } from '@shared/models/two-factor-auth.models'; @Injectable({ providedIn: 'root' @@ -70,6 +71,7 @@ export class AuthService { redirectUrl: string; oauth2Clients: Array = null; + twoFactorAuthProviders: Array = null; private refreshTokenSubject: ReplaySubject = null; private jwtHelper = new JwtHelperService(); @@ -114,6 +116,18 @@ export class AuthService { public login(loginRequest: LoginRequest): Observable { return this.http.post('/api/auth/login', loginRequest, defaultHttpOptions()).pipe( + tap((loginResponse: LoginResponse) => { + this.setUserFromJwtToken(loginResponse.token, loginResponse.refreshToken, true); + if (loginResponse.scope === Authority.PRE_VERIFICATION_TOKEN) { + this.router.navigateByUrl(`login/mfa`); + } + } + )); + } + + public checkTwoFaVerificationCode(providerType: TwoFactorAuthProviderType, verificationCode: number): Observable { + return this.http.post(`/api/auth/2fa/verification/check?providerType=${providerType}&verificationCode=${verificationCode}`, + null, defaultHttpOptions()).pipe( tap((loginResponse: LoginResponse) => { this.setUserFromJwtToken(loginResponse.token, loginResponse.refreshToken, true); } @@ -215,6 +229,15 @@ export class AuthService { ); } + public getAvailableTwoFaLoginProviders(): Observable> { + return this.http.get>(`/api/auth/2fa/providers`, defaultHttpOptions()).pipe( + catchError(() => of([])), + tap((providers) => { + this.twoFactorAuthProviders = providers; + }) + ); + } + private forceDefaultPlace(authState?: AuthState, path?: string, params?: any): boolean { if (authState && authState.authUser) { if (authState.authUser.authority === Authority.TENANT_ADMIN || authState.authUser.authority === Authority.CUSTOMER_USER) { @@ -382,6 +405,9 @@ export class AuthService { loadUserSubject.error(err); } ); + } else if (authPayload.authUser.authority === Authority.PRE_VERIFICATION_TOKEN) { + loadUserSubject.next(authPayload); + loadUserSubject.complete(); } else if (authPayload.authUser.userId) { this.userService.getUser(authPayload.authUser.userId).subscribe( (user) => { @@ -594,8 +620,8 @@ export class AuthService { private updateAndValidateToken(token, prefix, notify) { let valid = false; const tokenData = this.jwtHelper.decodeToken(token); - const issuedAt = tokenData.iat; - const expTime = tokenData.exp; + const issuedAt = tokenData?.iat; + const expTime = tokenData?.exp; if (issuedAt && expTime) { const ttl = expTime - issuedAt; if (ttl > 0) { diff --git a/ui-ngx/src/app/core/guards/auth.guard.ts b/ui-ngx/src/app/core/guards/auth.guard.ts index 5ead048f30..4fe53d7bdc 100644 --- a/ui-ngx/src/app/core/guards/auth.guard.ts +++ b/ui-ngx/src/app/core/guards/auth.guard.ts @@ -94,6 +94,8 @@ export class AuthGuard implements CanActivate, CanActivateChild { return true; }) ); + } else if (path === 'login.mfa') { + return of(this.router.parseUrl('/login')); } else { return of(true); } @@ -114,6 +116,17 @@ export class AuthGuard implements CanActivate, CanActivateChild { this.mobileService.handleMobileNavigation(path, params); return of(false); } + if (authState.authUser.authority === Authority.PRE_VERIFICATION_TOKEN) { + if (path === 'login.mfa') { + return this.authService.getAvailableTwoFaLoginProviders().pipe( + map(() => { + return true; + }) + ); + } + this.authService.logout(); + return of(false); + } const defaultUrl = this.authService.defaultUrl(true, authState, path, params); if (defaultUrl) { // this.authService.gotoDefaultPlace(true); diff --git a/ui-ngx/src/app/modules/home/pages/security/authentication-dialog/email-auth-dialog.component.html b/ui-ngx/src/app/modules/home/pages/security/authentication-dialog/email-auth-dialog.component.html index d591dc7261..75211537d4 100644 --- a/ui-ngx/src/app/modules/home/pages/security/authentication-dialog/email-auth-dialog.component.html +++ b/ui-ngx/src/app/modules/home/pages/security/authentication-dialog/email-auth-dialog.component.html @@ -37,9 +37,9 @@

security.2fa.dialog.email-step-description

- + - {{ 'user.email-required' | translate }} @@ -64,11 +64,11 @@ {{ 'security.2fa.dialog.verification-step-description' | translate : {address: emailConfigForm.get('email').value} }}

- + {{ 'security.2fa.dialog.verification-code-invalid' | translate }} diff --git a/ui-ngx/src/app/modules/home/pages/security/authentication-dialog/sms-auth-dialog.component.html b/ui-ngx/src/app/modules/home/pages/security/authentication-dialog/sms-auth-dialog.component.html index 61b093dd00..f27e138a0e 100644 --- a/ui-ngx/src/app/modules/home/pages/security/authentication-dialog/sms-auth-dialog.component.html +++ b/ui-ngx/src/app/modules/home/pages/security/authentication-dialog/sms-auth-dialog.component.html @@ -37,9 +37,9 @@

security.2fa.dialog.sms-step-description

- + - @@ -67,11 +67,11 @@ {{ 'security.2fa.dialog.verification-step-description' | translate : {address: smsConfigForm.get('phone').value} }}

- + {{ 'security.2fa.dialog.verification-code-invalid' | translate }} diff --git a/ui-ngx/src/app/modules/home/pages/security/authentication-dialog/totp-auth-dialog.component.html b/ui-ngx/src/app/modules/home/pages/security/authentication-dialog/totp-auth-dialog.component.html index 6de8259011..54796c467d 100644 --- a/ui-ngx/src/app/modules/home/pages/security/authentication-dialog/totp-auth-dialog.component.html +++ b/ui-ngx/src/app/modules/home/pages/security/authentication-dialog/totp-auth-dialog.component.html @@ -53,11 +53,11 @@

security.2fa.dialog.scan-qr-code

security.2fa.dialog.enter-verification-code

- + {{ 'security.2fa.dialog.verification-code-invalid' | translate }} diff --git a/ui-ngx/src/app/modules/login/login-routing.module.ts b/ui-ngx/src/app/modules/login/login-routing.module.ts index 1d10a296c4..6305fa9dfd 100644 --- a/ui-ngx/src/app/modules/login/login-routing.module.ts +++ b/ui-ngx/src/app/modules/login/login-routing.module.ts @@ -22,6 +22,8 @@ import { AuthGuard } from '@core/guards/auth.guard'; import { ResetPasswordRequestComponent } from '@modules/login/pages/login/reset-password-request.component'; import { ResetPasswordComponent } from '@modules/login/pages/login/reset-password.component'; import { CreatePasswordComponent } from '@modules/login/pages/login/create-password.component'; +import { TwoFactorAuthLoginComponent } from '@modules/login/pages/login/two-factor-auth-login.component'; +import { Authority } from '@shared/models/authority.enum'; const routes: Routes = [ { @@ -69,6 +71,16 @@ const routes: Routes = [ module: 'public' }, canActivate: [AuthGuard] + }, + { + path: 'login/mfa', + component: TwoFactorAuthLoginComponent, + data: { + title: 'login.create-password', + auth: [Authority.PRE_VERIFICATION_TOKEN], + module: 'public' + }, + canActivate: [AuthGuard] } ]; diff --git a/ui-ngx/src/app/modules/login/login.module.ts b/ui-ngx/src/app/modules/login/login.module.ts index 78fec568c6..6414de2bf0 100644 --- a/ui-ngx/src/app/modules/login/login.module.ts +++ b/ui-ngx/src/app/modules/login/login.module.ts @@ -23,13 +23,15 @@ import { SharedModule } from '@app/shared/shared.module'; import { ResetPasswordRequestComponent } from '@modules/login/pages/login/reset-password-request.component'; import { ResetPasswordComponent } from '@modules/login/pages/login/reset-password.component'; import { CreatePasswordComponent } from '@modules/login/pages/login/create-password.component'; +import { TwoFactorAuthLoginComponent } from '@modules/login/pages/login/two-factor-auth-login.component'; @NgModule({ declarations: [ LoginComponent, ResetPasswordRequestComponent, ResetPasswordComponent, - CreatePasswordComponent + CreatePasswordComponent, + TwoFactorAuthLoginComponent ], imports: [ CommonModule, diff --git a/ui-ngx/src/app/modules/login/pages/login/two-factor-auth-login.component.html b/ui-ngx/src/app/modules/login/pages/login/two-factor-auth-login.component.html new file mode 100644 index 0000000000..3db2b8b061 --- /dev/null +++ b/ui-ngx/src/app/modules/login/pages/login/two-factor-auth-login.component.html @@ -0,0 +1,79 @@ + + diff --git a/ui-ngx/src/app/modules/login/pages/login/two-factor-auth-login.component.scss b/ui-ngx/src/app/modules/login/pages/login/two-factor-auth-login.component.scss new file mode 100644 index 0000000000..7403d7a27c --- /dev/null +++ b/ui-ngx/src/app/modules/login/pages/login/two-factor-auth-login.component.scss @@ -0,0 +1,66 @@ +/** + * Copyright © 2016-2022 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +@import '../../../../../scss/constants'; + +:host { + display: flex; + flex: 1 1 0; + .tb-two-factor-auth-login-content { + background-color: #eee; + .tb-two-factor-auth-login-card { + padding: 48px 48px 48px 16px; + + @media #{$mat-gt-xs} { + width: 450px !important; + } + + .mat-card-title{ + font: 500 28px / 36px Roboto, "Helvetica Neue", sans-serif; + } + + .mat-card-content { + margin-top: 44px; + margin-left: 40px; + } + + .mat-body { + letter-spacing: 0.25px; + line-height: 16px; + margin: 0; + } + + .code-block { + margin-top: 16px; + } + + .providers-container{ + padding: 0; + + .mat-body { + padding-bottom: 8px; + } + } + } + } + ::ng-deep{ + button.provider { + text-align: start; + &:not(.mat-button-disabled) { + border-color: rgba(255, 255, 255, .8); + } + } + } +} diff --git a/ui-ngx/src/app/modules/login/pages/login/two-factor-auth-login.component.ts b/ui-ngx/src/app/modules/login/pages/login/two-factor-auth-login.component.ts new file mode 100644 index 0000000000..c2d34856e6 --- /dev/null +++ b/ui-ngx/src/app/modules/login/pages/login/two-factor-auth-login.component.ts @@ -0,0 +1,109 @@ +/// +/// Copyright © 2016-2022 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { Component, OnInit } from '@angular/core'; +import { AuthService } from '@core/auth/auth.service'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { PageComponent } from '@shared/components/page.component'; +import { FormBuilder, Validators } from '@angular/forms'; +import { TwoFactorAuthenticationService } from '@core/http/two-factor-authentication.service'; +import { + twoFactorAuthProvidersLoginData, + TwoFactorAuthProviderType, + TwoFaProviderInfo +} from '@shared/models/two-factor-auth.models'; +import { TranslateService } from '@ngx-translate/core'; + +@Component({ + selector: 'tb-two-factor-auth-login', + templateUrl: './two-factor-auth-login.component.html', + styleUrls: ['./two-factor-auth-login.component.scss'] +}) +export class TwoFactorAuthLoginComponent extends PageComponent implements OnInit { + + private providersInfo: TwoFaProviderInfo[]; + + selectedProvider: TwoFactorAuthProviderType; + twoFactorAuthProvider = TwoFactorAuthProviderType; + allowProviders: TwoFactorAuthProviderType[] = []; + + providersData = twoFactorAuthProvidersLoginData; + providerDescription = ''; + + verificationForm = this.fb.group({ + verificationCode: ['', [ + Validators.required, + Validators.minLength(6), + Validators.maxLength(6), + Validators.pattern(/^\d*$/) + ]] + }); + + constructor(protected store: Store, + private twoFactorAuthService: TwoFactorAuthenticationService, + private authService: AuthService, + private translate: TranslateService, + private fb: FormBuilder) { + super(store); + } + + ngOnInit() { + this.providersInfo = this.authService.twoFactorAuthProviders; + Object.values(TwoFactorAuthProviderType).forEach(provider => { + const providerConfig = this.providersInfo.find(config => config.type === provider); + if (providerConfig) { + if (providerConfig.default) { + this.selectedProvider = providerConfig.type; + this.providerDescription = this.translate.instant(this.providersData.get(providerConfig.type).description, { + contact: providerConfig.contact + }); + } + this.allowProviders.push(providerConfig.type); + } + }); + if (this.selectedProvider !== TwoFactorAuthProviderType.TOTP) { + this.sendCode(); + } + } + + sendVerificationCode() { + if (this.verificationForm.valid && this.selectedProvider) { + this.authService.checkTwoFaVerificationCode(this.selectedProvider, this.verificationForm.get('verificationCode').value).subscribe( + () => {} + ); + } + } + + selectProvider(type: TwoFactorAuthProviderType) { + this.selectedProvider = type; + const providerConfig = this.providersInfo.find(config => config.type === type); + this.providerDescription = this.translate.instant(this.providersData.get(providerConfig.type).description, { + contact: providerConfig.contact + }); + if (type !== TwoFactorAuthProviderType.TOTP && type !== null) { + this.sendCode(); + } + } + + sendCode() { + this.twoFactorAuthService.requestTwoFaVerificationCodeSend(this.selectedProvider).subscribe(() => {}); + } + + cancelLogin() { + this.authService.logout(); + } +} diff --git a/ui-ngx/src/app/shared/models/authority.enum.ts b/ui-ngx/src/app/shared/models/authority.enum.ts index 83dab9a8a8..683c153b07 100644 --- a/ui-ngx/src/app/shared/models/authority.enum.ts +++ b/ui-ngx/src/app/shared/models/authority.enum.ts @@ -19,5 +19,6 @@ export enum Authority { TENANT_ADMIN = 'TENANT_ADMIN', CUSTOMER_USER = 'CUSTOMER_USER', REFRESH_TOKEN = 'REFRESH_TOKEN', - ANONYMOUS = 'ANONYMOUS' + ANONYMOUS = 'ANONYMOUS', + PRE_VERIFICATION_TOKEN = 'PRE_VERIFICATION_TOKEN' } diff --git a/ui-ngx/src/app/shared/models/login.models.ts b/ui-ngx/src/app/shared/models/login.models.ts index 782e3a98a4..d3f1598fc5 100644 --- a/ui-ngx/src/app/shared/models/login.models.ts +++ b/ui-ngx/src/app/shared/models/login.models.ts @@ -14,6 +14,8 @@ /// limitations under the License. /// +import { Authority } from '@shared/models/authority.enum'; + export interface LoginRequest { username: string; password: string; @@ -26,4 +28,5 @@ export interface PublicLoginRequest { export interface LoginResponse { token: string; refreshToken: string; + scope?: Authority; } diff --git a/ui-ngx/src/app/shared/models/two-factor-auth.models.ts b/ui-ngx/src/app/shared/models/two-factor-auth.models.ts index 18bb3e4f20..75fc95ae4e 100644 --- a/ui-ngx/src/app/shared/models/two-factor-auth.models.ts +++ b/ui-ngx/src/app/shared/models/two-factor-auth.models.ts @@ -92,6 +92,7 @@ export interface AccountTwoFaSettings { export interface TwoFaProviderInfo { type: TwoFactorAuthProviderType; default: boolean; + contact?: string; } export interface TwoFactorAuthProviderData { @@ -99,6 +100,10 @@ export interface TwoFactorAuthProviderData { description: string; } +export interface TwoFactorAuthProviderLoginData extends TwoFactorAuthProviderData { + icon: string; +} + export const twoFactorAuthProvidersData = new Map( [ [ @@ -121,3 +126,29 @@ export const twoFactorAuthProvidersData = new Map( + [ + [ + TwoFactorAuthProviderType.TOTP, { + name: 'security.2fa.provider.totp', + description: 'login.totp-auth-description', + icon: 'mdi:cellphone-key' + } + ], + [ + TwoFactorAuthProviderType.SMS, { + name: 'security.2fa.provider.sms', + description: 'login.sms-auth-description', + icon: 'mdi:message-reply-text-outline' + } + ], + [ + TwoFactorAuthProviderType.EMAIL, { + name: 'security.2fa.provider.email', + description: 'login.email-auth-description', + icon: 'mdi:email-outline' + } + ], + ] +); diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index 98b4df1c2b..f16eaf4526 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -2480,7 +2480,14 @@ "email": "Email", "login-with": "Login with {{name}}", "or": "or", - "error": "Login error" + "error": "Login error", + "verify-your-identity": "Verify your identity", + "select-way-to-verify": "Select a way to verify", + "resend-code": "Resend code", + "try-another-way": "Try another way", + "totp-auth-description": "Please enter the security code from your authenticator app.", + "sms-auth-description": "A security code has been sent to your phone at {{contact}}.", + "email-auth-description": "A security code has been sent to your email address at {{contact}}." }, "markdown": { "copy-code": "Click to copy", From 9e70cb362d837239ac3a063408f559fd6415a74b Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Wed, 18 May 2022 15:56:46 +0300 Subject: [PATCH 658/798] refactoring: - dashboard delete remove generic --- .../entitiy/DefaultTbNotificationEntityService.java | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/DefaultTbNotificationEntityService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/DefaultTbNotificationEntityService.java index 62b020ac32..63ccd2b7a2 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/DefaultTbNotificationEntityService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/DefaultTbNotificationEntityService.java @@ -81,7 +81,7 @@ public class DefaultTbNotificationEntityService implements TbNotificationEntityS SecurityUser user, String body, Object... additionalInfo) { logEntityAction(tenantId, originatorId, alarm, customerId, actionType, user, additionalInfo); - sendAlarmDeleteNotificationMsg(tenantId, alarm.getId(), alarm, relatedEdgeIds, body); + sendAlarmDeleteNotificationMsg(tenantId, alarm, relatedEdgeIds, body); } @Override @@ -228,12 +228,11 @@ public class DefaultTbNotificationEntityService implements TbNotificationEntityS } } - protected void sendAlarmDeleteNotificationMsg(TenantId tenantId, I entityId, E entity, - List edgeIds, String body) { + protected void sendAlarmDeleteNotificationMsg(TenantId tenantId, Alarm alarm, List edgeIds, String body) { try { - sendDeleteNotificationMsg(tenantId, entityId, edgeIds, body); + sendDeleteNotificationMsg(tenantId, alarm.getId(), edgeIds, body); } catch (Exception e) { - log.warn("Failed to push delete " + entity.getClass().getName() + " msg to core: {}", entity, e); + log.warn("Failed to push delete msg to core: {}", alarm, e); } } @@ -242,7 +241,7 @@ public class DefaultTbNotificationEntityService implements TbNotificationEntityS try { sendDeleteNotificationMsg(tenantId, entityId, edgeIds, null); } catch (Exception e) { - log.warn("Failed to push delete " + entity.getClass().getName() + " msg to core: {}", entity, e); + log.warn("Failed to push delete msg to core: {}", entity, e); } } From 57924394db7df2bffe196b3f6f2d236da7d8ce6e Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Wed, 18 May 2022 16:15:59 +0300 Subject: [PATCH 659/798] UI: Fixed config totp provider --- .../authentication-dialog/totp-auth-dialog.component.html | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ui-ngx/src/app/modules/home/pages/security/authentication-dialog/totp-auth-dialog.component.html b/ui-ngx/src/app/modules/home/pages/security/authentication-dialog/totp-auth-dialog.component.html index 54796c467d..0a2df8cf95 100644 --- a/ui-ngx/src/app/modules/home/pages/security/authentication-dialog/totp-auth-dialog.component.html +++ b/ui-ngx/src/app/modules/home/pages/security/authentication-dialog/totp-auth-dialog.component.html @@ -66,8 +66,9 @@
From ebcfa52e8de6b2725db022130b06bd4f2765c5e1 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Wed, 18 May 2022 17:49:25 +0300 Subject: [PATCH 660/798] UI: Refactoring --- .../two-factor-auth-settings.component.html | 181 +++++++++++++----- .../two-factor-auth-settings.component.scss | 8 +- .../two-factor-auth-settings.component.ts | 8 +- .../two-factor-auth-login.component.scss | 8 +- .../login/two-factor-auth-login.component.ts | 23 ++- 5 files changed, 160 insertions(+), 68 deletions(-) diff --git a/ui-ngx/src/app/modules/home/pages/admin/two-factor-auth-settings.component.html b/ui-ngx/src/app/modules/home/pages/admin/two-factor-auth-settings.component.html index f3e850a8d7..8aff328999 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/two-factor-auth-settings.component.html +++ b/ui-ngx/src/app/modules/home/pages/admin/two-factor-auth-settings.component.html @@ -33,7 +33,7 @@
admin.2fa.verification-limitations -
+
admin.2fa.total-allowed-time-for-verification @@ -58,62 +58,137 @@
- - {{ 'admin.2fa.verification-code-send-rate-limit' | translate }} - -
- - admin.2fa.number-of-send-attempts - - - {{ 'admin.2fa.number-of-send-attempts-required' | translate }} - - + + + + + {{ 'admin.2fa.verification-code-send-rate-limit' | translate }} + + + +
+ + admin.2fa.number-of-send-attempts + + + {{ 'admin.2fa.number-of-send-attempts-required' | translate }} + + - {{ 'admin.2fa.number-of-send-attempts-pattern' | translate }} - - - - admin.2fa.within-time - - - {{ 'admin.2fa.within-time-required' | translate }} - - + admin.2fa.within-time + + + {{ 'admin.2fa.within-time-required' | translate }} + + - {{ 'admin.2fa.within-time-pattern' | translate }} - - -
- - {{ 'admin.2fa.verification-code-check-rate-limit' | translate }} - -
- - admin.2fa.number-of-checking-attempts - - - {{ 'admin.2fa.number-of-checking-attempts-required' | translate }} - - --> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {{ 'admin.2fa.verification-code-check-rate-limit' | translate }} + + + +
+ + admin.2fa.number-of-checking-attempts + + + {{ 'admin.2fa.number-of-checking-attempts-required' | translate }} + + - {{ 'admin.2fa.number-of-checking-attempts-pattern' | translate }} - - - - admin.2fa.within-time - - - {{ 'admin.2fa.within-time-required' | translate }} - - + admin.2fa.within-time + + + {{ 'admin.2fa.within-time-required' | translate }} + + - {{ 'admin.2fa.within-time-pattern' | translate }} - - -
+ {{ 'admin.2fa.within-time-pattern' | translate }} +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
admin.2fa.available-providers diff --git a/ui-ngx/src/app/modules/home/pages/admin/two-factor-auth-settings.component.scss b/ui-ngx/src/app/modules/home/pages/admin/two-factor-auth-settings.component.scss index 2ae3981717..6a716e6c47 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/two-factor-auth-settings.component.scss +++ b/ui-ngx/src/app/modules/home/pages/admin/two-factor-auth-settings.component.scss @@ -26,17 +26,15 @@ legend { color: rgba(0, 0, 0, .7); width: fit-content; + margin: 0 8px; } - &:not(:last-of-type) { - padding: 8px; + .input-row { + padding: 8px 8px 0; } &:last-of-type { margin-bottom: 24px; - legend { - margin: 0 8px; - } } .rate-limit-toggle { diff --git a/ui-ngx/src/app/modules/home/pages/admin/two-factor-auth-settings.component.ts b/ui-ngx/src/app/modules/home/pages/admin/two-factor-auth-settings.component.ts index c866612049..10cc499118 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/two-factor-auth-settings.component.ts +++ b/ui-ngx/src/app/modules/home/pages/admin/two-factor-auth-settings.component.ts @@ -92,10 +92,14 @@ export class TwoFactorAuthSettingsComponent extends PageComponent implements OnI } toggleProviders($event: Event, i: number): void { + this.toggleExtensionPanel($event, i + 2, this.providersForm.at(i).get('enable').value); + } + + toggleExtensionPanel($event: Event, i: number, currentState: boolean) { if ($event) { $event.stopPropagation(); } - if (this.providersForm.at(i).get('enable').value) { + if (currentState) { this.getByIndexPanel(i).close(); } else { this.getByIndexPanel(i).open(); @@ -175,7 +179,7 @@ export class TwoFactorAuthSettingsComponent extends PageComponent implements OnI const findIndex = allowProvidersConfig.indexOf(provider); if (findIndex > -1) { processFormValue.providers.push(Object.assign(settings.providers[findIndex], {enable: true})); - this.getByIndexPanel(index).open(); + this.getByIndexPanel(index + 2).open(); } else { processFormValue.providers.push({enable: false}); } diff --git a/ui-ngx/src/app/modules/login/pages/login/two-factor-auth-login.component.scss b/ui-ngx/src/app/modules/login/pages/login/two-factor-auth-login.component.scss index 7403d7a27c..c29e4ae6d4 100644 --- a/ui-ngx/src/app/modules/login/pages/login/two-factor-auth-login.component.scss +++ b/ui-ngx/src/app/modules/login/pages/login/two-factor-auth-login.component.scss @@ -28,7 +28,7 @@ } .mat-card-title{ - font: 500 28px / 36px Roboto, "Helvetica Neue", sans-serif; + font: 400 28px / 36px Roboto, "Helvetica Neue", sans-serif; } .mat-card-content { @@ -58,9 +58,15 @@ ::ng-deep{ button.provider { text-align: start; + font-weight: 400; &:not(.mat-button-disabled) { border-color: rgba(255, 255, 255, .8); } + .icon{ + height: 18px; + width: 18px; + vertical-align: sub; + } } } } diff --git a/ui-ngx/src/app/modules/login/pages/login/two-factor-auth-login.component.ts b/ui-ngx/src/app/modules/login/pages/login/two-factor-auth-login.component.ts index c2d34856e6..0c47899651 100644 --- a/ui-ngx/src/app/modules/login/pages/login/two-factor-auth-login.component.ts +++ b/ui-ngx/src/app/modules/login/pages/login/two-factor-auth-login.component.ts @@ -36,6 +36,7 @@ import { TranslateService } from '@ngx-translate/core'; export class TwoFactorAuthLoginComponent extends PageComponent implements OnInit { private providersInfo: TwoFaProviderInfo[]; + private prevProvider: TwoFactorAuthProviderType; selectedProvider: TwoFactorAuthProviderType; twoFactorAuthProvider = TwoFactorAuthProviderType; @@ -89,13 +90,16 @@ export class TwoFactorAuthLoginComponent extends PageComponent implements OnInit } selectProvider(type: TwoFactorAuthProviderType) { + this.prevProvider = type === null ? this.selectedProvider : null; this.selectedProvider = type; - const providerConfig = this.providersInfo.find(config => config.type === type); - this.providerDescription = this.translate.instant(this.providersData.get(providerConfig.type).description, { - contact: providerConfig.contact - }); - if (type !== TwoFactorAuthProviderType.TOTP && type !== null) { - this.sendCode(); + if (type !== null) { + const providerConfig = this.providersInfo.find(config => config.type === type); + this.providerDescription = this.translate.instant(this.providersData.get(providerConfig.type).description, { + contact: providerConfig.contact + }); + if (type !== TwoFactorAuthProviderType.TOTP) { + this.sendCode(); + } } } @@ -104,6 +108,11 @@ export class TwoFactorAuthLoginComponent extends PageComponent implements OnInit } cancelLogin() { - this.authService.logout(); + if (this.prevProvider) { + this.selectedProvider = this.prevProvider; + this.prevProvider = null; + } else { + this.authService.logout(); + } } } From 708b7038d46c9438938fb8eb19df1e4b9c77ecd6 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Wed, 18 May 2022 17:53:54 +0300 Subject: [PATCH 661/798] UI: Clear code --- .../two-factor-auth-settings.component.html | 56 ------------------- .../two-factor-auth-settings.component.ts | 6 ++ 2 files changed, 6 insertions(+), 56 deletions(-) diff --git a/ui-ngx/src/app/modules/home/pages/admin/two-factor-auth-settings.component.html b/ui-ngx/src/app/modules/home/pages/admin/two-factor-auth-settings.component.html index 8aff328999..04cfd2c528 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/two-factor-auth-settings.component.html +++ b/ui-ngx/src/app/modules/home/pages/admin/two-factor-auth-settings.component.html @@ -95,34 +95,6 @@
- - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -161,34 +133,6 @@
- - - - - - - - - - - - - - - - - - - - - - - - - - - -
admin.2fa.available-providers diff --git a/ui-ngx/src/app/modules/home/pages/admin/two-factor-auth-settings.component.ts b/ui-ngx/src/app/modules/home/pages/admin/two-factor-auth-settings.component.ts index 10cc499118..c46cb202eb 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/two-factor-auth-settings.component.ts +++ b/ui-ngx/src/app/modules/home/pages/admin/two-factor-auth-settings.component.ts @@ -175,6 +175,12 @@ export class TwoFactorAuthSettingsComponent extends PageComponent implements OnI verificationCodeSendRateLimitTime: sendRateLimitTime || 60, providers: [] }); + if (sendRateLimitNumber > 0) { + this.getByIndexPanel(0).open(); + } + if (checkRateLimitNumber > 0) { + this.getByIndexPanel(1).open(); + } Object.values(TwoFactorAuthProviderType).forEach((provider, index) => { const findIndex = allowProvidersConfig.indexOf(provider); if (findIndex > -1) { From 92117d12809f8c052c4211e3ef6678fc133939c7 Mon Sep 17 00:00:00 2001 From: YevhenBondarenko Date: Wed, 18 May 2022 22:23:20 +0200 Subject: [PATCH 662/798] used scheduler instead of thread sleep --- .../entitiy/AbstractTbEntityService.java | 7 +- .../entitiy/queue/DefaultTbQueueService.java | 69 ++++++++----------- .../DefaultTbTenantProfileService.java | 4 +- .../dao/device/DeviceProfileServiceImpl.java | 1 - 4 files changed, 35 insertions(+), 46 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/AbstractTbEntityService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/AbstractTbEntityService.java index e2ccc1f3d2..74529d8c15 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/AbstractTbEntityService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/AbstractTbEntityService.java @@ -15,14 +15,12 @@ */ package org.thingsboard.server.service.entitiy; -import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import lombok.Getter; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; -import org.thingsboard.server.cluster.TbClusterService; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.HasName; import org.thingsboard.server.common.data.User; @@ -50,6 +48,7 @@ import org.thingsboard.server.dao.edge.EdgeService; import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.exception.IncorrectParameterException; import org.thingsboard.server.dao.model.ModelConstants; +import org.thingsboard.server.dao.queue.QueueService; import org.thingsboard.server.dao.rule.RuleChainService; import org.thingsboard.server.dao.tenant.TbTenantProfileCache; import org.thingsboard.server.dao.tenant.TenantService; @@ -69,8 +68,6 @@ public abstract class AbstractTbEntityService { protected static final int DEFAULT_PAGE_SIZE = 1000; - private static final ObjectMapper json = new ObjectMapper(); - @Value("${server.log_controller_error_stack_trace}") @Getter private boolean logControllerErrorStackTrace; @@ -106,6 +103,8 @@ public abstract class AbstractTbEntityService { protected RuleChainService ruleChainService; @Autowired protected EdgeNotificationService edgeNotificationService; + @Autowired + protected QueueService queueService; protected ListenableFuture removeAlarmsByEntityId(TenantId tenantId, EntityId entityId) { ListenableFuture> alarmsFuture = diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/queue/DefaultTbQueueService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/queue/DefaultTbQueueService.java index 38bc7be474..6a5778a474 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/queue/DefaultTbQueueService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/queue/DefaultTbQueueService.java @@ -16,7 +16,6 @@ package org.thingsboard.server.service.entitiy.queue; import lombok.AllArgsConstructor; -import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.thingsboard.server.cluster.TbClusterService; @@ -30,27 +29,30 @@ import org.thingsboard.server.common.data.queue.Queue; import org.thingsboard.server.common.data.tenant.profile.TenantProfileQueueConfiguration; import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; import org.thingsboard.server.dao.device.DeviceProfileService; -import org.thingsboard.server.dao.queue.QueueService; import org.thingsboard.server.queue.TbQueueAdmin; +import org.thingsboard.server.queue.scheduler.SchedulerComponent; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.entitiy.AbstractTbEntityService; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; @Slf4j @Service @TbCoreComponent @AllArgsConstructor -public class DefaultTbQueueService implements TbQueueService { +public class DefaultTbQueueService extends AbstractTbEntityService implements TbQueueService { private static final String MAIN = "Main"; + private static final long DELETE_DELAY = 30; - private final QueueService queueService; private final TbClusterService tbClusterService; private final TbQueueAdmin tbQueueAdmin; private final DeviceProfileService deviceProfileService; + private final SchedulerComponent scheduler; @Override public Queue saveQueue(Queue queue) { @@ -90,57 +92,50 @@ public class DefaultTbQueueService implements TbQueueService { } private void onQueueCreated(Queue queue) { - if (tbQueueAdmin != null) { - for (int i = 0; i < queue.getPartitions(); i++) { - tbQueueAdmin.createTopicIfNotExists( - new TopicPartitionInfo(queue.getTopic(), queue.getTenantId(), i, false).getFullTopicName()); - } + for (int i = 0; i < queue.getPartitions(); i++) { + tbQueueAdmin.createTopicIfNotExists( + new TopicPartitionInfo(queue.getTopic(), queue.getTenantId(), i, false).getFullTopicName()); } - if (tbClusterService != null) { - tbClusterService.onQueueChange(queue); - } + tbClusterService.onQueueChange(queue); } private void onQueueUpdated(Queue queue, Queue oldQueue) { int oldPartitions = oldQueue.getPartitions(); int currentPartitions = queue.getPartitions(); - if (currentPartitions != oldPartitions && tbQueueAdmin != null) { + if (currentPartitions != oldPartitions) { if (currentPartitions > oldPartitions) { log.info("Added [{}] new partitions to [{}] queue", currentPartitions - oldPartitions, queue.getName()); for (int i = oldPartitions; i < currentPartitions; i++) { tbQueueAdmin.createTopicIfNotExists( new TopicPartitionInfo(queue.getTopic(), queue.getTenantId(), i, false).getFullTopicName()); } - if (tbClusterService != null) { - tbClusterService.onQueueChange(queue); - } + tbClusterService.onQueueChange(queue); } else { log.info("Removed [{}] partitions from [{}] queue", oldPartitions - currentPartitions, queue.getName()); - if (tbClusterService != null) { - tbClusterService.onQueueChange(queue); - } - await(); - for (int i = currentPartitions; i < oldPartitions; i++) { - String fullTopicName = new TopicPartitionInfo(queue.getTopic(), queue.getTenantId(), i, false).getFullTopicName(); - log.info("Removed partition [{}]", fullTopicName); - tbQueueAdmin.deleteTopic( - fullTopicName); - } + tbClusterService.onQueueChange(queue); + + scheduler.schedule(() -> { + for (int i = currentPartitions; i < oldPartitions; i++) { + String fullTopicName = new TopicPartitionInfo(queue.getTopic(), queue.getTenantId(), i, false).getFullTopicName(); + log.info("Removed partition [{}]", fullTopicName); + tbQueueAdmin.deleteTopic( + fullTopicName); + } + }, DELETE_DELAY, TimeUnit.SECONDS); } - } else if (!oldQueue.equals(queue) && tbClusterService != null) { + } else if (!oldQueue.equals(queue)) { tbClusterService.onQueueChange(queue); } } private void onQueueDeleted(Queue queue) { - if (tbClusterService != null) { - tbClusterService.onQueueDelete(queue); - await(); - } + tbClusterService.onQueueDelete(queue); + // queueStatsService.deleteQueueStatsByQueueId(tenantId, queueId); - if (tbQueueAdmin != null) { + + scheduler.schedule(() -> { for (int i = 0; i < queue.getPartitions(); i++) { String fullTopicName = new TopicPartitionInfo(queue.getTopic(), queue.getTenantId(), i, false).getFullTopicName(); log.info("Deleting queue [{}]", fullTopicName); @@ -150,16 +145,12 @@ public class DefaultTbQueueService implements TbQueueService { log.error("Failed to delete queue [{}]", fullTopicName); } } - } - } - - @SneakyThrows - private void await() { - Thread.sleep(3000); + }, DELETE_DELAY, TimeUnit.SECONDS); } @Override - public void updateQueuesByTenants(List tenantIds, TenantProfile newTenantProfile, TenantProfile oldTenantProfile) { + public void updateQueuesByTenants(List tenantIds, TenantProfile newTenantProfile, TenantProfile + oldTenantProfile) { boolean oldIsolated = oldTenantProfile != null && oldTenantProfile.isIsolatedTbRuleEngine(); boolean newIsolated = newTenantProfile.isIsolatedTbRuleEngine(); diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/tenant_profile/DefaultTbTenantProfileService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/tenant_profile/DefaultTbTenantProfileService.java index 23a838e72a..23d9f74b8e 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/tenant_profile/DefaultTbTenantProfileService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/tenant_profile/DefaultTbTenantProfileService.java @@ -32,7 +32,7 @@ import java.util.List; @TbCoreComponent @AllArgsConstructor public class DefaultTbTenantProfileService implements TbTenantProfileService { - private final TbQueueService queueService; + private final TbQueueService tbQueueService; private final TenantProfileService tenantProfileService; private final TenantService tenantService; @@ -42,7 +42,7 @@ public class DefaultTbTenantProfileService implements TbTenantProfileService { if (oldTenantProfile != null && savedTenantProfile.isIsolatedTbRuleEngine()) { List tenantIds = tenantService.findTenantIdsByTenantProfileId(savedTenantProfile.getId()); - queueService.updateQueuesByTenants(tenantIds, savedTenantProfile, oldTenantProfile); + tbQueueService.updateQueuesByTenants(tenantIds, savedTenantProfile, oldTenantProfile); } return savedTenantProfile; diff --git a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileServiceImpl.java index f981aa55e5..04084de987 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileServiceImpl.java @@ -39,7 +39,6 @@ import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.dao.entity.AbstractCachedEntityService; import org.thingsboard.server.dao.exception.DataValidationException; -import org.thingsboard.server.dao.queue.QueueService; import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.service.PaginatedRemover; import org.thingsboard.server.dao.service.Validator; From 4f7c1665663cb8e4c932e50c5d7b9f3405cdda64 Mon Sep 17 00:00:00 2001 From: YevhenBondarenko Date: Thu, 19 May 2022 00:36:26 +0200 Subject: [PATCH 663/798] using repartitionExecutor instead of synchronized --- .../service/queue/DefaultTbRuleEngineConsumerService.java | 6 +++--- .../server/queue/discovery/HashPartitionService.java | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbRuleEngineConsumerService.java b/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbRuleEngineConsumerService.java index 2108b32992..1844929d35 100644 --- a/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbRuleEngineConsumerService.java +++ b/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbRuleEngineConsumerService.java @@ -393,10 +393,10 @@ public class DefaultTbRuleEngineConsumerService extends AbstractConsumerService< tbDeviceRpcService.processRpcResponseFromDevice(response); callback.onSuccess(); } else if (nfMsg.hasQueueUpdateMsg()) { - updateQueue(nfMsg.getQueueUpdateMsg()); + repartitionExecutor.execute(() -> updateQueue(nfMsg.getQueueUpdateMsg())); callback.onSuccess(); } else if (nfMsg.hasQueueDeleteMsg()) { - deleteQueue(nfMsg.getQueueDeleteMsg()); + repartitionExecutor.execute(() -> deleteQueue(nfMsg.getQueueDeleteMsg())); callback.onSuccess(); } else { log.trace("Received notification with missing handler"); @@ -404,7 +404,7 @@ public class DefaultTbRuleEngineConsumerService extends AbstractConsumerService< } } - private synchronized void updateQueue(TransportProtos.QueueUpdateMsg queueUpdateMsg) { + private void updateQueue(TransportProtos.QueueUpdateMsg queueUpdateMsg) { log.info("Received queue update msg: [{}]", queueUpdateMsg); String queueName = queueUpdateMsg.getQueueName(); TenantId tenantId = new TenantId(new UUID(queueUpdateMsg.getTenantIdMSB(), queueUpdateMsg.getTenantIdLSB())); diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/discovery/HashPartitionService.java b/common/queue/src/main/java/org/thingsboard/server/queue/discovery/HashPartitionService.java index 92278f7b4a..f9033754ff 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/discovery/HashPartitionService.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/discovery/HashPartitionService.java @@ -173,7 +173,6 @@ public class HashPartitionService implements PartitionService { } else { QueueRoutingInfo queueRoutingInfo = queuesById.get(queueId); - //TODO: replace if we can notify CheckPoint rule nodes about queue changes if (queueRoutingInfo == null) { log.debug("Queue was removed but still used in CheckPoint rule node. [{}][{}]", tenantId, entityId); queueKey = getMainQueueKey(serviceType, tenantId); @@ -205,6 +204,8 @@ public class HashPartitionService implements PartitionService { @Override public synchronized void recalculatePartitions(ServiceInfo currentService, List otherServices) { + partitionsInit(); + tbTransportServicesByType.clear(); logServiceInfo(currentService); otherServices.forEach(this::logServiceInfo); From abe127c3faeb92152ee8aac5c7b37a65ca07b7eb Mon Sep 17 00:00:00 2001 From: YevhenBondarenko Date: Thu, 19 May 2022 01:18:13 +0200 Subject: [PATCH 664/798] fixed checkpoint node --- .../thingsboard/server/controller/QueueController.java | 1 + .../thingsboard/rule/engine/flow/TbCheckpointNode.java | 10 +++++++--- .../engine/flow/TbCheckpointNodeConfiguration.java | 2 +- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/controller/QueueController.java b/application/src/main/java/org/thingsboard/server/controller/QueueController.java index 3b7b407b47..9b83aa43fb 100644 --- a/application/src/main/java/org/thingsboard/server/controller/QueueController.java +++ b/application/src/main/java/org/thingsboard/server/controller/QueueController.java @@ -107,6 +107,7 @@ public class QueueController extends BaseController { checkParameter("queueId", queueIdStr); try { QueueId queueId = new QueueId(UUID.fromString(queueIdStr)); + checkQueueId(queueId, Operation.READ); return checkNotNull(queueService.findQueueById(getTenantId(), queueId)); } catch ( Exception e) { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/flow/TbCheckpointNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/flow/TbCheckpointNode.java index 470a774b90..ff69212c67 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/flow/TbCheckpointNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/flow/TbCheckpointNode.java @@ -23,9 +23,12 @@ import org.thingsboard.rule.engine.api.TbNodeConfiguration; import org.thingsboard.rule.engine.api.TbNodeException; import org.thingsboard.rule.engine.api.TbRelationTypes; import org.thingsboard.rule.engine.api.util.TbNodeUtils; +import org.thingsboard.server.common.data.id.QueueId; import org.thingsboard.server.common.data.plugin.ComponentType; import org.thingsboard.server.common.msg.TbMsg; +import java.util.UUID; + @Slf4j @RuleNode( type = ComponentType.FLOW, @@ -38,16 +41,17 @@ import org.thingsboard.server.common.msg.TbMsg; ) public class TbCheckpointNode implements TbNode { - private TbCheckpointNodeConfiguration config; + private QueueId queueId; @Override public void init(TbContext ctx, TbNodeConfiguration configuration) throws TbNodeException { - this.config = TbNodeUtils.convert(configuration, TbCheckpointNodeConfiguration.class); + TbCheckpointNodeConfiguration config = TbNodeUtils.convert(configuration, TbCheckpointNodeConfiguration.class); + this.queueId = new QueueId(UUID.fromString(config.getQueueId())); } @Override public void onMsg(TbContext ctx, TbMsg msg) { - ctx.enqueueForTellNext(msg, config.getQueueId(), TbRelationTypes.SUCCESS, () -> ctx.ack(msg), error -> ctx.tellFailure(msg, error)); + ctx.enqueueForTellNext(msg, queueId, TbRelationTypes.SUCCESS, () -> ctx.ack(msg), error -> ctx.tellFailure(msg, error)); } @Override diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/flow/TbCheckpointNodeConfiguration.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/flow/TbCheckpointNodeConfiguration.java index 0546732874..42bd6342c9 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/flow/TbCheckpointNodeConfiguration.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/flow/TbCheckpointNodeConfiguration.java @@ -22,7 +22,7 @@ import org.thingsboard.server.common.data.id.QueueId; @Data public class TbCheckpointNodeConfiguration implements NodeConfiguration { - private QueueId queueId; + private String queueId; @Override public TbCheckpointNodeConfiguration defaultConfiguration() { From fa4a20d711d0d263fbe197eeba15a7a860c1c9b4 Mon Sep 17 00:00:00 2001 From: YevhenBondarenko Date: Thu, 19 May 2022 10:04:51 +0200 Subject: [PATCH 665/798] Added resolve by queue name for backward compatibility --- .../server/actors/ActorSystemContext.java | 5 +++++ .../server/actors/ruleChain/DefaultTbContext.java | 12 ++++++++++++ .../server/queue/discovery/HashPartitionService.java | 12 ++++++++++++ .../server/queue/discovery/PartitionService.java | 3 +++ .../org/thingsboard/rule/engine/api/TbContext.java | 3 +++ 5 files changed, 35 insertions(+) diff --git a/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java b/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java index 4059c51a57..500ad60524 100644 --- a/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java +++ b/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java @@ -506,6 +506,11 @@ public class ActorSystemContext { return partitionService.resolve(serviceType, queueId, tenantId, entityId); } + @Deprecated + public TopicPartitionInfo resolve(ServiceType serviceType, String queueName, TenantId tenantId, EntityId entityId) { + return partitionService.resolve(serviceType, tenantId, entityId, queueName); + } + public String getServiceId() { return serviceInfoProvider.getServiceId(); } diff --git a/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java index ee3f5e15ac..bf643a8967 100644 --- a/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java +++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java @@ -161,6 +161,13 @@ class DefaultTbContext implements TbContext { enqueue(tpi, tbMsg, onFailure, onSuccess); } + @Override + @Deprecated + public void enqueue(TbMsg tbMsg, String queueName, Runnable onSuccess, Consumer onFailure) { + TopicPartitionInfo tpi = resolvePartition(tbMsg, queueName); + enqueue(tpi, tbMsg, onFailure, onSuccess); + } + @Override public void enqueue(TbMsg tbMsg, QueueId queueId, Runnable onSuccess, Consumer onFailure) { TopicPartitionInfo tpi = resolvePartition(tbMsg, queueId); @@ -229,6 +236,11 @@ class DefaultTbContext implements TbContext { return mainCtx.resolve(ServiceType.TB_RULE_ENGINE, queueId, getTenantId(), tbMsg.getOriginator()); } + @Deprecated + private TopicPartitionInfo resolvePartition(TbMsg tbMsg, String queueName) { + return mainCtx.resolve(ServiceType.TB_RULE_ENGINE, queueName, getTenantId(), tbMsg.getOriginator()); + } + private TopicPartitionInfo resolvePartition(TbMsg tbMsg) { return resolvePartition(tbMsg, tbMsg.getQueueId()); } diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/discovery/HashPartitionService.java b/common/queue/src/main/java/org/thingsboard/server/queue/discovery/HashPartitionService.java index f9033754ff..5a522f9233 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/discovery/HashPartitionService.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/discovery/HashPartitionService.java @@ -160,6 +160,18 @@ public class HashPartitionService implements PartitionService { removeTenant(tenantId); } + @Override + @Deprecated + public TopicPartitionInfo resolve(ServiceType serviceType, TenantId tenantId, EntityId entityId, String queueName) { + log.warn("This method is deprecated and will be removed!!!"); + TenantId isolatedOrSystemTenantId = getIsolatedOrSystemTenantId(serviceType, tenantId); + QueueKey queueKey = new QueueKey(serviceType, queueName, isolatedOrSystemTenantId); + if (!partitionSizesMap.containsKey(queueKey)) { + queueKey = new QueueKey(serviceType, isolatedOrSystemTenantId); + } + return resolve(queueKey, entityId); + } + @Override public TopicPartitionInfo resolve(ServiceType serviceType, TenantId tenantId, EntityId entityId) { return resolve(serviceType, null, tenantId, entityId); diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/discovery/PartitionService.java b/common/queue/src/main/java/org/thingsboard/server/queue/discovery/PartitionService.java index 207de90e7b..3844bf02c9 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/discovery/PartitionService.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/discovery/PartitionService.java @@ -32,6 +32,9 @@ import java.util.UUID; */ public interface PartitionService { + @Deprecated + TopicPartitionInfo resolve(ServiceType serviceType, TenantId tenantId, EntityId entityId, String queueName); + TopicPartitionInfo resolve(ServiceType serviceType, TenantId tenantId, EntityId entityId); TopicPartitionInfo resolve(ServiceType serviceType, QueueId queueId, TenantId tenantId, EntityId entityId); diff --git a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbContext.java b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbContext.java index ce66b4fbce..1a817806da 100644 --- a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbContext.java +++ b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbContext.java @@ -142,6 +142,9 @@ public interface TbContext { */ void output(TbMsg msg, String relationType); + @Deprecated + void enqueue(TbMsg tbMsg, String queueName, Runnable onSuccess, Consumer onFailure); + /** * Puts new message to custom queue for processing * From e32e161745a2f05b7d60771f69531d2597875236 Mon Sep 17 00:00:00 2001 From: YevhenBondarenko Date: Thu, 19 May 2022 11:13:31 +0200 Subject: [PATCH 666/798] fixed queue permissions --- .../server/controller/BaseController.java | 13 +++++++++++-- .../permission/TenantAdminPermissions.java | 18 +++++++++++++++++- 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/controller/BaseController.java b/application/src/main/java/org/thingsboard/server/controller/BaseController.java index b787a85d1b..195ea9a3ee 100644 --- a/application/src/main/java/org/thingsboard/server/controller/BaseController.java +++ b/application/src/main/java/org/thingsboard/server/controller/BaseController.java @@ -69,8 +69,8 @@ import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityIdFactory; import org.thingsboard.server.common.data.id.EntityViewId; import org.thingsboard.server.common.data.id.OtaPackageId; -import org.thingsboard.server.common.data.id.RpcId; import org.thingsboard.server.common.data.id.QueueId; +import org.thingsboard.server.common.data.id.RpcId; import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.RuleNodeId; import org.thingsboard.server.common.data.id.TbResourceId; @@ -85,9 +85,9 @@ import org.thingsboard.server.common.data.page.SortOrder; import org.thingsboard.server.common.data.page.TimePageLink; import org.thingsboard.server.common.data.plugin.ComponentDescriptor; import org.thingsboard.server.common.data.plugin.ComponentType; +import org.thingsboard.server.common.data.queue.Queue; import org.thingsboard.server.common.data.relation.EntityRelation; import org.thingsboard.server.common.data.rpc.Rpc; -import org.thingsboard.server.common.data.queue.Queue; import org.thingsboard.server.common.data.rule.RuleChain; import org.thingsboard.server.common.data.rule.RuleChainType; import org.thingsboard.server.common.data.rule.RuleNode; @@ -150,6 +150,7 @@ import java.util.UUID; import static org.thingsboard.server.controller.ControllerConstants.DEFAULT_PAGE_SIZE; import static org.thingsboard.server.controller.ControllerConstants.INCORRECT_TENANT_ID; +import static org.thingsboard.server.controller.UserController.YOU_DON_T_HAVE_PERMISSION_TO_PERFORM_THIS_OPERATION; import static org.thingsboard.server.dao.service.Validator.validateId; @Slf4j @@ -825,6 +826,14 @@ public abstract class BaseController { Queue queue = queueService.findQueueById(getCurrentUser().getTenantId(), queueId); checkNotNull(queue); accessControlService.checkPermission(getCurrentUser(), Resource.QUEUE, operation, queueId, queue); + TenantId tenantId = getTenantId(); + if (queue.getTenantId().isNullUid() && !tenantId.isNullUid()) { + TenantProfile tenantProfile = tenantProfileCache.get(tenantId); + if (tenantProfile.isIsolatedTbRuleEngine()) { + throw new ThingsboardException(YOU_DON_T_HAVE_PERMISSION_TO_PERFORM_THIS_OPERATION, + ThingsboardErrorCode.PERMISSION_DENIED); + } + } return queue; } diff --git a/application/src/main/java/org/thingsboard/server/service/security/permission/TenantAdminPermissions.java b/application/src/main/java/org/thingsboard/server/service/security/permission/TenantAdminPermissions.java index bcc6251315..5810c6c4f8 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/permission/TenantAdminPermissions.java +++ b/application/src/main/java/org/thingsboard/server/service/security/permission/TenantAdminPermissions.java @@ -45,7 +45,7 @@ public class TenantAdminPermissions extends AbstractPermissions { put(Resource.OTA_PACKAGE, tenantEntityPermissionChecker); put(Resource.EDGE, tenantEntityPermissionChecker); put(Resource.RPC, tenantEntityPermissionChecker); - put(Resource.QUEUE, tenantEntityPermissionChecker); + put(Resource.QUEUE, queuePermissionChecker); } public static final PermissionChecker tenantEntityPermissionChecker = new PermissionChecker() { @@ -121,4 +121,20 @@ public class TenantAdminPermissions extends AbstractPermissions { } }; + + private static final PermissionChecker queuePermissionChecker = new PermissionChecker() { + + @Override + public boolean hasPermission(SecurityUser user, Operation operation, EntityId entityId, HasTenantId entity) { + if (entity.getTenantId() == null || entity.getTenantId().isNullUid()) { + return operation == Operation.READ; + } + if (!user.getTenantId().equals(entity.getTenantId())) { + return false; + } + return true; + } + + }; + } From b8f2d6ee9c99c6e475b4669125f1bae8f4269126 Mon Sep 17 00:00:00 2001 From: YevhenBondarenko Date: Thu, 19 May 2022 13:09:40 +0200 Subject: [PATCH 667/798] added tenant profile upgrade --- .../install/SqlDatabaseUpgradeService.java | 100 ++++++++++++++---- 1 file changed, 80 insertions(+), 20 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java b/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java index 473f3accda..db2352b706 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java +++ b/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java @@ -25,6 +25,7 @@ import org.springframework.context.annotation.Profile; import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.EntitySubtype; import org.thingsboard.server.common.data.Tenant; +import org.thingsboard.server.common.data.TenantProfile; import org.thingsboard.server.common.data.id.QueueId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; @@ -35,13 +36,16 @@ import org.thingsboard.server.common.data.queue.Queue; import org.thingsboard.server.common.data.queue.SubmitStrategy; import org.thingsboard.server.common.data.queue.SubmitStrategyType; import org.thingsboard.server.common.data.rule.RuleNode; +import org.thingsboard.server.common.data.tenant.profile.TenantProfileQueueConfiguration; import org.thingsboard.server.dao.dashboard.DashboardService; import org.thingsboard.server.dao.device.DeviceProfileService; import org.thingsboard.server.dao.device.DeviceService; import org.thingsboard.server.dao.queue.QueueService; import org.thingsboard.server.dao.rule.RuleChainService; +import org.thingsboard.server.dao.tenant.TenantProfileService; import org.thingsboard.server.dao.tenant.TenantService; import org.thingsboard.server.dao.usagerecord.ApiUsageStateService; +import org.thingsboard.server.queue.settings.TbRuleEngineQueueConfiguration; import org.thingsboard.server.service.install.sql.SqlDbHelper; import java.nio.charset.Charset; @@ -55,6 +59,7 @@ import java.sql.SQLException; import java.sql.SQLSyntaxErrorException; import java.sql.SQLWarning; import java.sql.Statement; +import java.util.Collections; import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; @@ -125,6 +130,9 @@ public class SqlDatabaseUpgradeService implements DatabaseEntitiesUpgradeService @Autowired private RuleChainService ruleChainService; + @Autowired + private TenantProfileService tenantProfileService; + @Override public void upgradeDatabase(String fromVersion) throws Exception { switch (fromVersion) { @@ -567,26 +575,7 @@ public class SqlDatabaseUpgradeService implements DatabaseEntitiesUpgradeService try { if (!CollectionUtils.isEmpty(queueConfig.getQueues())) { queueConfig.getQueues().forEach(queueSettings -> { - Queue queue = new Queue(); - queue.setTenantId(TenantId.SYS_TENANT_ID); - queue.setName(queueSettings.getName()); - queue.setTopic(queueSettings.getTopic()); - queue.setPollInterval(queueSettings.getPollInterval()); - queue.setPartitions(queueSettings.getPartitions()); - queue.setPackProcessingTimeout(queueSettings.getPackProcessingTimeout()); - SubmitStrategy submitStrategy = new SubmitStrategy(); - submitStrategy.setBatchSize(queueSettings.getSubmitStrategy().getBatchSize()); - submitStrategy.setType(SubmitStrategyType.valueOf(queueSettings.getSubmitStrategy().getType())); - queue.setSubmitStrategy(submitStrategy); - ProcessingStrategy processingStrategy = new ProcessingStrategy(); - processingStrategy.setType(ProcessingStrategyType.valueOf(queueSettings.getProcessingStrategy().getType())); - processingStrategy.setRetries(queueSettings.getProcessingStrategy().getRetries()); - processingStrategy.setFailurePercentage(queueSettings.getProcessingStrategy().getFailurePercentage()); - processingStrategy.setPauseBetweenRetries(queueSettings.getProcessingStrategy().getPauseBetweenRetries()); - processingStrategy.setMaxPauseBetweenRetries(queueSettings.getProcessingStrategy().getMaxPauseBetweenRetries()); - queue.setProcessingStrategy(processingStrategy); - queue.setConsumerPerPartition(queueSettings.isConsumerPerPartition()); - queueService.saveQueue(queue); + queueService.saveQueue(queueConfigToQueue(queueSettings)); }); } else { systemDataLoaderService.createQueues(); @@ -625,6 +614,32 @@ public class SqlDatabaseUpgradeService implements DatabaseEntitiesUpgradeService pageLink = pageLink.nextPageLink(); } while (pageData.hasNext()); + log.info("Updating tenant profiles..."); + PageLink profilePageLink = new PageLink(100); + PageData profilePageData; + do { + profilePageData = tenantProfileService.findTenantProfiles(TenantId.SYS_TENANT_ID, profilePageLink); + + profilePageData.getData().forEach(profile -> { + try { + List queueConfiguration = profile.getProfileData().getQueueConfiguration(); + if (profile.isIsolatedTbRuleEngine() && (queueConfiguration == null || queueConfiguration.isEmpty())) { + TenantProfileQueueConfiguration mainQueueConfig = getMainQueueConfiguration(); + profile.getProfileData().setQueueConfiguration(Collections.singletonList((mainQueueConfig))); + tenantProfileService.saveTenantProfile(TenantId.SYS_TENANT_ID, profile); + List isolatedTenants = tenantService.findTenantIdsByTenantProfileId(profile.getId()); + isolatedTenants.forEach(tenantId -> { + queueService.saveQueue(new Queue(tenantId, mainQueueConfig)); + }); + } + } catch (Exception e) { + } + + }); + profilePageLink = profilePageLink.nextPageLink(); + } while (profilePageData.hasNext()); + + log.info("Updating schema settings..."); conn.createStatement().execute("UPDATE tb_schema_settings SET schema_version = 3004000;"); log.info("Schema updated."); @@ -677,4 +692,49 @@ public class SqlDatabaseUpgradeService implements DatabaseEntitiesUpgradeService } return isOldSchema; } + + private Queue queueConfigToQueue(TbRuleEngineQueueConfiguration queueSettings) { + Queue queue = new Queue(); + queue.setTenantId(TenantId.SYS_TENANT_ID); + queue.setName(queueSettings.getName()); + queue.setTopic(queueSettings.getTopic()); + queue.setPollInterval(queueSettings.getPollInterval()); + queue.setPartitions(queueSettings.getPartitions()); + queue.setPackProcessingTimeout(queueSettings.getPackProcessingTimeout()); + SubmitStrategy submitStrategy = new SubmitStrategy(); + submitStrategy.setBatchSize(queueSettings.getSubmitStrategy().getBatchSize()); + submitStrategy.setType(SubmitStrategyType.valueOf(queueSettings.getSubmitStrategy().getType())); + queue.setSubmitStrategy(submitStrategy); + ProcessingStrategy processingStrategy = new ProcessingStrategy(); + processingStrategy.setType(ProcessingStrategyType.valueOf(queueSettings.getProcessingStrategy().getType())); + processingStrategy.setRetries(queueSettings.getProcessingStrategy().getRetries()); + processingStrategy.setFailurePercentage(queueSettings.getProcessingStrategy().getFailurePercentage()); + processingStrategy.setPauseBetweenRetries(queueSettings.getProcessingStrategy().getPauseBetweenRetries()); + processingStrategy.setMaxPauseBetweenRetries(queueSettings.getProcessingStrategy().getMaxPauseBetweenRetries()); + queue.setProcessingStrategy(processingStrategy); + queue.setConsumerPerPartition(queueSettings.isConsumerPerPartition()); + return queue; + } + + private TenantProfileQueueConfiguration getMainQueueConfiguration() { + TenantProfileQueueConfiguration mainQueueConfiguration = new TenantProfileQueueConfiguration(); + mainQueueConfiguration.setName("Main"); + mainQueueConfiguration.setTopic("tb_rule_engine.main"); + mainQueueConfiguration.setPollInterval(25); + mainQueueConfiguration.setPartitions(10); + mainQueueConfiguration.setConsumerPerPartition(true); + mainQueueConfiguration.setPackProcessingTimeout(2000); + SubmitStrategy mainQueueSubmitStrategy = new SubmitStrategy(); + mainQueueSubmitStrategy.setType(SubmitStrategyType.BURST); + mainQueueSubmitStrategy.setBatchSize(1000); + mainQueueConfiguration.setSubmitStrategy(mainQueueSubmitStrategy); + ProcessingStrategy mainQueueProcessingStrategy = new ProcessingStrategy(); + mainQueueProcessingStrategy.setType(ProcessingStrategyType.SKIP_ALL_FAILURES); + mainQueueProcessingStrategy.setRetries(3); + mainQueueProcessingStrategy.setFailurePercentage(0); + mainQueueProcessingStrategy.setPauseBetweenRetries(3); + mainQueueProcessingStrategy.setMaxPauseBetweenRetries(3); + mainQueueConfiguration.setProcessingStrategy(mainQueueProcessingStrategy); + return mainQueueConfiguration; + } } From 0e32206bd465f0f19a2da8488fcd4c15b2e0f9ed Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Thu, 19 May 2022 15:04:15 +0300 Subject: [PATCH 668/798] UI: Refactoring --- .../two-factor-auth-settings.component.html | 141 +++++++++--------- .../two-factor-auth-settings.component.scss | 5 - .../two-factor-auth-settings.component.ts | 4 +- .../email-auth-dialog.component.html | 10 +- .../sms-auth-dialog.component.html | 7 +- .../totp-auth-dialog.component.html | 5 +- .../two-factor-auth-login.component.html | 4 +- .../assets/locale/locale.constant-en_US.json | 4 +- 8 files changed, 90 insertions(+), 90 deletions(-) diff --git a/ui-ngx/src/app/modules/home/pages/admin/two-factor-auth-settings.component.html b/ui-ngx/src/app/modules/home/pages/admin/two-factor-auth-settings.component.html index 04cfd2c528..b5a06a6669 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/two-factor-auth-settings.component.html +++ b/ui-ngx/src/app/modules/home/pages/admin/two-factor-auth-settings.component.html @@ -31,6 +31,75 @@
+
+ admin.2fa.available-providers + + + + + + + {{ provider.value.providerType }} + + + + + + + + admin.2fa.issuer-name + + + {{ "admin.2fa.issuer-name-required" | translate }} + + + +
+ + admin.2fa.verification-message-template + + + {{ "admin.2fa.verification-message-template-required" | translate }} + + + {{ "admin.2fa.verification-message-template-pattern" | translate }} + + + + + admin.2fa.verification-code-lifetime + + + {{ "admin.2fa.verification-code-lifetime-required" | translate }} + + + {{ "admin.2fa.verification-code-lifetime-pattern" | translate }} + + +
+
+ + admin.2fa.verification-code-lifetime + + + {{ "admin.2fa.verification-code-lifetime-required" | translate }} + + + {{ "admin.2fa.verification-code-lifetime-pattern" | translate }} + + +
+
+
+
+ +
+
admin.2fa.verification-limitations
@@ -99,8 +168,7 @@ - {{ 'admin.2fa.verification-code-check-rate-limit' | translate }} @@ -134,75 +202,6 @@
-
- admin.2fa.available-providers - - - - - - - {{ provider.value.providerType }} - - - - - - - - admin.2fa.issuer-name - - - {{ "admin.2fa.issuer-name-required" | translate }} - - - -
- - admin.2fa.verification-message-template - - - {{ "admin.2fa.verification-message-template-required" | translate }} - - - {{ "admin.2fa.verification-message-template-pattern" | translate }} - - - - - admin.2fa.verification-code-lifetime - - - {{ "admin.2fa.verification-code-lifetime-required" | translate }} - - - {{ "admin.2fa.verification-code-lifetime-pattern" | translate }} - - -
-
- - admin.2fa.verification-code-lifetime - - - {{ "admin.2fa.verification-code-lifetime-required" | translate }} - - - {{ "admin.2fa.verification-code-lifetime-pattern" | translate }} - - -
-
-
-
- -
-
+ {{ 'security.2fa.dialog.verification-code-invalid' | translate }} + + {{ 'login.resend-code-wait' | translate : {time: countDownTime} }} + + + {{ 'login.resend-code-wait' | translate : {time: countDownTime} }} + -
- -
- +
+ + + +
+
+

security.2fa.dialog.backup-code-description

+
+
+ {{ code }} +
+
+

security.2fa.dialog.backup-code-warn

+
+ + +
+
diff --git a/ui-ngx/src/app/modules/home/pages/security/authentication-dialog/backup-code-auth-dialog.component.ts b/ui-ngx/src/app/modules/home/pages/security/authentication-dialog/backup-code-auth-dialog.component.ts new file mode 100644 index 0000000000..70c2888ac5 --- /dev/null +++ b/ui-ngx/src/app/modules/home/pages/security/authentication-dialog/backup-code-auth-dialog.component.ts @@ -0,0 +1,65 @@ +/// +/// Copyright © 2016-2022 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { Component } from '@angular/core'; +import { DialogComponent } from '@shared/components/dialog.component'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { Router } from '@angular/router'; +import { TwoFactorAuthenticationService } from '@core/http/two-factor-authentication.service'; +import { MatDialogRef } from '@angular/material/dialog'; +import { FormBuilder } from '@angular/forms'; +import { + AccountTwoFaSettings, + BackupCodeTwoFactorAuthAccountConfig, + TwoFactorAuthProviderType +} from '@shared/models/two-factor-auth.models'; +import { mergeMap, tap } from 'rxjs/operators'; +import { ImportExportService } from '@home/components/import-export/import-export.service'; + +@Component({ + selector: 'tb-backup-code-auth-dialog', + templateUrl: './backup-code-auth-dialog.component.html', + styleUrls: ['./authentication-dialog.component.scss'] +}) +export class BackupCodeAuthDialogComponent extends DialogComponent { + + private config: AccountTwoFaSettings; + backupCode: BackupCodeTwoFactorAuthAccountConfig; + + constructor(protected store: Store, + protected router: Router, + private twoFaService: TwoFactorAuthenticationService, + private importExportService: ImportExportService, + public dialogRef: MatDialogRef, + public fb: FormBuilder) { + super(store, router, dialogRef); + this.twoFaService.generateTwoFaAccountConfig(TwoFactorAuthProviderType.BACKUP_CODE).pipe( + tap((data: BackupCodeTwoFactorAuthAccountConfig) => this.backupCode = data), + mergeMap(data => this.twoFaService.verifyAndSaveTwoFaAccountConfig(data, null, {ignoreLoading: true})) + ).subscribe((config) => { + this.config = config; + }); + } + + closeDialog() { + this.dialogRef.close(this.config); + } + + downloadFile() { + this.importExportService.exportText(this.backupCode.codes, 'backup-codes'); + } +} diff --git a/ui-ngx/src/app/modules/home/pages/security/authentication-dialog/email-auth-dialog.component.html b/ui-ngx/src/app/modules/home/pages/security/authentication-dialog/email-auth-dialog.component.html index 39a46340d9..963374f49d 100644 --- a/ui-ngx/src/app/modules/home/pages/security/authentication-dialog/email-auth-dialog.component.html +++ b/ui-ngx/src/app/modules/home/pages/security/authentication-dialog/email-auth-dialog.component.html @@ -80,7 +80,7 @@ type="submit" color="primary" [disabled]="(isLoading$ | async) || emailVerificationForm.invalid"> - {{ 'security.2fa.dialog.activate' | translate }} + {{ 'action.activate' | translate }}
@@ -94,8 +94,8 @@
diff --git a/ui-ngx/src/app/modules/home/pages/security/authentication-dialog/email-auth-dialog.component.ts b/ui-ngx/src/app/modules/home/pages/security/authentication-dialog/email-auth-dialog.component.ts index 166ff799b7..385ea0e818 100644 --- a/ui-ngx/src/app/modules/home/pages/security/authentication-dialog/email-auth-dialog.component.ts +++ b/ui-ngx/src/app/modules/home/pages/security/authentication-dialog/email-auth-dialog.component.ts @@ -22,7 +22,11 @@ import { Router } from '@angular/router'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { TwoFactorAuthenticationService } from '@core/http/two-factor-authentication.service'; -import { TwoFactorAuthAccountConfig, TwoFactorAuthProviderType } from '@shared/models/two-factor-auth.models'; +import { + AccountTwoFaSettings, + TwoFactorAuthAccountConfig, + TwoFactorAuthProviderType +} from '@shared/models/two-factor-auth.models'; import { MatStepper } from '@angular/material/stepper'; export interface EmailAuthDialogData { @@ -37,6 +41,7 @@ export interface EmailAuthDialogData { export class EmailAuthDialogComponent extends DialogComponent { private authAccountConfig: TwoFactorAuthAccountConfig; + private config: AccountTwoFaSettings; emailConfigForm: FormGroup; emailVerificationForm: FormGroup; @@ -84,9 +89,10 @@ export class EmailAuthDialogComponent extends DialogComponent { - this.stepper.next(); - }); + this.emailVerificationForm.get('verificationCode').value).subscribe((config) => { + this.config = config; + this.stepper.next(); + }); } else { this.showFormErrors(this.emailVerificationForm); } @@ -95,7 +101,7 @@ export class EmailAuthDialogComponent extends DialogComponent 1); + return this.dialogRef.close(this.config); } private showFormErrors(form: FormGroup) { diff --git a/ui-ngx/src/app/modules/home/pages/security/authentication-dialog/sms-auth-dialog.component.html b/ui-ngx/src/app/modules/home/pages/security/authentication-dialog/sms-auth-dialog.component.html index 49d8ee661f..50256ccc39 100644 --- a/ui-ngx/src/app/modules/home/pages/security/authentication-dialog/sms-auth-dialog.component.html +++ b/ui-ngx/src/app/modules/home/pages/security/authentication-dialog/sms-auth-dialog.component.html @@ -82,7 +82,7 @@ type="submit" color="primary" [disabled]="(isLoading$ | async) || smsVerificationForm.invalid"> - {{ 'security.2fa.dialog.activate' | translate }} + {{ 'action.activate' | translate }}
@@ -96,8 +96,8 @@ diff --git a/ui-ngx/src/app/modules/home/pages/security/authentication-dialog/sms-auth-dialog.component.ts b/ui-ngx/src/app/modules/home/pages/security/authentication-dialog/sms-auth-dialog.component.ts index 030b67fd7a..7ab946b86f 100644 --- a/ui-ngx/src/app/modules/home/pages/security/authentication-dialog/sms-auth-dialog.component.ts +++ b/ui-ngx/src/app/modules/home/pages/security/authentication-dialog/sms-auth-dialog.component.ts @@ -22,7 +22,11 @@ import { Router } from '@angular/router'; import { MatDialogRef } from '@angular/material/dialog'; import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { TwoFactorAuthenticationService } from '@core/http/two-factor-authentication.service'; -import { TwoFactorAuthAccountConfig, TwoFactorAuthProviderType } from '@shared/models/two-factor-auth.models'; +import { + AccountTwoFaSettings, + TwoFactorAuthAccountConfig, + TwoFactorAuthProviderType +} from '@shared/models/two-factor-auth.models'; import { phoneNumberPattern } from '@shared/models/settings.models'; import { MatStepper } from '@angular/material/stepper'; @@ -34,6 +38,7 @@ import { MatStepper } from '@angular/material/stepper'; export class SMSAuthDialogComponent extends DialogComponent { private authAccountConfig: TwoFactorAuthAccountConfig; + private config: AccountTwoFaSettings; phoneNumberPattern = phoneNumberPattern; @@ -82,9 +87,10 @@ export class SMSAuthDialogComponent extends DialogComponent { - this.stepper.next(); - }); + this.smsVerificationForm.get('verificationCode').value).subscribe((config) => { + this.config = config; + this.stepper.next(); + }); } else { this.showFormErrors(this.smsVerificationForm); } @@ -93,7 +99,7 @@ export class SMSAuthDialogComponent extends DialogComponent 1); + return this.dialogRef.close(this.config); } private showFormErrors(form: FormGroup) { diff --git a/ui-ngx/src/app/modules/home/pages/security/authentication-dialog/totp-auth-dialog.component.html b/ui-ngx/src/app/modules/home/pages/security/authentication-dialog/totp-auth-dialog.component.html index 1a788c8ba5..695d39860f 100644 --- a/ui-ngx/src/app/modules/home/pages/security/authentication-dialog/totp-auth-dialog.component.html +++ b/ui-ngx/src/app/modules/home/pages/security/authentication-dialog/totp-auth-dialog.component.html @@ -84,8 +84,8 @@ diff --git a/ui-ngx/src/app/modules/home/pages/security/authentication-dialog/totp-auth-dialog.component.ts b/ui-ngx/src/app/modules/home/pages/security/authentication-dialog/totp-auth-dialog.component.ts index 52aab77b57..8bda837df3 100644 --- a/ui-ngx/src/app/modules/home/pages/security/authentication-dialog/totp-auth-dialog.component.ts +++ b/ui-ngx/src/app/modules/home/pages/security/authentication-dialog/totp-auth-dialog.component.ts @@ -22,7 +22,11 @@ import { Router } from '@angular/router'; import { MatDialogRef } from '@angular/material/dialog'; import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { TwoFactorAuthenticationService } from '@core/http/two-factor-authentication.service'; -import { TotpTwoFactorAuthAccountConfig, TwoFactorAuthProviderType } from '@shared/models/two-factor-auth.models'; +import { + AccountTwoFaSettings, + TotpTwoFactorAuthAccountConfig, + TwoFactorAuthProviderType +} from '@shared/models/two-factor-auth.models'; import { MatStepper } from '@angular/material/stepper'; @Component({ @@ -33,6 +37,7 @@ import { MatStepper } from '@angular/material/stepper'; export class TotpAuthDialogComponent extends DialogComponent { private authAccountConfig: TotpTwoFactorAuthAccountConfig; + private config: AccountTwoFaSettings; totpConfigForm: FormGroup; totpAuthURL: string; @@ -69,7 +74,8 @@ export class TotpAuthDialogComponent extends DialogComponent { + this.totpConfigForm.get('verificationCode').value).subscribe((config) => { + this.config = config; this.stepper.next(); }); } else { @@ -81,7 +87,7 @@ export class TotpAuthDialogComponent extends DialogComponent 1); + return this.dialogRef.close(this.config); } } diff --git a/ui-ngx/src/app/modules/home/pages/security/security.component.html b/ui-ngx/src/app/modules/home/pages/security/security.component.html index 6d5f57213f..8daa2f63ae 100644 --- a/ui-ngx/src/app/modules/home/pages/security/security.component.html +++ b/ui-ngx/src/app/modules/home/pages/security/security.component.html @@ -67,9 +67,15 @@ [checked]="useByDefault === provider" (click)="changeDefaultProvider($event, provider)" [disabled]="(isLoading$ | async) || activeSingleProvider" - *ngIf="twoFactorAuth.get(provider).value"> + *ngIf="twoFactorAuth.get(provider).value && provider !== twoFactorAuthProviderType.BACKUP_CODE"> security.2fa.main-2fa-method + diff --git a/ui-ngx/src/app/modules/home/pages/security/security.component.scss b/ui-ngx/src/app/modules/home/pages/security/security.component.scss index b95909c356..4688e46fae 100644 --- a/ui-ngx/src/app/modules/home/pages/security/security.component.scss +++ b/ui-ngx/src/app/modules/home/pages/security/security.component.scss @@ -29,13 +29,6 @@ @media #{$mat-gt-xl} { width: 45%; } - .title-container { - margin: 0; - } - .profile-email { - font-size: 16px; - font-weight: 400; - } .mat-subheader { line-height: 24px; color: rgba(0,0,0,0.54); @@ -52,22 +45,6 @@ opacity: 0.6; padding: 8px 0; } - .tb-home-dashboard { - tb-dashboard-autocomplete { - @media #{$mat-gt-sm} { - padding-right: 12px; - } - - @media #{$mat-lt-md} { - padding-bottom: 12px; - } - } - mat-checkbox { - @media #{$mat-gt-sm} { - margin-top: 16px; - } - } - } } .description { @@ -76,13 +53,6 @@ margin-right: 8px; } - .auth-title { - font-weight: 500; - line-height: 20px; - letter-spacing: 0.25px; - margin: 0; - } - .mat-divider-horizontal { left: 16px; right: 16px; @@ -111,5 +81,9 @@ .mat-checkbox { margin-top: 8px; } + + .mat-stroked-button { + margin-top: 8px; + } } } diff --git a/ui-ngx/src/app/modules/home/pages/security/security.component.ts b/ui-ngx/src/app/modules/home/pages/security/security.component.ts index 412bc305a3..e142dcbb1c 100644 --- a/ui-ngx/src/app/modules/home/pages/security/security.component.ts +++ b/ui-ngx/src/app/modules/home/pages/security/security.component.ts @@ -35,7 +35,8 @@ import { } from '@shared/models/two-factor-auth.models'; import { authenticationDialogMap } from '@home/pages/security/authentication-dialog/authentication-dialog.map'; import { takeUntil, tap } from 'rxjs/operators'; -import { Subject } from 'rxjs'; +import { Observable, of, Subject } from 'rxjs'; +import { isDefinedAndNotNull } from '@core/utils'; @Component({ selector: 'tb-security', @@ -45,11 +46,13 @@ import { Subject } from 'rxjs'; export class SecurityComponent extends PageComponent implements OnInit, OnDestroy { private readonly destroy$ = new Subject(); + private accountConfig: AccountTwoFaSettings; twoFactorAuth: FormGroup; user: User; allowTwoFactorProviders: TwoFactorAuthProviderType[] = []; providersData = twoFactorAuthProvidersData; + twoFactorAuthProviderType = TwoFactorAuthProviderType; useByDefault: TwoFactorAuthProviderType = null; activeSingleProvider = true; @@ -94,13 +97,19 @@ export class SecurityComponent extends PageComponent implements OnInit, OnDestro this.twoFactorAuth = this.fb.group({ TOTP: [false], SMS: [false], - EMAIL: [false] + EMAIL: [false], + BACKUP_CODE: [{value: false, disabled: true}] }); this.twoFactorAuth.valueChanges.pipe( takeUntil(this.destroy$) ).subscribe((value: {TwoFactorAuthProviderType: boolean}) => { - const formActiveValue = Object.values(value).filter(item => item); + const formActiveValue = Object.keys(value).filter(item => value[item] && item !== TwoFactorAuthProviderType.BACKUP_CODE); this.activeSingleProvider = formActiveValue.length < 2; + if (formActiveValue.length) { + this.twoFactorAuth.get('BACKUP_CODE').enable({emitEvent: false}); + } else { + this.twoFactorAuth.get('BACKUP_CODE').disable({emitEvent: false}); + } }); } @@ -116,7 +125,8 @@ export class SecurityComponent extends PageComponent implements OnInit, OnDestro } private processTwoFactorAuthConfig(setting: AccountTwoFaSettings) { - const configs = setting.configs; + this.accountConfig = setting; + const configs = this.accountConfig.configs; Object.values(TwoFactorAuthProviderType).forEach(provider => { if (configs[provider]) { this.twoFactorAuth.get(provider).setValue(true); @@ -171,20 +181,23 @@ export class SecurityComponent extends PageComponent implements OnInit, OnDestro } }); } else { - const dialogData = provider === TwoFactorAuthProviderType.EMAIL ? {email: this.user.email} : {}; - this.dialog.open(authenticationDialogMap.get(provider), { - disableClose: true, - panelClass: ['tb-dialog', 'tb-fullscreen-dialog'], - data: dialogData - }).afterClosed().subscribe(res => { - if (res) { - this.twoFactorAuth.get(provider).setValue(res); - this.useByDefault = provider; - } - }); + this.createdNewAuthConfig(provider); } } + private createdNewAuthConfig(provider: TwoFactorAuthProviderType) { + const dialogData = provider === TwoFactorAuthProviderType.EMAIL ? {email: this.user.email} : {}; + this.dialog.open(authenticationDialogMap.get(provider), { + disableClose: true, + panelClass: ['tb-dialog', 'tb-fullscreen-dialog'], + data: dialogData + }).afterClosed().subscribe(res => { + if (isDefinedAndNotNull(res)) { + this.processTwoFactorAuthConfig(res); + } + }); + } + changeDefaultProvider(event: MouseEvent, provider: TwoFactorAuthProviderType) { event.stopPropagation(); event.preventDefault(); @@ -195,4 +208,27 @@ export class SecurityComponent extends PageComponent implements OnInit, OnDestro .subscribe(data => this.processTwoFactorAuthConfig(data)); } } + + generateNewBackupCode() { + const codeLeft = this.accountConfig.configs[TwoFactorAuthProviderType.BACKUP_CODE].codesLeft; + let subscription: Observable; + if (codeLeft) { + subscription = this.dialogService.confirm( + 'Get new set of backup codes?', + `If you get new backup codes, ${codeLeft} remaining codes you have left will be unusable.`, + '', + 'Get new codes' + ); + } else { + subscription = of(true); + } + subscription.subscribe(res => { + if (res) { + this.twoFactorAuth.disable({emitEvent: false}); + this.twoFaService.deleteTwoFaAccountConfig(TwoFactorAuthProviderType.BACKUP_CODE) + .pipe(tap(() => this.twoFactorAuth.enable({emitEvent: false}))) + .subscribe(() => this.createdNewAuthConfig(TwoFactorAuthProviderType.BACKUP_CODE)); + } + }); + } } diff --git a/ui-ngx/src/app/modules/home/pages/security/security.module.ts b/ui-ngx/src/app/modules/home/pages/security/security.module.ts index 2df54747a7..5db12c2a68 100644 --- a/ui-ngx/src/app/modules/home/pages/security/security.module.ts +++ b/ui-ngx/src/app/modules/home/pages/security/security.module.ts @@ -22,13 +22,17 @@ import { SecurityRoutingModule } from './security-routing.module'; import { TotpAuthDialogComponent } from './authentication-dialog/totp-auth-dialog.component'; import { SMSAuthDialogComponent } from '@home/pages/security/authentication-dialog/sms-auth-dialog.component'; import { EmailAuthDialogComponent } from '@home/pages/security/authentication-dialog/email-auth-dialog.component'; +import { + BackupCodeAuthDialogComponent +} from '@home/pages/security/authentication-dialog/backup-code-auth-dialog.component'; @NgModule({ declarations: [ SecurityComponent, TotpAuthDialogComponent, SMSAuthDialogComponent, - EmailAuthDialogComponent + EmailAuthDialogComponent, + BackupCodeAuthDialogComponent ], imports: [ CommonModule, diff --git a/ui-ngx/src/app/modules/login/pages/login/two-factor-auth-login.component.html b/ui-ngx/src/app/modules/login/pages/login/two-factor-auth-login.component.html index 78417564b3..cf9e3186ba 100644 --- a/ui-ngx/src/app/modules/login/pages/login/two-factor-auth-login.component.html +++ b/ui-ngx/src/app/modules/login/pages/login/two-factor-auth-login.component.html @@ -42,11 +42,11 @@ - - - - - - - - - +
+ +
{ }; constructor(private store: Store, @@ -64,11 +66,17 @@ export class TenantProfileDataComponent implements ControlValueAccessor, OnInit this.tenantProfileDataFormGroup = this.fb.group({ configuration: [null, Validators.required] }); - this.tenantProfileDataFormGroup.valueChanges.subscribe(() => { + this.valueChange$ = this.tenantProfileDataFormGroup.valueChanges.subscribe(() => { this.updateModel(); }); } + ngOnDestroy() { + if (this.valueChange$) { + this.valueChange$.unsubscribe(); + } + } + setDisabledState(isDisabled: boolean): void { this.disabled = isDisabled; if (this.disabled) { @@ -87,7 +95,7 @@ export class TenantProfileDataComponent implements ControlValueAccessor, OnInit if (this.tenantProfileDataFormGroup.valid) { tenantProfileData = this.tenantProfileDataFormGroup.getRawValue(); } - this.propagateChange(tenantProfileData); + this.propagateChange(tenantProfileData.configuration); } } diff --git a/ui-ngx/src/app/modules/home/components/profile/tenant-profile.component.scss b/ui-ngx/src/app/modules/home/components/profile/tenant-profile.component.scss index c76509e9e3..f9940b3dae 100644 --- a/ui-ngx/src/app/modules/home/components/profile/tenant-profile.component.scss +++ b/ui-ngx/src/app/modules/home/components/profile/tenant-profile.component.scss @@ -35,6 +35,10 @@ width: fit-content; } } + + .mat-expansion-panel-header { + height: 48px; + } .expansion-panel-block { padding-bottom: 16px; } diff --git a/ui-ngx/src/app/modules/home/components/profile/tenant-profile.component.ts b/ui-ngx/src/app/modules/home/components/profile/tenant-profile.component.ts index 6a57ee57d0..ed88fe9fba 100644 --- a/ui-ngx/src/app/modules/home/components/profile/tenant-profile.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/tenant-profile.component.ts @@ -23,6 +23,7 @@ import { ActionNotificationShow } from '@app/core/notification/notification.acti import { TranslateService } from '@ngx-translate/core'; import { EntityTableConfig } from '@home/models/entity/entities-table-config.models'; import { EntityComponent } from '../entity/entity.component'; +import { guid } from '@core/utils'; @Component({ selector: 'tb-tenant-profile', @@ -54,6 +55,7 @@ export class TenantProfileComponent extends EntityComponent { buildForm(entity: TenantProfile): FormGroup { const mainQueue = [ { + id: guid(), consumerPerPartition: true, name: 'Main', packProcessingTimeout: 2000, @@ -70,7 +72,10 @@ export class TenantProfileComponent extends EntityComponent { batchSize: 1000, type: 'BURST' }, - topic: 'tb_rule_engine.main' + topic: 'tb_rule_engine.main', + additionalInfo: { + description: '' + } } ]; const formGroup = this.fb.group( diff --git a/ui-ngx/src/app/modules/home/components/queue/queue-form.component.html b/ui-ngx/src/app/modules/home/components/queue/queue-form.component.html index e13faf0f43..920ad2f162 100644 --- a/ui-ngx/src/app/modules/home/components/queue/queue-form.component.html +++ b/ui-ngx/src/app/modules/home/components/queue/queue-form.component.html @@ -15,161 +15,182 @@ limitations under the License. --> - -
- - admin.queue-name - - - {{ 'queue.name-required' | translate }} - - - - queue.poll-interval - - - {{ 'queue.poll-interval-required' | translate }} - - - {{ 'queue.poll-interval-min-value' | translate }} - - - - queue.partitions - - - {{ 'queue.partitions-required' | translate }} - - - {{ 'queue.partitions-min-value' | translate }} - - - -
{{ 'queue.consumer-per-partition' | translate }}
-
{{'queue.consumer-per-partition-hint' | translate}}
-
- - queue.processing-timeout - - - {{ 'queue.pack-processing-timeout-required' | translate }} - - - {{ 'queue.pack-processing-timeout-min-value' | translate }} - - - - - - - queue.submit-strategy - - - -
- - queue.submit-strategy - - - {{ strategy }} - - - - {{ 'queue.submit-strategy-type-required' | translate }} - - - - queue.batch-size - - - {{ 'queue.batch-size-required' | translate }} - - - {{ 'queue.batch-size-min-value' | translate }} - - -
-
-
- - - - queue.processing-strategy - - - -
- - queue.processing-strategy - - - {{ strategy }} - - - - {{ 'queue.processing-strategy-type-required' | translate }} - - - - queue.retries - - - {{ 'queue.retries-required' | translate }} - - - {{ 'queue.retries-min-value' | translate }} - - - - queue.failure-percentage - - - {{ 'queue.failure-percentage-required' | translate }} - - - {{ 'queue.failure-percentage-min-value' | translate }} - - - {{ 'queue.failure-percentage-max-value' | translate }} - - - - queue.pause-between-retries - - - {{ 'queue.pause-between-retries-required' | translate }} - - - {{ 'queue.pause-between-retries-min-value' | translate }} - - - - queue.max-pause-between-retries - - - {{ 'queue.max-pause-between-retries-required' | translate }} - - - {{ 'queue.max-pause-between-retries-min-value' | translate }} - - -
-
-
-
- - queue.description - - - + + +
+ + {{ queueTitle }} + + + +
+
+ +
+ + admin.queue-name + + + {{ 'queue.name-required' | translate }} + + + {{ 'queue.name-unique' | translate }} + + + + queue.poll-interval + + + {{ 'queue.poll-interval-required' | translate }} + + + {{ 'queue.poll-interval-min-value' | translate }} + + + + queue.partitions + + + {{ 'queue.partitions-required' | translate }} + + + {{ 'queue.partitions-min-value' | translate }} + + + +
{{ 'queue.consumer-per-partition' | translate }}
+
{{'queue.consumer-per-partition-hint' | translate}}
+
+ + queue.processing-timeout + + + {{ 'queue.pack-processing-timeout-required' | translate }} + + + {{ 'queue.pack-processing-timeout-min-value' | translate }} + + + + + + + queue.submit-strategy + + + +
+ + queue.submit-strategy + + + {{ strategy }} + + + + {{ 'queue.submit-strategy-type-required' | translate }} + + + + queue.batch-size + + + {{ 'queue.batch-size-required' | translate }} + + + {{ 'queue.batch-size-min-value' | translate }} + + +
+
+
+ + + + queue.processing-strategy + + + +
+ + queue.processing-strategy + + + {{ strategy }} + + + + {{ 'queue.processing-strategy-type-required' | translate }} + + + + queue.retries + + + {{ 'queue.retries-required' | translate }} + + + {{ 'queue.retries-min-value' | translate }} + + + + queue.failure-percentage + + + {{ 'queue.failure-percentage-required' | translate }} + + + {{ 'queue.failure-percentage-min-value' | translate }} + + + {{ 'queue.failure-percentage-max-value' | translate }} + + + + queue.pause-between-retries + + + {{ 'queue.pause-between-retries-required' | translate }} + + + {{ 'queue.pause-between-retries-min-value' | translate }} + + + + queue.max-pause-between-retries + + + {{ 'queue.max-pause-between-retries-required' | translate }} + + + {{ 'queue.max-pause-between-retries-min-value' | translate }} + + +
+
+
+
+ + queue.description + + + +
+
diff --git a/ui-ngx/src/app/modules/home/components/queue/queue-form.component.ts b/ui-ngx/src/app/modules/home/components/queue/queue-form.component.ts index 38552bff25..6a437799b5 100644 --- a/ui-ngx/src/app/modules/home/components/queue/queue-form.component.ts +++ b/ui-ngx/src/app/modules/home/components/queue/queue-form.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, forwardRef, Input, OnInit } from '@angular/core'; +import { Component, forwardRef, Input, OnInit, Output, EventEmitter, OnDestroy } from '@angular/core'; import { ControlValueAccessor, FormBuilder, @@ -29,6 +29,7 @@ import { MatDialog } from '@angular/material/dialog'; import { UtilsService } from '@core/services/utils.service'; import { QueueInfo, QueueProcessingStrategyTypes, QueueSubmitStrategyTypes } from '@shared/models/queue.models'; import { isDefinedAndNotNull } from '@core/utils'; +import { Subscription } from 'rxjs'; @Component({ selector: 'tb-queue-form', @@ -47,7 +48,7 @@ import { isDefinedAndNotNull } from '@core/utils'; } ] }) -export class QueueFormComponent implements ControlValueAccessor, OnInit, Validator { +export class QueueFormComponent implements ControlValueAccessor, OnInit, OnDestroy, Validator { @Input() disabled: boolean; @@ -55,20 +56,28 @@ export class QueueFormComponent implements ControlValueAccessor, OnInit, Validat @Input() newQueue = false; + @Input() + mainQueue = false; + @Input() systemQueue = false; - private modelValue: QueueInfo; + @Input() + expanded = false; - queueFormGroup: FormGroup; + @Output() + removeQueue = new EventEmitter(); + queueFormGroup: FormGroup; submitStrategies: string[] = []; processingStrategies: string[] = []; - + queueTitle = ''; hideBatchSize = false; + private modelValue: QueueInfo; private propagateChange = null; private propagateChangePending = false; + private valueChange$: Subscription = null; constructor(private dialog: MatDialog, private utils: UtilsService, @@ -114,10 +123,13 @@ export class QueueFormComponent implements ControlValueAccessor, OnInit, Validat description: [''] }) }); - this.queueFormGroup.valueChanges.subscribe(() => { + this.valueChange$ = this.queueFormGroup.valueChanges.subscribe(() => { this.updateModel(); }); - this.queueFormGroup.get('name').valueChanges.subscribe((value) => this.queueFormGroup.patchValue({topic: `tb_rule_engine.${value}`})); + this.queueFormGroup.get('name').valueChanges.subscribe((value) => { + this.queueFormGroup.patchValue({topic: `tb_rule_engine.${value}`}); + this.queueTitle = this.utils.customTranslation(value, value); + }); this.queueFormGroup.get('submitStrategy').get('type').valueChanges.subscribe(() => { this.submitStrategyTypeChanged(); }); @@ -128,6 +140,13 @@ export class QueueFormComponent implements ControlValueAccessor, OnInit, Validat } } + ngOnDestroy() { + if (this.valueChange$) { + this.valueChange$.unsubscribe(); + this.valueChange$ = null; + } + } + setDisabledState(isDisabled: boolean): void { this.disabled = isDisabled; if (this.disabled) { @@ -141,6 +160,10 @@ export class QueueFormComponent implements ControlValueAccessor, OnInit, Validat writeValue(value: QueueInfo): void { this.propagateChangePending = false; this.modelValue = value; + if (!this.modelValue.name) { + this.expanded = true; + } + this.queueTitle = this.utils.customTranslation(value.name, value.name); if (isDefinedAndNotNull(this.modelValue)) { this.queueFormGroup.patchValue(this.modelValue, {emitEvent: false}); } diff --git a/ui-ngx/src/app/modules/home/pages/tenant-profile/tenant-profiles-table-config.resolver.ts b/ui-ngx/src/app/modules/home/pages/tenant-profile/tenant-profiles-table-config.resolver.ts index 3b1773842d..7484d8b403 100644 --- a/ui-ngx/src/app/modules/home/pages/tenant-profile/tenant-profiles-table-config.resolver.ts +++ b/ui-ngx/src/app/modules/home/pages/tenant-profile/tenant-profiles-table-config.resolver.ts @@ -33,6 +33,8 @@ import { TenantProfileComponent } from '../../components/profile/tenant-profile. import { TenantProfileTabsComponent } from './tenant-profile-tabs.component'; import { DialogService } from '@core/services/dialog.service'; import { ImportExportService } from '@home/components/import-export/import-export.service'; +import { map } from 'rxjs/operators'; +import { guid } from '@core/utils'; @Injectable() export class TenantProfilesTableConfigResolver implements Resolve> { @@ -84,7 +86,12 @@ export class TenantProfilesTableConfigResolver implements Resolve this.translate.instant('tenant-profile.delete-tenant-profiles-text'); this.config.entitiesFetchFunction = pageLink => this.tenantProfileService.getTenantProfiles(pageLink); - this.config.loadEntity = id => this.tenantProfileService.getTenantProfile(id.id); + this.config.loadEntity = id => this.tenantProfileService.getTenantProfile(id.id).pipe( + map(tenantProfile => ({ + ...tenantProfile, + profileData: {...tenantProfile.profileData, queueConfiguration: this.addId(tenantProfile.profileData.queueConfiguration)}, + })) + ); this.config.saveEntity = tenantProfile => this.tenantProfileService.saveTenantProfile(tenantProfile); this.config.deleteEntity = id => this.tenantProfileService.deleteTenantProfile(id.id); this.config.onEntityAction = action => this.onTenantProfileAction(action); @@ -93,6 +100,15 @@ export class TenantProfilesTableConfigResolver implements Resolve { + value.id = guid(); + queuesWithId.push(value); + }); + return queuesWithId; + } + resolve(): EntityTableConfig { this.config.tableTitle = this.translate.instant('tenant-profile.tenant-profiles'); diff --git a/ui-ngx/src/app/shared/components/queue/queue-autocomplete.component.html b/ui-ngx/src/app/shared/components/queue/queue-autocomplete.component.html index 471d191b61..c8af29fbd4 100644 --- a/ui-ngx/src/app/shared/components/queue/queue-autocomplete.component.html +++ b/ui-ngx/src/app/shared/components/queue/queue-autocomplete.component.html @@ -15,7 +15,7 @@ limitations under the License. --> - + - + [displayWith]="displayQueueFn" + > + - {{getDescription(queue)}} + {{getDescription(queue)}}
diff --git a/ui-ngx/src/app/shared/components/queue/queue-autocomplete.component.scss b/ui-ngx/src/app/shared/components/queue/queue-autocomplete.component.scss new file mode 100644 index 0000000000..45a4ab9808 --- /dev/null +++ b/ui-ngx/src/app/shared/components/queue/queue-autocomplete.component.scss @@ -0,0 +1,29 @@ +/** + * Copyright © 2016-2022 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. + */ + +::ng-deep { + .queue-option { + .mat-option-text { + display: inline; + } + .queue-option-description { + display: block; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + } +} diff --git a/ui-ngx/src/app/shared/components/queue/queue-autocomplete.component.ts b/ui-ngx/src/app/shared/components/queue/queue-autocomplete.component.ts index 0caa487d66..897e041649 100644 --- a/ui-ngx/src/app/shared/components/queue/queue-autocomplete.component.ts +++ b/ui-ngx/src/app/shared/components/queue/queue-autocomplete.component.ts @@ -36,7 +36,7 @@ import { emptyPageData } from '@shared/models/page/page-data'; @Component({ selector: 'tb-queue-autocomplete', templateUrl: './queue-autocomplete.component.html', - styleUrls: [], + styleUrls: ['./queue-autocomplete.component.scss'], providers: [{ provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => QueueAutocompleteComponent), @@ -207,7 +207,10 @@ export class QueueAutocompleteComponent implements ControlValueAccessor, OnInit getDescription(value) { return value.additionalInfo?.description ? value.additionalInfo.description : - `Submit Strategy: ${value.submitStrategy.type}, Processing Strategy: ${value.processingStrategy.type}`; + this.translate.instant( + 'queue.alt-description', + {submitStrategy: value.submitStrategy.type, processingStrategy: value.processingStrategy.type} + ); } clear() { diff --git a/ui-ngx/src/app/shared/models/queue.models.ts b/ui-ngx/src/app/shared/models/queue.models.ts index 86bd99a19d..6c67b4db1b 100644 --- a/ui-ngx/src/app/shared/models/queue.models.ts +++ b/ui-ngx/src/app/shared/models/queue.models.ts @@ -43,6 +43,7 @@ export enum QueueProcessingStrategyTypes { } export interface QueueInfo extends BaseData { + generatedId?: string; name: string; packProcessingTimeout: number; partitions: number; diff --git a/ui-ngx/src/app/shared/models/tenant.model.ts b/ui-ngx/src/app/shared/models/tenant.model.ts index 14c0d232df..33d1367c01 100644 --- a/ui-ngx/src/app/shared/models/tenant.model.ts +++ b/ui-ngx/src/app/shared/models/tenant.model.ts @@ -98,7 +98,7 @@ export function createTenantProfileConfiguration(type: TenantProfileType): Tenan export interface TenantProfileData { configuration: TenantProfileConfiguration; - queueConfiguration?: QueueInfo; + queueConfiguration?: Array; } export interface TenantProfile extends BaseData { diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index 93e1afe006..ff1f85b1f0 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -2773,6 +2773,7 @@ "select-name": "Select queue name", "name": "Name", "name-required": "Queue name is required!", + "name-unique": "Queue name is not unique!", "queue-required": "Queue is required!", "topic-required": "Queue topic is required!", "poll-interval-required": "Poll interval is required!", @@ -2819,7 +2820,8 @@ "delete": "Delete queue", "copyId": "Copy queue Id", "idCopiedMessage": "Queue Id has been copied to clipboard", - "description": "Description" + "description": "Description", + "alt-description": "Submit Strategy: {{submitStrategy}}, Processing Strategy: {{processingStrategy}}" }, "tenant": { "tenant": "Tenant", From 152f5bc2a821c91e362bf0cf9bf919e3f4a32cf0 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Thu, 26 May 2022 09:41:42 +0300 Subject: [PATCH 701/798] UI: Add action print backup code --- .../authentication-dialog.component.scss | 7 ++- .../backup-code-auth-dialog.component.html | 11 ++-- .../backup-code-auth-dialog.component.ts | 23 +++++++++ .../backup-code-print-template.raw | 50 +++++++++++++++++++ .../pages/security/security.component.html | 6 +-- .../assets/locale/locale.constant-en_US.json | 1 + 6 files changed, 90 insertions(+), 8 deletions(-) create mode 100644 ui-ngx/src/app/modules/home/pages/security/authentication-dialog/backup-code-print-template.raw diff --git a/ui-ngx/src/app/modules/home/pages/security/authentication-dialog/authentication-dialog.component.scss b/ui-ngx/src/app/modules/home/pages/security/authentication-dialog/authentication-dialog.component.scss index 0b27d00983..a9325b6468 100644 --- a/ui-ngx/src/app/modules/home/pages/security/authentication-dialog/authentication-dialog.component.scss +++ b/ui-ngx/src/app/modules/home/pages/security/authentication-dialog/authentication-dialog.component.scss @@ -80,10 +80,10 @@ max-width: 500px; .container { max-width: 500px; - margin: 40px 0 24px; + margin: 40px 0 8px; .code { letter-spacing: 0.25px; - padding: 0 30px; + padding: 0 24px; margin-bottom: 16px; font-family: Roboto Mono, "Helvetica Neue", monospace; &.even { @@ -91,6 +91,9 @@ } } } + .action-buttons { + margin-bottom: 40px; + } } & ::ng-deep { diff --git a/ui-ngx/src/app/modules/home/pages/security/authentication-dialog/backup-code-auth-dialog.component.html b/ui-ngx/src/app/modules/home/pages/security/authentication-dialog/backup-code-auth-dialog.component.html index 61701c9358..ab3ba1a797 100644 --- a/ui-ngx/src/app/modules/home/pages/security/authentication-dialog/backup-code-auth-dialog.component.html +++ b/ui-ngx/src/app/modules/home/pages/security/authentication-dialog/backup-code-auth-dialog.component.html @@ -34,13 +34,18 @@ {{ code }}
-

security.2fa.dialog.backup-code-warn

-
+
-
+

security.2fa.dialog.backup-code-warn

+
+ +
diff --git a/ui-ngx/src/app/modules/home/pages/security/authentication-dialog/backup-code-auth-dialog.component.ts b/ui-ngx/src/app/modules/home/pages/security/authentication-dialog/backup-code-auth-dialog.component.ts index 70c2888ac5..5d3a26b13d 100644 --- a/ui-ngx/src/app/modules/home/pages/security/authentication-dialog/backup-code-auth-dialog.component.ts +++ b/ui-ngx/src/app/modules/home/pages/security/authentication-dialog/backup-code-auth-dialog.component.ts @@ -29,6 +29,9 @@ import { } from '@shared/models/two-factor-auth.models'; import { mergeMap, tap } from 'rxjs/operators'; import { ImportExportService } from '@home/components/import-export/import-export.service'; +import { deepClone } from '@core/utils'; + +import printTemplate from '!raw-loader!./backup-code-print-template.raw'; @Component({ selector: 'tb-backup-code-auth-dialog', @@ -62,4 +65,24 @@ export class BackupCodeAuthDialogComponent extends DialogComponent `
${code}
`).join(''); + const printPage = printTemplate.replace('${codesBlock}', codeTemplate); + const newWindow = window.open('', 'Print backup code'); + + newWindow.document.open(); + newWindow.document.write(printPage); + + setTimeout(() => { + newWindow.print(); + + newWindow.document.close(); + + setTimeout(() => { + newWindow.close(); + }, 10); + }, 0); + } } diff --git a/ui-ngx/src/app/modules/home/pages/security/authentication-dialog/backup-code-print-template.raw b/ui-ngx/src/app/modules/home/pages/security/authentication-dialog/backup-code-print-template.raw new file mode 100644 index 0000000000..1ab7efc79f --- /dev/null +++ b/ui-ngx/src/app/modules/home/pages/security/authentication-dialog/backup-code-print-template.raw @@ -0,0 +1,50 @@ + + + + + Backup code + + + + + +
+
+

+ Backup codes +

+
+ ${codesBlock} +
+
+
+ + diff --git a/ui-ngx/src/app/modules/home/pages/security/security.component.html b/ui-ngx/src/app/modules/home/pages/security/security.component.html index 8daa2f63ae..4a43db2ce7 100644 --- a/ui-ngx/src/app/modules/home/pages/security/security.component.html +++ b/ui-ngx/src/app/modules/home/pages/security/security.component.html @@ -66,15 +66,15 @@ + [disabled]="(isLoading$ | async)" + *ngIf="twoFactorAuth.get(provider).value && provider !== twoFactorAuthProviderType.BACKUP_CODE && !activeSingleProvider"> security.2fa.main-2fa-method diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index 485960c2e2..07817603dd 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -2590,6 +2590,7 @@ "authenticate-with": "You can authenticate with:", "disable-2fa-provider-text": "Disabling {{name}} will make your account less secure", "disable-2fa-provider-title": "Are you sure you want to disable {{name}}?", + "get-new-code": "Get new code", "main-2fa-method": "Use as main two-factor authentication method", "dialog": { "activation-step-description-email": "The next time you login in, you will be prompted to enter the security code that will be sent to your email address.", From 67e969fd75167d9de29ef2c42acf491a6387b230 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Thu, 26 May 2022 13:59:53 +0300 Subject: [PATCH 702/798] UI: 2fa improvement mobile view; remove required maxVerificationFailuresBeforeUserLockout --- .../two-factor-auth-settings.component.html | 5 +--- .../two-factor-auth-settings.component.scss | 9 ++++++-- .../two-factor-auth-settings.component.ts | 1 - .../authentication-dialog.component.scss | 20 ++++++++++++++-- .../email-auth-dialog.component.html | 23 ++++++++++++------- .../sms-auth-dialog.component.html | 23 ++++++++++++------- .../pages/security/security.component.html | 7 +++++- .../pages/security/security.component.scss | 6 ++++- .../home/pages/security/security.component.ts | 22 ++++++++++++++++++ .../two-factor-auth-login.component.scss | 14 +++++++---- .../shared/models/two-factor-auth.models.ts | 15 ++++++++---- .../assets/locale/locale.constant-en_US.json | 7 ++++-- 12 files changed, 114 insertions(+), 38 deletions(-) diff --git a/ui-ngx/src/app/modules/home/pages/admin/two-factor-auth-settings.component.html b/ui-ngx/src/app/modules/home/pages/admin/two-factor-auth-settings.component.html index 1efa83a2eb..dde38b21f6 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/two-factor-auth-settings.component.html +++ b/ui-ngx/src/app/modules/home/pages/admin/two-factor-auth-settings.component.html @@ -140,10 +140,7 @@
admin.2fa.max-verification-failures-before-user-lockout - - - {{ 'admin.2fa.max-verification-failures-before-user-lockout-required' | translate }} - + diff --git a/ui-ngx/src/app/modules/home/pages/admin/two-factor-auth-settings.component.scss b/ui-ngx/src/app/modules/home/pages/admin/two-factor-auth-settings.component.scss index e29f039fde..86254926b7 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/two-factor-auth-settings.component.scss +++ b/ui-ngx/src/app/modules/home/pages/admin/two-factor-auth-settings.component.scss @@ -16,8 +16,7 @@ @import "../../../../../scss/constants"; -:host{ - +:host { mat-card.settings-card { @media #{$mat-md} { width: 90%; @@ -48,17 +47,22 @@ .mat-expansion-panel { box-shadow: none; margin: 1px 0 0; + &.provider { overflow: inherit; + .mat-expansion-panel-header { padding: 0 24px 0 8px; + &.mat-expanded { height: 48px; } + .mat-slide-toggle { margin-right: 8px; } } + .mat-expansion-panel-header-title { height: 40px; } @@ -73,6 +77,7 @@ .mat-expansion-panel-header > .mat-content { overflow: inherit; } + .mat-expansion-panel-body { padding: 0 16px 8px 8px; } diff --git a/ui-ngx/src/app/modules/home/pages/admin/two-factor-auth-settings.component.ts b/ui-ngx/src/app/modules/home/pages/admin/two-factor-auth-settings.component.ts index c3f49a0c61..7567d57d73 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/two-factor-auth-settings.component.ts +++ b/ui-ngx/src/app/modules/home/pages/admin/two-factor-auth-settings.component.ts @@ -114,7 +114,6 @@ export class TwoFactorAuthSettingsComponent extends PageComponent implements OnI private build2faSettingsForm(): void { this.twoFaFormGroup = this.fb.group({ maxVerificationFailuresBeforeUserLockout: [30, [ - Validators.required, Validators.pattern(/^\d*$/), Validators.min(0), Validators.max(65535) diff --git a/ui-ngx/src/app/modules/home/pages/security/authentication-dialog/authentication-dialog.component.scss b/ui-ngx/src/app/modules/home/pages/security/authentication-dialog/authentication-dialog.component.scss index a9325b6468..116db0fb85 100644 --- a/ui-ngx/src/app/modules/home/pages/security/authentication-dialog/authentication-dialog.component.scss +++ b/ui-ngx/src/app/modules/home/pages/security/authentication-dialog/authentication-dialog.component.scss @@ -13,7 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -:host{ +@import "../../../../../../scss/constants"; + +:host { .mat-toolbar > h2 { font-weight: 400; letter-spacing: 0.25px; @@ -30,9 +32,11 @@ &:not(:first-of-type) { margin: 0 0 8px; } + &.description { margin: 0; color: rgba(0, 0, 0, 0.54); + &:last-of-type { margin-bottom: 24px; } @@ -45,6 +49,13 @@ .code-container { max-width: 170px; + + &.full-width-xs { + @media #{$mat-xs} { + max-width: 100%; + width: 100%; + } + } } .result-title { @@ -63,6 +74,7 @@ .step-description { max-width: 450px; + &.input { margin: 12px 0 0; } @@ -78,26 +90,30 @@ .backup-code { max-width: 500px; + .container { max-width: 500px; margin: 40px 0 8px; + .code { letter-spacing: 0.25px; padding: 0 24px; margin-bottom: 16px; font-family: Roboto Mono, "Helvetica Neue", monospace; + &.even { text-align: right; } } } + .action-buttons { margin-bottom: 40px; } } & ::ng-deep { - .mat-horizontal-stepper-header{ + .mat-horizontal-stepper-header { pointer-events: none !important; } } diff --git a/ui-ngx/src/app/modules/home/pages/security/authentication-dialog/email-auth-dialog.component.html b/ui-ngx/src/app/modules/home/pages/security/authentication-dialog/email-auth-dialog.component.html index 963374f49d..45663845be 100644 --- a/ui-ngx/src/app/modules/home/pages/security/authentication-dialog/email-auth-dialog.component.html +++ b/ui-ngx/src/app/modules/home/pages/security/authentication-dialog/email-auth-dialog.component.html @@ -64,8 +64,8 @@

{{ 'security.2fa.dialog.verification-step-description' | translate : {address: emailConfigForm.get('email').value} }}

-
- +
+ - +
+ + +
diff --git a/ui-ngx/src/app/modules/home/pages/security/authentication-dialog/sms-auth-dialog.component.html b/ui-ngx/src/app/modules/home/pages/security/authentication-dialog/sms-auth-dialog.component.html index 50256ccc39..25a444257e 100644 --- a/ui-ngx/src/app/modules/home/pages/security/authentication-dialog/sms-auth-dialog.component.html +++ b/ui-ngx/src/app/modules/home/pages/security/authentication-dialog/sms-auth-dialog.component.html @@ -66,8 +66,8 @@

{{ 'security.2fa.dialog.verification-step-description' | translate : {address: smsConfigForm.get('phone').value} }}

-
- +
+ - +
+ + +
diff --git a/ui-ngx/src/app/modules/home/pages/security/security.component.html b/ui-ngx/src/app/modules/home/pages/security/security.component.html index 4a43db2ce7..8e279f9c75 100644 --- a/ui-ngx/src/app/modules/home/pages/security/security.component.html +++ b/ui-ngx/src/app/modules/home/pages/security/security.component.html @@ -56,9 +56,14 @@

{{ providersData.get(provider).name | translate }}

-
+
{{ providersData.get(provider).description | translate }}
+ +
+ {{ providersData.get(provider).activatedHint | translate: providerDataInfo(provider) }} +
+
diff --git a/ui-ngx/src/app/modules/home/pages/security/security.component.scss b/ui-ngx/src/app/modules/home/pages/security/security.component.scss index 4688e46fae..7dad927038 100644 --- a/ui-ngx/src/app/modules/home/pages/security/security.component.scss +++ b/ui-ngx/src/app/modules/home/pages/security/security.component.scss @@ -19,6 +19,7 @@ .profile-container { padding: 8px; } + mat-card.profile-card { @media #{$mat-gt-sm} { width: 70%; @@ -29,16 +30,19 @@ @media #{$mat-gt-xl} { width: 45%; } + .mat-subheader { line-height: 24px; - color: rgba(0,0,0,0.54); + color: rgba(0, 0, 0, 0.54); font-size: 14px; font-weight: 400; } + .profile-last-login-ts { font-size: 16px; font-weight: 400; } + .profile-btn-subtext { font: 400 14px / 16px Roboto, "Helvetica Neue", sans-serif; letter-spacing: 0.25px; diff --git a/ui-ngx/src/app/modules/home/pages/security/security.component.ts b/ui-ngx/src/app/modules/home/pages/security/security.component.ts index e142dcbb1c..34eda9781a 100644 --- a/ui-ngx/src/app/modules/home/pages/security/security.component.ts +++ b/ui-ngx/src/app/modules/home/pages/security/security.component.ts @@ -30,6 +30,9 @@ import { ClipboardService } from 'ngx-clipboard'; import { TwoFactorAuthenticationService } from '@core/http/two-factor-authentication.service'; import { AccountTwoFaSettings, + BackupCodeTwoFactorAuthAccountConfig, + EmailTwoFactorAuthAccountConfig, + SmsTwoFactorAuthAccountConfig, twoFactorAuthProvidersData, TwoFactorAuthProviderType } from '@shared/models/two-factor-auth.models'; @@ -231,4 +234,23 @@ export class SecurityComponent extends PageComponent implements OnInit, OnDestro } }); } + + providerDataInfo(provider: TwoFactorAuthProviderType) { + const info = {info: null}; + const providerConfig = this.accountConfig.configs[provider]; + if (isDefinedAndNotNull(providerConfig)) { + switch (provider) { + case TwoFactorAuthProviderType.EMAIL: + info.info = (providerConfig as EmailTwoFactorAuthAccountConfig).email; + break; + case TwoFactorAuthProviderType.SMS: + info.info = (providerConfig as SmsTwoFactorAuthAccountConfig).phoneNumber; + break; + case TwoFactorAuthProviderType.BACKUP_CODE: + info.info = (providerConfig as BackupCodeTwoFactorAuthAccountConfig).codesLeft; + break; + } + } + return info; + } } diff --git a/ui-ngx/src/app/modules/login/pages/login/two-factor-auth-login.component.scss b/ui-ngx/src/app/modules/login/pages/login/two-factor-auth-login.component.scss index 4d559f5435..864d099e3c 100644 --- a/ui-ngx/src/app/modules/login/pages/login/two-factor-auth-login.component.scss +++ b/ui-ngx/src/app/modules/login/pages/login/two-factor-auth-login.component.scss @@ -18,8 +18,10 @@ :host { display: flex; flex: 1 1 0; + .tb-two-factor-auth-login-content { background-color: #eee; + .tb-two-factor-auth-login-card { padding: 48px 48px 48px 16px; @@ -27,7 +29,7 @@ width: 450px !important; } - .mat-card-title{ + .mat-card-title { font: 400 28px / 36px Roboto, "Helvetica Neue", sans-serif; } @@ -46,7 +48,7 @@ margin-top: 16px; } - .providers-container{ + .providers-container { padding: 0; .mat-body { @@ -61,19 +63,23 @@ } } } - ::ng-deep{ + + ::ng-deep { button.provider { text-align: start; font-weight: 400; + &:not(.mat-button-disabled) { border-color: rgba(255, 255, 255, .8); } - .icon{ + + .icon { height: 18px; width: 18px; vertical-align: sub; } } + .mat-form-field-invalid .mat-hint { margin-top: 20px; } diff --git a/ui-ngx/src/app/shared/models/two-factor-auth.models.ts b/ui-ngx/src/app/shared/models/two-factor-auth.models.ts index fc56838899..10040e00e7 100644 --- a/ui-ngx/src/app/shared/models/two-factor-auth.models.ts +++ b/ui-ngx/src/app/shared/models/two-factor-auth.models.ts @@ -103,9 +103,10 @@ export interface TwoFaProviderInfo { export interface TwoFactorAuthProviderData { name: string; description: string; + activatedHint: string; } -export interface TwoFactorAuthProviderLoginData extends TwoFactorAuthProviderData { +export interface TwoFactorAuthProviderLoginData extends Omit { icon: string; placeholder: string; } @@ -115,25 +116,29 @@ export const twoFactorAuthProvidersData = new Map Date: Thu, 26 May 2022 15:25:05 +0300 Subject: [PATCH 703/798] refactoring: OtaPackageController --- .../controller/OtaPackageController.java | 56 +++++------ .../entitiy/AbstractTbEntityService.java | 3 + .../DefaultTbOtaPackageService.java | 94 +++++++++++++++++++ .../TbOtaPackageService.java | 32 +++++++ 4 files changed, 157 insertions(+), 28 deletions(-) create mode 100644 application/src/main/java/org/thingsboard/server/service/entitiy/otaPackageController/DefaultTbOtaPackageService.java create mode 100644 application/src/main/java/org/thingsboard/server/service/entitiy/otaPackageController/TbOtaPackageService.java diff --git a/application/src/main/java/org/thingsboard/server/controller/OtaPackageController.java b/application/src/main/java/org/thingsboard/server/controller/OtaPackageController.java index 281a13a6a0..722723e283 100644 --- a/application/src/main/java/org/thingsboard/server/controller/OtaPackageController.java +++ b/application/src/main/java/org/thingsboard/server/controller/OtaPackageController.java @@ -17,6 +17,7 @@ package org.thingsboard.server.controller; import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiParam; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.core.io.ByteArrayResource; @@ -45,6 +46,7 @@ import org.thingsboard.server.common.data.ota.OtaPackageType; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.entitiy.otaPackageController.TbOtaPackageService; import org.thingsboard.server.service.security.permission.Operation; import org.thingsboard.server.service.security.permission.Resource; @@ -73,8 +75,11 @@ import static org.thingsboard.server.controller.ControllerConstants.UUID_WIKI_LI @RestController @TbCoreComponent @RequestMapping("/api") +@RequiredArgsConstructor public class OtaPackageController extends BaseController { + private final TbOtaPackageService tbOtaPackageService; + public static final String OTA_PACKAGE_ID = "otaPackageId"; public static final String CHECKSUM_ALGORITHM = "checksumAlgorithm"; @@ -154,19 +159,12 @@ public class OtaPackageController extends BaseController { @ResponseBody public OtaPackageInfo saveOtaPackageInfo(@ApiParam(value = "A JSON value representing the OTA Package.") @RequestBody SaveOtaPackageInfoRequest otaPackageInfo) throws ThingsboardException { - boolean created = otaPackageInfo.getId() == null; - try { - otaPackageInfo.setTenantId(getTenantId()); - checkEntity(otaPackageInfo.getId(), otaPackageInfo, Resource.OTA_PACKAGE); - OtaPackageInfo savedOtaPackageInfo = otaPackageService.saveOtaPackageInfo(new OtaPackageInfo(otaPackageInfo), otaPackageInfo.isUsesUrl()); - logEntityAction(savedOtaPackageInfo.getId(), savedOtaPackageInfo, - null, created ? ActionType.ADDED : ActionType.UPDATED, null); - return savedOtaPackageInfo; - } catch (Exception e) { - logEntityAction(emptyId(EntityType.OTA_PACKAGE), otaPackageInfo, - null, created ? ActionType.ADDED : ActionType.UPDATED, e); - throw handleException(e); - } + otaPackageInfo.setTenantId(getTenantId()); + checkEntity(otaPackageInfo.getId(), otaPackageInfo, Resource.OTA_PACKAGE); + + return tbOtaPackageService.save(otaPackageInfo, getCurrentUser()); + + } @ApiOperation(value = "Save OTA Package data (saveOtaPackageData)", @@ -188,17 +186,17 @@ public class OtaPackageController extends BaseController { checkParameter(CHECKSUM_ALGORITHM, checksumAlgorithmStr); try { OtaPackageId otaPackageId = new OtaPackageId(toUUID(strOtaPackageId)); - OtaPackageInfo info = checkOtaPackageInfoId(otaPackageId, Operation.READ); + OtaPackageInfo otaPackageInfo = checkOtaPackageInfoId(otaPackageId, Operation.READ); OtaPackage otaPackage = new OtaPackage(otaPackageId); - otaPackage.setCreatedTime(info.getCreatedTime()); + otaPackage.setCreatedTime(otaPackageInfo.getCreatedTime()); otaPackage.setTenantId(getTenantId()); - otaPackage.setDeviceProfileId(info.getDeviceProfileId()); - otaPackage.setType(info.getType()); - otaPackage.setTitle(info.getTitle()); - otaPackage.setVersion(info.getVersion()); - otaPackage.setTag(info.getTag()); - otaPackage.setAdditionalInfo(info.getAdditionalInfo()); + otaPackage.setDeviceProfileId(otaPackageInfo.getDeviceProfileId()); + otaPackage.setType(otaPackageInfo.getType()); + otaPackage.setTitle(otaPackageInfo.getTitle()); + otaPackage.setVersion(otaPackageInfo.getVersion()); + otaPackage.setTag(otaPackageInfo.getTag()); + otaPackage.setAdditionalInfo(otaPackageInfo.getAdditionalInfo()); ChecksumAlgorithm checksumAlgorithm = ChecksumAlgorithm.valueOf(checksumAlgorithmStr.toUpperCase()); @@ -213,11 +211,9 @@ public class OtaPackageController extends BaseController { otaPackage.setContentType(file.getContentType()); otaPackage.setData(ByteBuffer.wrap(bytes)); otaPackage.setDataSize((long) bytes.length); - OtaPackageInfo savedOtaPackage = otaPackageService.saveOtaPackage(otaPackage); - logEntityAction(savedOtaPackage.getId(), savedOtaPackage, null, ActionType.UPDATED, null); - return savedOtaPackage; + return tbOtaPackageService.saveOtaPackageData(otaPackageId, otaPackage, getCurrentUser(), null); } catch (Exception e) { - logEntityAction(emptyId(EntityType.OTA_PACKAGE), null, null, ActionType.UPDATED, e, strOtaPackageId); + tbOtaPackageService.saveOtaPackageData(null, null, getCurrentUser(), e); throw handleException(e); } } @@ -289,11 +285,15 @@ public class OtaPackageController extends BaseController { public void deleteOtaPackage(@ApiParam(value = OTA_PACKAGE_ID_PARAM_DESCRIPTION) @PathVariable("otaPackageId") String strOtaPackageId) throws ThingsboardException { checkParameter(OTA_PACKAGE_ID, strOtaPackageId); + OtaPackageId otaPackageId = new OtaPackageId(toUUID(strOtaPackageId)); + OtaPackageInfo otaPackageInfo = checkOtaPackageInfoId(otaPackageId, Operation.DELETE); + + tbOtaPackageService.delete(otaPackageInfo, getCurrentUser()); try { - OtaPackageId otaPackageId = new OtaPackageId(toUUID(strOtaPackageId)); - OtaPackageInfo info = checkOtaPackageInfoId(otaPackageId, Operation.DELETE); + + otaPackageService.deleteOtaPackage(getTenantId(), otaPackageId); - logEntityAction(otaPackageId, info, null, ActionType.DELETED, null, strOtaPackageId); + logEntityAction(otaPackageId, otaPackageInfo, null, ActionType.DELETED, null, strOtaPackageId); } catch (Exception e) { logEntityAction(emptyId(EntityType.OTA_PACKAGE), null, null, ActionType.DELETED, e, strOtaPackageId); throw handleException(e); diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/AbstractTbEntityService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/AbstractTbEntityService.java index f19f5c603b..fdeb8ceb89 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/AbstractTbEntityService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/AbstractTbEntityService.java @@ -54,6 +54,7 @@ import org.thingsboard.server.dao.entityview.EntityViewService; import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.exception.IncorrectParameterException; import org.thingsboard.server.dao.model.ModelConstants; +import org.thingsboard.server.dao.ota.OtaPackageService; import org.thingsboard.server.dao.relation.RelationService; import org.thingsboard.server.dao.rule.RuleChainService; import org.thingsboard.server.dao.tenant.TbTenantProfileCache; @@ -132,6 +133,8 @@ public abstract class AbstractTbEntityService { protected OtaPackageStateService otaPackageStateService; @Autowired protected RelationService relationService; + @Autowired + protected OtaPackageService otaPackageService; protected ListenableFuture removeAlarmsByEntityId(TenantId tenantId, EntityId entityId) { ListenableFuture> alarmsFuture = diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/otaPackageController/DefaultTbOtaPackageService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/otaPackageController/DefaultTbOtaPackageService.java new file mode 100644 index 0000000000..8365ce470b --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/otaPackageController/DefaultTbOtaPackageService.java @@ -0,0 +1,94 @@ +/** + * Copyright © 2016-2022 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.service.entitiy.otaPackageController; + +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.OtaPackage; +import org.thingsboard.server.common.data.OtaPackageInfo; +import org.thingsboard.server.common.data.SaveOtaPackageInfoRequest; +import org.thingsboard.server.common.data.audit.ActionType; +import org.thingsboard.server.common.data.exception.ThingsboardException; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.OtaPackageId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.entitiy.AbstractTbEntityService; +import org.thingsboard.server.service.security.model.SecurityUser; + +@Service +@TbCoreComponent +@AllArgsConstructor +@Slf4j +public class DefaultTbOtaPackageService extends AbstractTbEntityService implements TbOtaPackageService { + @Override + public OtaPackageInfo save(SaveOtaPackageInfoRequest saveOtaPackageInfoRequest, SecurityUser user) throws ThingsboardException { + TenantId tenantId = saveOtaPackageInfoRequest.getTenantId(); + ActionType actionType = saveOtaPackageInfoRequest.getId() == null ? ActionType.ADDED : ActionType.UPDATED; + try { + OtaPackageInfo savedOtaPackageInfo = otaPackageService.saveOtaPackageInfo(new OtaPackageInfo(saveOtaPackageInfoRequest), saveOtaPackageInfoRequest.isUsesUrl()); + notificationEntityService.notifyEntity(tenantId, saveOtaPackageInfoRequest.getId(), saveOtaPackageInfoRequest, null, + actionType, user, null); + return savedOtaPackageInfo; + } catch (Exception e) { + notificationEntityService.notifyEntity(tenantId, emptyId(EntityType.OTA_PACKAGE), saveOtaPackageInfoRequest, null, + actionType, user, e); + throw handleException(e); + } + } + + @Override + public void delete(OtaPackageInfo otaPackageInfo, SecurityUser user) throws ThingsboardException { + TenantId tenantId = otaPackageInfo.getTenantId(); + OtaPackageId otaPackageId = otaPackageInfo.getId(); + try { + otaPackageService.deleteOtaPackage(tenantId, otaPackageId); + notificationEntityService.notifyEntity(tenantId, otaPackageId, otaPackageInfo, null, + ActionType.DELETED, user, null, otaPackageInfo.getId().toString()); + } catch (Exception e) { + notificationEntityService.notifyEntity(tenantId, emptyId(EntityType.OTA_PACKAGE), null, null, + ActionType.DELETED, user, e, otaPackageInfo.getId().toString()); + throw handleException(e); + } + + + } + + @Override + public OtaPackageInfo saveOtaPackageData(EntityId otaPackageId, OtaPackage otaPackage, SecurityUser user, Exception e) throws ThingsboardException { + TenantId tenantId = otaPackage.getTenantId(); + if (e == null) { + try { + OtaPackageInfo savedOtaPackage = otaPackageService.saveOtaPackage(otaPackage); + notificationEntityService.notifyEntity(tenantId, savedOtaPackage.getId(), savedOtaPackage, null, + ActionType.UPDATED, user, null); + return savedOtaPackage; + } catch (Exception e1) { + notificationEntityService.notifyEntity(tenantId, emptyId(EntityType.OTA_PACKAGE), null, null, + ActionType.UPDATED, user, e1, otaPackageId.toString()); + throw handleException(e1); + } + } else { + notificationEntityService.notifyEntity(tenantId, emptyId(EntityType.OTA_PACKAGE), null, null, + ActionType.UPDATED, user, e, otaPackageId.toString()); + throw handleException(e); + } + } + + +} diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/otaPackageController/TbOtaPackageService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/otaPackageController/TbOtaPackageService.java new file mode 100644 index 0000000000..f199d64e0e --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/otaPackageController/TbOtaPackageService.java @@ -0,0 +1,32 @@ +/** + * Copyright © 2016-2022 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.service.entitiy.otaPackageController; + +import org.thingsboard.server.common.data.OtaPackage; +import org.thingsboard.server.common.data.OtaPackageInfo; +import org.thingsboard.server.common.data.SaveOtaPackageInfoRequest; +import org.thingsboard.server.common.data.exception.ThingsboardException; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.service.security.model.SecurityUser; + +public interface TbOtaPackageService { + + OtaPackageInfo save(SaveOtaPackageInfoRequest saveOtaPackageInfoRequest, SecurityUser user) throws ThingsboardException; + + void delete(OtaPackageInfo otaPackageInfo, SecurityUser user) throws ThingsboardException; + + OtaPackageInfo saveOtaPackageData(EntityId otaPackageId, OtaPackage otaPackage, SecurityUser user, Exception e) throws ThingsboardException; +} From eeb7dc23382770017847158ea76b5925695ac4fb Mon Sep 17 00:00:00 2001 From: Viacheslav Klimov Date: Thu, 26 May 2022 16:13:24 +0300 Subject: [PATCH 704/798] Improvements for 2FA --- .../server/controller/TwoFaConfigController.java | 4 ++-- .../auth/mfa/config/DefaultTwoFaConfigManager.java | 12 ++++++++++-- .../security/auth/mfa/config/TwoFaConfigManager.java | 2 +- .../rest/RestAwareAuthenticationSuccessHandler.java | 4 +++- .../system/DefaultSystemSecurityService.java | 7 ++++--- .../security/model/mfa/PlatformTwoFaSettings.java | 2 +- .../mfa/provider/BackupCodeTwoFaProviderConfig.java | 2 +- 7 files changed, 22 insertions(+), 11 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/controller/TwoFaConfigController.java b/application/src/main/java/org/thingsboard/server/controller/TwoFaConfigController.java index 138b20b160..60239f3603 100644 --- a/application/src/main/java/org/thingsboard/server/controller/TwoFaConfigController.java +++ b/application/src/main/java/org/thingsboard/server/controller/TwoFaConfigController.java @@ -258,9 +258,9 @@ public class TwoFaConfigController extends BaseController { ControllerConstants.SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH) @PostMapping("/settings") @PreAuthorize("hasAnyAuthority('SYS_ADMIN')") - public void savePlatformTwoFaSettings(@ApiParam(value = "Settings value", required = true) + public PlatformTwoFaSettings savePlatformTwoFaSettings(@ApiParam(value = "Settings value", required = true) @RequestBody PlatformTwoFaSettings twoFaSettings) throws ThingsboardException { - twoFaConfigManager.savePlatformTwoFaSettings(getTenantId(), twoFaSettings); + return twoFaConfigManager.savePlatformTwoFaSettings(getTenantId(), twoFaSettings); } diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/mfa/config/DefaultTwoFaConfigManager.java b/application/src/main/java/org/thingsboard/server/service/security/auth/mfa/config/DefaultTwoFaConfigManager.java index bfe426d532..23e6dc065f 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/mfa/config/DefaultTwoFaConfigManager.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/mfa/config/DefaultTwoFaConfigManager.java @@ -99,6 +99,9 @@ public class DefaultTwoFaConfigManager implements TwoFaConfigManager { return newSettings; }); Map configs = settings.getConfigs(); + if (configs.isEmpty() && accountConfig.getProviderType() == TwoFaProviderType.BACKUP_CODE) { + throw new IllegalArgumentException("To use 2FA backup codes you first need to configure at least one provider"); + } if (accountConfig.isUseByDefault()) { configs.values().forEach(config -> config.setUseByDefault(false)); } @@ -114,7 +117,11 @@ public class DefaultTwoFaConfigManager implements TwoFaConfigManager { AccountTwoFaSettings settings = getAccountTwoFaSettings(tenantId, userId) .orElseThrow(() -> new IllegalArgumentException("2FA not configured")); settings.getConfigs().remove(providerType); - if (!settings.getConfigs().isEmpty() && settings.getConfigs().values().stream().noneMatch(TwoFaAccountConfig::isUseByDefault)) { + if (settings.getConfigs().size() == 1) { + settings.getConfigs().remove(TwoFaProviderType.BACKUP_CODE); + } + if (!settings.getConfigs().isEmpty() && settings.getConfigs().values().stream() + .noneMatch(TwoFaAccountConfig::isUseByDefault)) { settings.getConfigs().values().stream() .min(Comparator.comparing(TwoFaAccountConfig::getProviderType)) .ifPresent(config -> config.setUseByDefault(true)); @@ -135,7 +142,7 @@ public class DefaultTwoFaConfigManager implements TwoFaConfigManager { } @Override - public void savePlatformTwoFaSettings(TenantId tenantId, PlatformTwoFaSettings twoFactorAuthSettings) throws ThingsboardException { + public PlatformTwoFaSettings savePlatformTwoFaSettings(TenantId tenantId, PlatformTwoFaSettings twoFactorAuthSettings) throws ThingsboardException { ConstraintValidator.validateFields(twoFactorAuthSettings); for (TwoFaProviderConfig providerConfig : twoFactorAuthSettings.getProviders()) { twoFactorAuthService.checkProvider(tenantId, providerConfig.getProviderType()); @@ -149,6 +156,7 @@ public class DefaultTwoFaConfigManager implements TwoFaConfigManager { }); settings.setJsonValue(JacksonUtil.valueToTree(twoFactorAuthSettings)); adminSettingsService.saveAdminSettings(tenantId, settings); + return twoFactorAuthSettings; } @Override diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/mfa/config/TwoFaConfigManager.java b/application/src/main/java/org/thingsboard/server/service/security/auth/mfa/config/TwoFaConfigManager.java index 0fe33b7757..c0e3200a4d 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/mfa/config/TwoFaConfigManager.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/mfa/config/TwoFaConfigManager.java @@ -39,7 +39,7 @@ public interface TwoFaConfigManager { Optional getPlatformTwoFaSettings(TenantId tenantId, boolean sysadminSettingsAsDefault); - void savePlatformTwoFaSettings(TenantId tenantId, PlatformTwoFaSettings twoFactorAuthSettings) throws ThingsboardException; + PlatformTwoFaSettings savePlatformTwoFaSettings(TenantId tenantId, PlatformTwoFaSettings twoFactorAuthSettings) throws ThingsboardException; void deletePlatformTwoFaSettings(TenantId tenantId); diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/rest/RestAwareAuthenticationSuccessHandler.java b/application/src/main/java/org/thingsboard/server/service/security/auth/rest/RestAwareAuthenticationSuccessHandler.java index 4ab4069b61..b4f0b293d3 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/rest/RestAwareAuthenticationSuccessHandler.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/rest/RestAwareAuthenticationSuccessHandler.java @@ -55,7 +55,9 @@ public class RestAwareAuthenticationSuccessHandler implements AuthenticationSucc if (authentication instanceof MfaAuthenticationToken) { int preVerificationTokenLifetime = twoFaConfigManager.getPlatformTwoFaSettings(securityUser.getTenantId(), true) - .flatMap(settings -> Optional.ofNullable(settings.getTotalAllowedTimeForVerification())).orElse((int) TimeUnit.MINUTES.toSeconds(30)); + .flatMap(settings -> Optional.ofNullable(settings.getTotalAllowedTimeForVerification()) + .filter(time -> time > 0)) + .orElse((int) TimeUnit.MINUTES.toSeconds(30)); tokenPair.setToken(tokenFactory.createPreVerificationToken(securityUser, preVerificationTokenLifetime).getToken()); tokenPair.setRefreshToken(null); tokenPair.setScope(Authority.PRE_VERIFICATION_TOKEN); diff --git a/application/src/main/java/org/thingsboard/server/service/security/system/DefaultSystemSecurityService.java b/application/src/main/java/org/thingsboard/server/service/security/system/DefaultSystemSecurityService.java index 57d009070c..65b89c85b8 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/system/DefaultSystemSecurityService.java +++ b/application/src/main/java/org/thingsboard/server/service/security/system/DefaultSystemSecurityService.java @@ -172,11 +172,12 @@ public class DefaultSystemSecurityService implements SystemSecurityService { return; } - if (twoFaSettings.getMaxVerificationFailuresBeforeUserLockout() > 0 - && failedVerificationAttempts >= twoFaSettings.getMaxVerificationFailuresBeforeUserLockout()) { + Integer maxVerificationFailures = twoFaSettings.getMaxVerificationFailuresBeforeUserLockout(); + if (maxVerificationFailures != null && maxVerificationFailures > 0 + && failedVerificationAttempts >= maxVerificationFailures) { userService.setUserCredentialsEnabled(TenantId.SYS_TENANT_ID, userId, false); SecuritySettings securitySettings = self.getSecuritySettings(tenantId); - lockAccount(userId, securityUser.getEmail(), securitySettings.getUserLockoutNotificationEmail(), twoFaSettings.getMaxVerificationFailuresBeforeUserLockout()); + lockAccount(userId, securityUser.getEmail(), securitySettings.getUserLockoutNotificationEmail(), maxVerificationFailures); throw new LockedException("User account was locked due to exceeded 2FA verification attempts"); } } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/security/model/mfa/PlatformTwoFaSettings.java b/common/data/src/main/java/org/thingsboard/server/common/data/security/model/mfa/PlatformTwoFaSettings.java index 9d581c0867..930b858316 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/security/model/mfa/PlatformTwoFaSettings.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/security/model/mfa/PlatformTwoFaSettings.java @@ -37,7 +37,7 @@ public class PlatformTwoFaSettings { @Pattern(regexp = "[1-9]\\d*:[1-9]\\d*", message = "verification code check rate limit configuration is invalid") private String verificationCodeCheckRateLimit; @Min(value = 0, message = "maximum number of verification failure before user lockout must be positive") - private int maxVerificationFailuresBeforeUserLockout; + private Integer maxVerificationFailuresBeforeUserLockout; @Min(value = 1, message = "total amount of time allotted for verification must be greater than 0") private Integer totalAllowedTimeForVerification; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/security/model/mfa/provider/BackupCodeTwoFaProviderConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/security/model/mfa/provider/BackupCodeTwoFaProviderConfig.java index 9dc2a00b23..92def57ee4 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/security/model/mfa/provider/BackupCodeTwoFaProviderConfig.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/security/model/mfa/provider/BackupCodeTwoFaProviderConfig.java @@ -22,7 +22,7 @@ import javax.validation.constraints.Min; @Data public class BackupCodeTwoFaProviderConfig implements TwoFaProviderConfig { - @Min(0) + @Min(1) private int codesQuantity; @Override From 76de89189037383cc4956fac9594a4ff4f1fe9a1 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Thu, 26 May 2022 16:26:14 +0300 Subject: [PATCH 705/798] UI: 2fa improvement settings --- .../server/controller/TwoFaConfigController.java | 6 +++--- .../security/auth/mfa/DefaultTwoFactorAuthService.java | 2 +- .../data/security/model/mfa/PlatformTwoFaSettings.java | 1 + .../model/mfa/provider/BackupCodeTwoFaProviderConfig.java | 2 +- .../src/app/core/http/two-factor-authentication.service.ts | 4 ++-- .../home/pages/admin/two-factor-auth-settings.component.ts | 7 ++++--- 6 files changed, 12 insertions(+), 10 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/controller/TwoFaConfigController.java b/application/src/main/java/org/thingsboard/server/controller/TwoFaConfigController.java index 60239f3603..4b34b95f59 100644 --- a/application/src/main/java/org/thingsboard/server/controller/TwoFaConfigController.java +++ b/application/src/main/java/org/thingsboard/server/controller/TwoFaConfigController.java @@ -218,10 +218,10 @@ public class TwoFaConfigController extends BaseController { @ApiOperation(value = "Save platform 2FA settings (savePlatformTwoFaSettings)", notes = "Save 2FA settings for platform. The settings have following properties:\n" + "- `providers` - the list of 2FA providers' configs. Users will only be allowed to use 2FA providers from this list. \n\n" + - "- `minVerificationCodeSendPeriod` - minimal period in seconds to wait after verification code send request to send next request. " + - "The format is standard: 'amountOfRequests:periodInSeconds'. The value of '1:60' would limit verification " + - "code sending requests to one per minute.\n" + + "- `minVerificationCodeSendPeriod` - minimal period in seconds to wait after verification code send request to send next request. \n" + "- `verificationCodeCheckRateLimit` - rate limit configuration for verification code checking.\n" + + "The format is standard: 'amountOfRequests:periodInSeconds'. The value of '1:60' would limit verification " + + "code checking requests to one per minute.\n" + "- `maxVerificationFailuresBeforeUserLockout` - maximum number of verification failures before a user gets disabled.\n" + "- `totalAllowedTimeForVerification` - total amount of time in seconds allotted for verification. " + "Basically, this property sets a lifetime for pre-verification token. If not set, default value of 30 minutes is used.\n" + NEW_LINE + diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/mfa/DefaultTwoFactorAuthService.java b/application/src/main/java/org/thingsboard/server/service/security/auth/mfa/DefaultTwoFactorAuthService.java index 5929f04468..78bf0cdcf6 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/mfa/DefaultTwoFactorAuthService.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/mfa/DefaultTwoFactorAuthService.java @@ -88,7 +88,7 @@ public class DefaultTwoFactorAuthService implements TwoFactorAuthService { if (checkLimits) { Integer minVerificationCodeSendPeriod = twoFaSettings.getMinVerificationCodeSendPeriod(); String rateLimit = null; - if (minVerificationCodeSendPeriod != null && minVerificationCodeSendPeriod > 0) { + if (minVerificationCodeSendPeriod != null && minVerificationCodeSendPeriod > 4) { rateLimit = "1:" + minVerificationCodeSendPeriod; } checkRateLimits(user.getId(), accountConfig.getProviderType(), rateLimit, verificationCodeSendingRateLimits); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/security/model/mfa/PlatformTwoFaSettings.java b/common/data/src/main/java/org/thingsboard/server/common/data/security/model/mfa/PlatformTwoFaSettings.java index 930b858316..9b660758d8 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/security/model/mfa/PlatformTwoFaSettings.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/security/model/mfa/PlatformTwoFaSettings.java @@ -33,6 +33,7 @@ public class PlatformTwoFaSettings { @Valid private List providers; + @Min(value = 5, message = "minimum verification code sent period must be greater than or equal 5") private Integer minVerificationCodeSendPeriod; @Pattern(regexp = "[1-9]\\d*:[1-9]\\d*", message = "verification code check rate limit configuration is invalid") private String verificationCodeCheckRateLimit; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/security/model/mfa/provider/BackupCodeTwoFaProviderConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/security/model/mfa/provider/BackupCodeTwoFaProviderConfig.java index 92def57ee4..e8d4b90e03 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/security/model/mfa/provider/BackupCodeTwoFaProviderConfig.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/security/model/mfa/provider/BackupCodeTwoFaProviderConfig.java @@ -22,7 +22,7 @@ import javax.validation.constraints.Min; @Data public class BackupCodeTwoFaProviderConfig implements TwoFaProviderConfig { - @Min(1) + @Min(value = 1, message = "backup codes quantity must be greater than 0") private int codesQuantity; @Override diff --git a/ui-ngx/src/app/core/http/two-factor-authentication.service.ts b/ui-ngx/src/app/core/http/two-factor-authentication.service.ts index 2e3a7caeeb..a22da972ad 100644 --- a/ui-ngx/src/app/core/http/two-factor-authentication.service.ts +++ b/ui-ngx/src/app/core/http/two-factor-authentication.service.ts @@ -40,8 +40,8 @@ export class TwoFactorAuthenticationService { return this.http.get(`/api/2fa/settings`, defaultHttpOptionsFromConfig(config)); } - saveTwoFaSettings(settings: TwoFactorAuthSettings, config?: RequestConfig): Observable { - return this.http.post(`/api/2fa/settings`, settings, defaultHttpOptionsFromConfig(config)); + saveTwoFaSettings(settings: TwoFactorAuthSettings, config?: RequestConfig): Observable { + return this.http.post(`/api/2fa/settings`, settings, defaultHttpOptionsFromConfig(config)); } getAvailableTwoFaProviders(config?: RequestConfig): Observable> { diff --git a/ui-ngx/src/app/modules/home/pages/admin/two-factor-auth-settings.component.ts b/ui-ngx/src/app/modules/home/pages/admin/two-factor-auth-settings.component.ts index 7567d57d73..72480b4d44 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/two-factor-auth-settings.component.ts +++ b/ui-ngx/src/app/modules/home/pages/admin/two-factor-auth-settings.component.ts @@ -79,7 +79,8 @@ export class TwoFactorAuthSettingsComponent extends PageComponent implements OnI providers.forEach(provider => delete provider.enable); const config = Object.assign(setting, {providers}); this.twoFaService.saveTwoFaSettings(config).subscribe( - () => { + (settings) => { + this.setAuthConfigFormValue(settings); this.twoFaFormGroup.markAsUntouched(); this.twoFaFormGroup.markAsPristine(); } @@ -124,8 +125,8 @@ export class TwoFactorAuthSettingsComponent extends PageComponent implements OnI Validators.pattern(/^\d*$/) ]], verificationCodeCheckRateLimitEnable: [false], - verificationCodeCheckRateLimitNumber: ['3', this.posIntValidation], - verificationCodeCheckRateLimitTime: ['900', this.posIntValidation], + verificationCodeCheckRateLimitNumber: [{value: 3, disabled: true}, this.posIntValidation], + verificationCodeCheckRateLimitTime: [{value: 900, disabled: true}, this.posIntValidation], minVerificationCodeSendPeriod: ['30', [Validators.required, Validators.min(5), Validators.pattern(/^\d*$/)]], providers: this.fb.array([]) }); From 105acc16baa250ddff3dabc3b3449424a12ad450 Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Thu, 26 May 2022 20:32:22 +0300 Subject: [PATCH 706/798] refactoring: OtaPackageController fix bug tests with OtaPackage --- .../otaPackageController/DefaultTbOtaPackageService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/otaPackageController/DefaultTbOtaPackageService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/otaPackageController/DefaultTbOtaPackageService.java index 8365ce470b..98083edca0 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/otaPackageController/DefaultTbOtaPackageService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/otaPackageController/DefaultTbOtaPackageService.java @@ -42,7 +42,7 @@ public class DefaultTbOtaPackageService extends AbstractTbEntityService implemen ActionType actionType = saveOtaPackageInfoRequest.getId() == null ? ActionType.ADDED : ActionType.UPDATED; try { OtaPackageInfo savedOtaPackageInfo = otaPackageService.saveOtaPackageInfo(new OtaPackageInfo(saveOtaPackageInfoRequest), saveOtaPackageInfoRequest.isUsesUrl()); - notificationEntityService.notifyEntity(tenantId, saveOtaPackageInfoRequest.getId(), saveOtaPackageInfoRequest, null, + notificationEntityService.notifyEntity(tenantId, savedOtaPackageInfo.getId(), savedOtaPackageInfo, null, actionType, user, null); return savedOtaPackageInfo; } catch (Exception e) { From 7b3a3dc103f1b0fcd9aa45bd20b627d813ee2675 Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Thu, 26 May 2022 21:21:51 +0300 Subject: [PATCH 707/798] refactoring: OtaPackageController fix bug testDeleteFirmware --- .../server/controller/OtaPackageController.java | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/controller/OtaPackageController.java b/application/src/main/java/org/thingsboard/server/controller/OtaPackageController.java index 722723e283..36de0f4c76 100644 --- a/application/src/main/java/org/thingsboard/server/controller/OtaPackageController.java +++ b/application/src/main/java/org/thingsboard/server/controller/OtaPackageController.java @@ -33,11 +33,9 @@ import org.springframework.web.bind.annotation.RequestPart; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; -import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.OtaPackage; import org.thingsboard.server.common.data.OtaPackageInfo; import org.thingsboard.server.common.data.SaveOtaPackageInfoRequest; -import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.DeviceProfileId; import org.thingsboard.server.common.data.id.OtaPackageId; @@ -289,15 +287,6 @@ public class OtaPackageController extends BaseController { OtaPackageInfo otaPackageInfo = checkOtaPackageInfoId(otaPackageId, Operation.DELETE); tbOtaPackageService.delete(otaPackageInfo, getCurrentUser()); - try { - - - otaPackageService.deleteOtaPackage(getTenantId(), otaPackageId); - logEntityAction(otaPackageId, otaPackageInfo, null, ActionType.DELETED, null, strOtaPackageId); - } catch (Exception e) { - logEntityAction(emptyId(EntityType.OTA_PACKAGE), null, null, ActionType.DELETED, e, strOtaPackageId); - throw handleException(e); - } } } From 026361ce69b5aff0649ea1b7a21803e81d028911 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Fri, 27 May 2022 10:35:17 +0300 Subject: [PATCH 708/798] UI: Revert change --- ui-ngx/src/app/core/auth/auth.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui-ngx/src/app/core/auth/auth.service.ts b/ui-ngx/src/app/core/auth/auth.service.ts index efec71142f..829d6c49b4 100644 --- a/ui-ngx/src/app/core/auth/auth.service.ts +++ b/ui-ngx/src/app/core/auth/auth.service.ts @@ -238,7 +238,7 @@ export class AuthService { ); } - private forceDefaultPlace(authState?: AuthState, path?: string, params?: any): boolean { + public forceDefaultPlace(authState?: AuthState, path?: string, params?: any): boolean { if (authState && authState.authUser) { if (authState.authUser.authority === Authority.TENANT_ADMIN || authState.authUser.authority === Authority.CUSTOMER_USER) { if ((this.userHasDefaultDashboard(authState) && authState.forceFullscreen) || authState.authUser.isPublic) { From 985f7c79aa98ef9deb3b5b04168a62346662cec6 Mon Sep 17 00:00:00 2001 From: Viacheslav Klimov Date: Fri, 27 May 2022 12:34:12 +0300 Subject: [PATCH 709/798] 2FA refactoring --- .../server/service/mail/DefaultMailService.java | 15 +++++---------- .../auth/mfa/DefaultTwoFactorAuthService.java | 8 ++++---- .../mfa/provider/impl/EmailTwoFaProvider.java | 4 +++- .../server/service/sms/DefaultSmsService.java | 10 +++++----- .../security/model/mfa/PlatformTwoFaSettings.java | 2 ++ .../thingsboard/rule/engine/api/MailService.java | 4 ++-- .../thingsboard/rule/engine/api/SmsService.java | 4 ++-- 7 files changed, 23 insertions(+), 24 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/mail/DefaultMailService.java b/application/src/main/java/org/thingsboard/server/service/mail/DefaultMailService.java index 9d2f7b8891..b0c77f2794 100644 --- a/application/src/main/java/org/thingsboard/server/service/mail/DefaultMailService.java +++ b/application/src/main/java/org/thingsboard/server/service/mail/DefaultMailService.java @@ -105,16 +105,6 @@ public class DefaultMailService implements MailService { } } - @Override - public boolean isConfigured(TenantId tenantId) { - try { - mailSender.testConnection(); - return true; - } catch (MessagingException e) { - return false; - } - } - private JavaMailSenderImpl createMailSender(JsonNode jsonConfig) { JavaMailSenderImpl mailSender = new JavaMailSenderImpl(); mailSender.setHost(jsonConfig.get("smtpHost").asText()); @@ -360,6 +350,11 @@ public class DefaultMailService implements MailService { sendMail(mailSender, mailFrom, email, subject, message); } + @Override + public void testConnection(TenantId tenantId) throws Exception { + mailSender.testConnection(); + } + private String toEnabledValueLabel(ApiFeature apiFeature) { switch (apiFeature) { case DB: diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/mfa/DefaultTwoFactorAuthService.java b/application/src/main/java/org/thingsboard/server/service/security/auth/mfa/DefaultTwoFactorAuthService.java index 78bf0cdcf6..af78d307d3 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/mfa/DefaultTwoFactorAuthService.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/mfa/DefaultTwoFactorAuthService.java @@ -121,11 +121,11 @@ public class DefaultTwoFactorAuthService implements TwoFactorAuthService { TwoFaProviderConfig providerConfig = twoFaSettings.getProviderConfig(accountConfig.getProviderType()) .orElseThrow(() -> PROVIDER_NOT_CONFIGURED_ERROR); - boolean verificationSuccess; + boolean verificationSuccess = false; if (StringUtils.isNotBlank(verificationCode)) { - verificationSuccess = getTwoFaProvider(accountConfig.getProviderType()).checkVerificationCode(user, verificationCode, providerConfig, accountConfig); - } else { - verificationSuccess = false; + if (StringUtils.isNumeric(verificationCode) || accountConfig.getProviderType() == TwoFaProviderType.BACKUP_CODE) { + verificationSuccess = getTwoFaProvider(accountConfig.getProviderType()).checkVerificationCode(user, verificationCode, providerConfig, accountConfig); + } } if (checkLimits) { try { diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/mfa/provider/impl/EmailTwoFaProvider.java b/application/src/main/java/org/thingsboard/server/service/security/auth/mfa/provider/impl/EmailTwoFaProvider.java index 2aa5985514..085b5a9f45 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/mfa/provider/impl/EmailTwoFaProvider.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/mfa/provider/impl/EmailTwoFaProvider.java @@ -48,7 +48,9 @@ public class EmailTwoFaProvider extends OtpBasedTwoFaProvider providers; @Min(value = 5, message = "minimum verification code sent period must be greater than or equal 5") diff --git a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/MailService.java b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/MailService.java index 6ac4417934..c4b47d2e46 100644 --- a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/MailService.java +++ b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/MailService.java @@ -28,8 +28,6 @@ public interface MailService { void updateMailConfiguration(); - boolean isConfigured(TenantId tenantId); - void sendEmail(TenantId tenantId, String email, String subject, String message) throws ThingsboardException; void sendTestMail(JsonNode config, String email) throws ThingsboardException; @@ -54,4 +52,6 @@ public interface MailService { void sendApiFeatureStateEmail(ApiFeature apiFeature, ApiUsageStateValue stateValue, String email, ApiUsageStateMailMessage msg) throws ThingsboardException; + void testConnection(TenantId tenantId) throws Exception; + } diff --git a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/SmsService.java b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/SmsService.java index c38f99ecb2..90c199cd42 100644 --- a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/SmsService.java +++ b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/SmsService.java @@ -24,10 +24,10 @@ public interface SmsService { void updateSmsConfiguration(); - boolean isConfigured(TenantId tenantId); - void sendSms(TenantId tenantId, CustomerId customerId, String[] numbersTo, String message) throws ThingsboardException;; void sendTestSms(TestSmsRequest testSmsRequest) throws ThingsboardException; + boolean isConfigured(TenantId tenantId); + } From 4ba75a7c38a4183bc2ae442ce8169ec7c59eede2 Mon Sep 17 00:00:00 2001 From: fe-dev Date: Fri, 27 May 2022 15:37:36 +0300 Subject: [PATCH 710/798] UI: Fixed build project --- ui-ngx/src/app/modules/common/modules-map.ts | 2 ++ ui-ngx/src/app/shared/models/settings.models.ts | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/ui-ngx/src/app/modules/common/modules-map.ts b/ui-ngx/src/app/modules/common/modules-map.ts index b4f4ea1328..0d3f102564 100644 --- a/ui-ngx/src/app/modules/common/modules-map.ts +++ b/ui-ngx/src/app/modules/common/modules-map.ts @@ -157,6 +157,7 @@ import * as ImageInputComponent from '@shared/components/image-input.component'; import * as FileInputComponent from '@shared/components/file-input.component'; import * as MessageTypeAutocompleteComponent from '@shared/components/message-type-autocomplete.component'; import * as KeyValMapComponent from '@shared/components/kv-map.component'; +import * as MultipleImageInputComponent from '@shared/components/multiple-image-input.component'; import * as NavTreeComponent from '@shared/components/nav-tree.component'; import * as LedLightComponent from '@shared/components/led-light.component'; import * as TbJsonToStringDirective from '@shared/components/directives/tb-json-to-string.directive'; @@ -439,6 +440,7 @@ class ModulesMap implements IModulesMap { '@shared/components/file-input.component': FileInputComponent, '@shared/components/message-type-autocomplete.component': MessageTypeAutocompleteComponent, '@shared/components/kv-map.component': KeyValMapComponent, + '@shared/components/multiple-image-input.component': MultipleImageInputComponent, '@shared/components/nav-tree.component': NavTreeComponent, '@shared/components/led-light.component': LedLightComponent, '@shared/components/directives/tb-json-to-string.directive': TbJsonToStringDirective, diff --git a/ui-ngx/src/app/shared/models/settings.models.ts b/ui-ngx/src/app/shared/models/settings.models.ts index 615d0994c9..e00c8e28a0 100644 --- a/ui-ngx/src/app/shared/models/settings.models.ts +++ b/ui-ngx/src/app/shared/models/settings.models.ts @@ -141,7 +141,7 @@ export enum TypeOfNumber { Abbreviated = 'Abbreviated' } -interface TypeDescriptor { +export interface TypeDescriptor { name: string; value: number; } From 095851897aa6d5f4a067416a3f60337d6f5b1000 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Fri, 27 May 2022 16:02:32 +0300 Subject: [PATCH 711/798] UI: Fixed 2fa auth.guard and setting style --- ui-ngx/src/app/core/guards/auth.guard.ts | 19 ++++++++++--------- .../two-factor-auth-settings.component.html | 18 +++++++++--------- .../two-factor-auth-settings.component.ts | 6 +++--- 3 files changed, 22 insertions(+), 21 deletions(-) diff --git a/ui-ngx/src/app/core/guards/auth.guard.ts b/ui-ngx/src/app/core/guards/auth.guard.ts index 4fe53d7bdc..fb424b06e3 100644 --- a/ui-ngx/src/app/core/guards/auth.guard.ts +++ b/ui-ngx/src/app/core/guards/auth.guard.ts @@ -78,7 +78,7 @@ export class AuthGuard implements CanActivate, CanActivateChild { const params = lastChild.params || {}; const isPublic = data.module === 'public'; - if (!authState.isAuthenticated) { + if (!authState.isAuthenticated || isPublic) { if (publicId && publicId.length > 0) { this.authService.setUserFromJwtToken(null, null, false); this.authService.reloadUser(); @@ -95,7 +95,15 @@ export class AuthGuard implements CanActivate, CanActivateChild { }) ); } else if (path === 'login.mfa') { - return of(this.router.parseUrl('/login')); + if (authState.authUser?.authority === Authority.PRE_VERIFICATION_TOKEN) { + return this.authService.getAvailableTwoFaLoginProviders().pipe( + map(() => { + return true; + }) + ); + } + this.authService.logout(); + return of(this.authService.defaultUrl(false)); } else { return of(true); } @@ -117,13 +125,6 @@ export class AuthGuard implements CanActivate, CanActivateChild { return of(false); } if (authState.authUser.authority === Authority.PRE_VERIFICATION_TOKEN) { - if (path === 'login.mfa') { - return this.authService.getAvailableTwoFaLoginProviders().pipe( - map(() => { - return true; - }) - ); - } this.authService.logout(); return of(false); } diff --git a/ui-ngx/src/app/modules/home/pages/admin/two-factor-auth-settings.component.html b/ui-ngx/src/app/modules/home/pages/admin/two-factor-auth-settings.component.html index dde38b21f6..9e0f54243f 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/two-factor-auth-settings.component.html +++ b/ui-ngx/src/app/modules/home/pages/admin/two-factor-auth-settings.component.html @@ -30,7 +30,7 @@
- +
admin.2fa.available-providers @@ -186,14 +186,14 @@
-
- -
- +
+
+ +
diff --git a/ui-ngx/src/app/modules/home/pages/admin/two-factor-auth-settings.component.ts b/ui-ngx/src/app/modules/home/pages/admin/two-factor-auth-settings.component.ts index 72480b4d44..98beae21cf 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/two-factor-auth-settings.component.ts +++ b/ui-ngx/src/app/modules/home/pages/admin/two-factor-auth-settings.component.ts @@ -147,9 +147,9 @@ export class TwoFactorAuthSettingsComponent extends PageComponent implements OnI } private setAuthConfigFormValue(settings: TwoFactorAuthSettings) { - const [checkRateLimitNumber, checkRateLimitTime] = this.splitRateLimit(settings.verificationCodeCheckRateLimit); - const allowProvidersConfig = settings.providers.map(provider => provider.providerType); - const processFormValue: TwoFactorAuthSettingsForm = Object.assign(deepClone(settings), { + const [checkRateLimitNumber, checkRateLimitTime] = this.splitRateLimit(settings?.verificationCodeCheckRateLimit); + const allowProvidersConfig = settings?.providers.map(provider => provider.providerType) || []; + const processFormValue: TwoFactorAuthSettingsForm = Object.assign({}, settings, { verificationCodeCheckRateLimitEnable: checkRateLimitNumber > 0, verificationCodeCheckRateLimitNumber: checkRateLimitNumber || 3, verificationCodeCheckRateLimitTime: checkRateLimitTime || 900, From 7836ad6f903e38a482e984bbb96fbbc6fe90f595 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Fri, 27 May 2022 17:34:09 +0300 Subject: [PATCH 712/798] UI: Add validation 2fa settings --- .../two-factor-auth-settings.component.ts | 21 ++++++++++++++++--- .../app/modules/login/login-routing.module.ts | 2 +- .../assets/locale/locale.constant-en_US.json | 1 + 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/ui-ngx/src/app/modules/home/pages/admin/two-factor-auth-settings.component.ts b/ui-ngx/src/app/modules/home/pages/admin/two-factor-auth-settings.component.ts index 98beae21cf..895b80ed08 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/two-factor-auth-settings.component.ts +++ b/ui-ngx/src/app/modules/home/pages/admin/two-factor-auth-settings.component.ts @@ -22,12 +22,13 @@ import { AppState } from '@core/core.state'; import { FormArray, FormBuilder, FormGroup, Validators } from '@angular/forms'; import { TwoFactorAuthenticationService } from '@core/http/two-factor-authentication.service'; import { + TwoFactorAuthProviderConfigForm, twoFactorAuthProvidersData, TwoFactorAuthProviderType, TwoFactorAuthSettings, TwoFactorAuthSettingsForm } from '@shared/models/two-factor-auth.models'; -import { deepClone, isNotEmptyStr } from '@core/utils'; +import { isNotEmptyStr } from '@core/utils'; import { Subject } from 'rxjs'; import { takeUntil } from 'rxjs/operators'; import { MatExpansionPanel } from '@angular/material/expansion'; @@ -73,7 +74,7 @@ export class TwoFactorAuthSettingsComponent extends PageComponent implements OnI save() { if (this.twoFaFormGroup.valid) { - const setting = this.twoFaFormGroup.value as TwoFactorAuthSettingsForm; + const setting = this.twoFaFormGroup.getRawValue() as TwoFactorAuthSettingsForm; this.joinRateLimit(setting, 'verificationCodeCheckRateLimit'); const providers = setting.providers.filter(provider => provider.enable); providers.forEach(provider => delete provider.enable); @@ -144,6 +145,20 @@ export class TwoFactorAuthSettingsComponent extends PageComponent implements OnI this.twoFaFormGroup.get('verificationCodeCheckRateLimitTime').disable({emitEvent: false}); } }); + this.providersForm.valueChanges.pipe( + takeUntil(this.destroy$) + ).subscribe((value: TwoFactorAuthProviderConfigForm[]) => { + const activeProvider = value.filter(provider => provider.enable); + const indexBackupCode = Object.values(TwoFactorAuthProviderType).indexOf(TwoFactorAuthProviderType.BACKUP_CODE); + if (!activeProvider.length || + activeProvider.length === 1 && activeProvider[0].providerType === TwoFactorAuthProviderType.BACKUP_CODE) { + this.providersForm.at(indexBackupCode).get('enable').setValue(false, {emitEvent: false}); + this.providersForm.at(indexBackupCode).disable( {emitEvent: false}); + this.providersForm.at(indexBackupCode).get('providerType').enable({emitEvent: false}); + } else { + this.providersForm.at(indexBackupCode).get('enable').enable( {emitEvent: false}); + } + }); } private setAuthConfigFormValue(settings: TwoFactorAuthSettings) { @@ -205,7 +220,7 @@ export class TwoFactorAuthSettingsComponent extends PageComponent implements OnI newProviders.get('providerType').enable({emitEvent: false}); } }); - this.providersForm.push(newProviders); + this.providersForm.push(newProviders, {emitEvent: false}); } private getByIndexPanel(index: number) { diff --git a/ui-ngx/src/app/modules/login/login-routing.module.ts b/ui-ngx/src/app/modules/login/login-routing.module.ts index 6305fa9dfd..425f6d16c8 100644 --- a/ui-ngx/src/app/modules/login/login-routing.module.ts +++ b/ui-ngx/src/app/modules/login/login-routing.module.ts @@ -76,7 +76,7 @@ const routes: Routes = [ path: 'login/mfa', component: TwoFactorAuthLoginComponent, data: { - title: 'login.create-password', + title: 'login.two-factor-authentication', auth: [Authority.PRE_VERIFICATION_TOKEN], module: 'public' }, diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index 7e5ab2fb72..69537cdd56 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -2480,6 +2480,7 @@ "request-password-reset": "Request Password Reset", "reset-password": "Reset Password", "create-password": "Create Password", + "two-factor-authentication": "Two factor authentication", "passwords-mismatch-error": "Entered passwords must be same!", "password-again": "Password again", "sign-in": "Please sign in", From 20c612b7cca0ba792ef0301972412c6a1196fe9b Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Fri, 27 May 2022 20:18:52 +0300 Subject: [PATCH 713/798] refactoring: RuleChainController --- .../controller/RuleChainController.java | 317 ++++-------------- .../entitiy/AbstractTbEntityService.java | 6 + .../DefaultTbNotificationEntityService.java | 44 ++- .../entitiy/TbNotificationEntityService.java | 12 +- .../entitiy/alarm/DefaultTbAlarmService.java | 2 +- .../entitiy/asset/DefaultTbAssetService.java | 4 +- .../customer/DefaultTbCustomerService.java | 3 - .../dashboard/DefaultTbDashboardService.java | 4 +- .../device/DefaultTbDeviceService.java | 4 +- .../DefaultTbDeviceProfileService.java | 9 +- .../DefaultTbEntityViewService.java | 6 +- .../DefaultTbRuleChainNotifyService.java | 266 +++++++++++++++ .../ruleChain/TbRuleChainNotifyService.java | 44 +++ 13 files changed, 433 insertions(+), 288 deletions(-) create mode 100644 application/src/main/java/org/thingsboard/server/service/entitiy/ruleChain/DefaultTbRuleChainNotifyService.java create mode 100644 application/src/main/java/org/thingsboard/server/service/entitiy/ruleChain/TbRuleChainNotifyService.java diff --git a/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java b/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java index 6479b838b2..0fc91b2453 100644 --- a/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java +++ b/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java @@ -22,6 +22,7 @@ import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.ObjectNode; import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiParam; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; @@ -40,12 +41,9 @@ import org.thingsboard.rule.engine.api.ScriptEngine; import org.thingsboard.server.actors.ActorSystemContext; import org.thingsboard.server.actors.tenant.DebugTbRateLimits; import org.thingsboard.server.common.data.DataConstants; -import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.Event; import org.thingsboard.server.common.data.StringUtils; -import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.edge.Edge; -import org.thingsboard.server.common.data.edge.EdgeEventActionType; import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.EdgeId; import org.thingsboard.server.common.data.id.RuleChainId; @@ -62,13 +60,12 @@ import org.thingsboard.server.common.data.rule.RuleChainImportResult; import org.thingsboard.server.common.data.rule.RuleChainMetaData; import org.thingsboard.server.common.data.rule.RuleChainOutputLabelsUsage; import org.thingsboard.server.common.data.rule.RuleChainType; -import org.thingsboard.server.common.data.rule.RuleChainUpdateResult; -import org.thingsboard.server.common.data.rule.RuleNode; import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsgDataType; import org.thingsboard.server.common.msg.TbMsgMetaData; import org.thingsboard.server.dao.event.EventService; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.entitiy.ruleChain.TbRuleChainNotifyService; import org.thingsboard.server.service.install.InstallScripts; import org.thingsboard.server.service.rule.TbRuleChainService; import org.thingsboard.server.service.script.JsInvokeService; @@ -77,15 +74,11 @@ import org.thingsboard.server.service.security.permission.Operation; import org.thingsboard.server.service.security.permission.Resource; import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; -import java.util.TreeSet; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.TimeUnit; -import java.util.stream.Collectors; import static org.thingsboard.server.controller.ControllerConstants.EDGE_ASSIGN_ASYNC_FIRST_STEP_DESCRIPTION; import static org.thingsboard.server.controller.ControllerConstants.EDGE_ASSIGN_RECEIVE_STEP_DESCRIPTION; @@ -114,8 +107,11 @@ import static org.thingsboard.server.controller.ControllerConstants.UUID_WIKI_LI @RestController @TbCoreComponent @RequestMapping("/api") +@RequiredArgsConstructor public class RuleChainController extends BaseController { + private final TbRuleChainNotifyService tbRuleChainNotifyService; + public static final String RULE_CHAIN_ID = "ruleChainId"; public static final String RULE_NODE_ID = "ruleNodeId"; @@ -246,38 +242,11 @@ public class RuleChainController extends BaseController { public RuleChain saveRuleChain( @ApiParam(value = "A JSON value representing the rule chain.") @RequestBody RuleChain ruleChain) throws ThingsboardException { - try { - boolean created = ruleChain.getId() == null; - ruleChain.setTenantId(getCurrentUser().getTenantId()); - - checkEntity(ruleChain.getId(), ruleChain, Resource.RULE_CHAIN); - - RuleChain savedRuleChain = checkNotNull(ruleChainService.saveRuleChain(ruleChain)); - if (RuleChainType.CORE.equals(savedRuleChain.getType())) { - tbClusterService.broadcastEntityStateChangeEvent(ruleChain.getTenantId(), savedRuleChain.getId(), - created ? ComponentLifecycleEvent.CREATED : ComponentLifecycleEvent.UPDATED); - } - - logEntityAction(savedRuleChain.getId(), savedRuleChain, - null, - created ? ActionType.ADDED : ActionType.UPDATED, null); - - if (RuleChainType.EDGE.equals(savedRuleChain.getType())) { - if (!created) { - sendEntityNotificationMsg(savedRuleChain.getTenantId(), savedRuleChain.getId(), EdgeEventActionType.UPDATED); - } - } - - return savedRuleChain; - } catch (Exception e) { - - logEntityAction(emptyId(EntityType.RULE_CHAIN), ruleChain, - null, ruleChain.getId() == null ? ActionType.ADDED : ActionType.UPDATED, e); - - throw handleException(e); - } - } + ruleChain.setTenantId(getCurrentUser().getTenantId()); + checkEntity(ruleChain.getId(), ruleChain, Resource.RULE_CHAIN); + return tbRuleChainNotifyService.save(ruleChain, getCurrentUser()); + } @ApiOperation(value = "Create Default Rule Chain", notes = "Create rule chain from template, based on the specified name in the request. " + @@ -288,24 +257,11 @@ public class RuleChainController extends BaseController { public RuleChain saveRuleChain( @ApiParam(value = "A JSON value representing the request.") @RequestBody DefaultRuleChainCreateRequest request) throws ThingsboardException { - try { - checkNotNull(request); - checkParameter(request.getName(), "name"); - - RuleChain savedRuleChain = installScripts.createDefaultRuleChain(getCurrentUser().getTenantId(), request.getName()); - - tbClusterService.broadcastEntityStateChangeEvent(savedRuleChain.getTenantId(), savedRuleChain.getId(), ComponentLifecycleEvent.CREATED); - logEntityAction(savedRuleChain.getId(), savedRuleChain, null, ActionType.ADDED, null); - - return savedRuleChain; - } catch (Exception e) { - RuleChain ruleChain = new RuleChain(); - ruleChain.setName(request.getName()); - logEntityAction(emptyId(EntityType.RULE_CHAIN), ruleChain, null, ActionType.ADDED, e); - throw handleException(e); - } - } + checkNotNull(request); + checkParameter(request.getName(), "name"); + return tbRuleChainNotifyService.saveRuleChain(getTenantId(), request, getCurrentUser()); + } @ApiOperation(value = "Set Root Rule Chain (setRootRuleChain)", notes = "Makes the rule chain to be root rule chain. Updates previous root rule chain as well. " + TENANT_AUTHORITY_PARAGRAPH) @@ -316,38 +272,10 @@ public class RuleChainController extends BaseController { @ApiParam(value = RULE_CHAIN_ID_PARAM_DESCRIPTION) @PathVariable(RULE_CHAIN_ID) String strRuleChainId) throws ThingsboardException { checkParameter(RULE_CHAIN_ID, strRuleChainId); - try { - RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId)); - RuleChain ruleChain = checkRuleChain(ruleChainId, Operation.WRITE); - TenantId tenantId = getCurrentUser().getTenantId(); - RuleChain previousRootRuleChain = ruleChainService.getRootTenantRuleChain(tenantId); - if (ruleChainService.setRootRuleChain(getTenantId(), ruleChainId)) { - if (previousRootRuleChain != null) { - previousRootRuleChain = ruleChainService.findRuleChainById(getTenantId(), previousRootRuleChain.getId()); - - tbClusterService.broadcastEntityStateChangeEvent(previousRootRuleChain.getTenantId(), previousRootRuleChain.getId(), - ComponentLifecycleEvent.UPDATED); - - logEntityAction(previousRootRuleChain.getId(), previousRootRuleChain, - null, ActionType.UPDATED, null); - } - ruleChain = ruleChainService.findRuleChainById(getTenantId(), ruleChainId); - - tbClusterService.broadcastEntityStateChangeEvent(ruleChain.getTenantId(), ruleChain.getId(), - ComponentLifecycleEvent.UPDATED); + RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId)); + RuleChain ruleChain = checkRuleChain(ruleChainId, Operation.WRITE); + return tbRuleChainNotifyService.setRootRuleChain(getTenantId(),ruleChain, getCurrentUser()); - logEntityAction(ruleChain.getId(), ruleChain, - null, ActionType.UPDATED, null); - - } - return ruleChain; - } catch (Exception e) { - logEntityAction(emptyId(EntityType.RULE_CHAIN), - null, - null, - ActionType.UPDATED, e, strRuleChainId); - throw handleException(e); - } } @ApiOperation(value = "Update Rule Chain Metadata", @@ -361,58 +289,20 @@ public class RuleChainController extends BaseController { @ApiParam(value = "Update related rule nodes.") @RequestParam(value = "updateRelated", required = false, defaultValue = "true") boolean updateRelated ) throws ThingsboardException { - try { - TenantId tenantId = getTenantId(); - if (debugPerTenantEnabled) { - ConcurrentMap debugPerTenantLimits = actorContext.getDebugPerTenantLimits(); - DebugTbRateLimits debugTbRateLimits = debugPerTenantLimits.getOrDefault(tenantId, null); - if (debugTbRateLimits != null) { - debugPerTenantLimits.remove(tenantId, debugTbRateLimits); - } - } - - RuleChain ruleChain = checkRuleChain(ruleChainMetaData.getRuleChainId(), Operation.WRITE); - RuleChainUpdateResult result = ruleChainService.saveRuleChainMetaData(tenantId, ruleChainMetaData); - checkNotNull(result.isSuccess() ? true : null); - - List updatedRuleChains; - if (updateRelated && result.isSuccess()) { - updatedRuleChains = tbRuleChainService.updateRelatedRuleChains(tenantId, ruleChainMetaData.getRuleChainId(), result); - } else { - updatedRuleChains = Collections.emptyList(); - } - - RuleChainMetaData savedRuleChainMetaData = checkNotNull(ruleChainService.loadRuleChainMetaData(tenantId, ruleChainMetaData.getRuleChainId())); - - if (RuleChainType.CORE.equals(ruleChain.getType())) { - tbClusterService.broadcastEntityStateChangeEvent(ruleChain.getTenantId(), ruleChain.getId(), ComponentLifecycleEvent.UPDATED); - updatedRuleChains.forEach(updatedRuleChain -> { - tbClusterService.broadcastEntityStateChangeEvent(updatedRuleChain.getTenantId(), updatedRuleChain.getId(), ComponentLifecycleEvent.UPDATED); - }); + TenantId tenantId = getTenantId(); + if (debugPerTenantEnabled) { + ConcurrentMap debugPerTenantLimits = actorContext.getDebugPerTenantLimits(); + DebugTbRateLimits debugTbRateLimits = debugPerTenantLimits.getOrDefault(tenantId, null); + if (debugTbRateLimits != null) { + debugPerTenantLimits.remove(tenantId, debugTbRateLimits); } + } - logEntityAction(ruleChain.getId(), ruleChain, null, ActionType.UPDATED, null, ruleChainMetaData); - for (RuleChain updatedRuleChain : updatedRuleChains) { - RuleChainMetaData updatedRuleChainMetaData = checkNotNull(ruleChainService.loadRuleChainMetaData(tenantId, updatedRuleChain.getId())); - logEntityAction(updatedRuleChain.getId(), updatedRuleChain, null, ActionType.UPDATED, null, updatedRuleChainMetaData); - } - - if (RuleChainType.EDGE.equals(ruleChain.getType())) { - sendEntityNotificationMsg(ruleChain.getTenantId(), ruleChain.getId(), EdgeEventActionType.UPDATED); - updatedRuleChains.forEach(updatedRuleChain -> { - sendEntityNotificationMsg(updatedRuleChain.getTenantId(), updatedRuleChain.getId(), EdgeEventActionType.UPDATED); - }); - } - - return savedRuleChainMetaData; - } catch (Exception e) { - - logEntityAction(emptyId(EntityType.RULE_CHAIN), null, - null, ActionType.UPDATED, e, ruleChainMetaData); + RuleChain ruleChain = checkRuleChain(ruleChainMetaData.getRuleChainId(), Operation.WRITE); - throw handleException(e); - } - } + return tbRuleChainNotifyService.saveRuleChainMetaData(tenantId, ruleChain, ruleChainMetaData, updateRelated, + getCurrentUser()); + } @ApiOperation(value = "Get Rule Chains (getRuleChains)", @@ -456,46 +346,11 @@ public class RuleChainController extends BaseController { @ApiParam(value = RULE_CHAIN_ID_PARAM_DESCRIPTION) @PathVariable(RULE_CHAIN_ID) String strRuleChainId) throws ThingsboardException { checkParameter(RULE_CHAIN_ID, strRuleChainId); - try { - RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId)); - RuleChain ruleChain = checkRuleChain(ruleChainId, Operation.DELETE); - - List referencingRuleNodes = ruleChainService.getReferencingRuleChainNodes(getTenantId(), ruleChainId); - Set referencingRuleChainIds = referencingRuleNodes.stream().map(RuleNode::getRuleChainId).collect(Collectors.toSet()); - - List relatedEdgeIds = null; - if (RuleChainType.EDGE.equals(ruleChain.getType())) { - relatedEdgeIds = findRelatedEdgeIds(getTenantId(), ruleChainId); - } - - ruleChainService.deleteRuleChainById(getTenantId(), ruleChainId); - - referencingRuleChainIds.remove(ruleChain.getId()); - - if (RuleChainType.CORE.equals(ruleChain.getType())) { - referencingRuleChainIds.forEach(referencingRuleChainId -> - tbClusterService.broadcastEntityStateChangeEvent(ruleChain.getTenantId(), referencingRuleChainId, ComponentLifecycleEvent.UPDATED)); - - tbClusterService.broadcastEntityStateChangeEvent(ruleChain.getTenantId(), ruleChain.getId(), ComponentLifecycleEvent.DELETED); - } - - logEntityAction(ruleChainId, ruleChain, - null, - ActionType.DELETED, null, strRuleChainId); - - if (RuleChainType.EDGE.equals(ruleChain.getType())) { - sendDeleteNotificationMsg(ruleChain.getTenantId(), ruleChain.getId(), relatedEdgeIds); - } - - } catch (Exception e) { - logEntityAction(emptyId(EntityType.RULE_CHAIN), - null, - null, - ActionType.DELETED, e, strRuleChainId); - throw handleException(e); - } - } + RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId)); + RuleChain ruleChain = checkRuleChain(ruleChainId, Operation.DELETE); + tbRuleChainNotifyService.delete(ruleChain, getCurrentUser()); + } @ApiOperation(value = "Get latest input message (getLatestRuleNodeDebugInput)", notes = "Gets the input message from the debug events for specified Rule Chain Id. " + @@ -681,31 +536,14 @@ public class RuleChainController extends BaseController { @PathVariable(RULE_CHAIN_ID) String strRuleChainId) throws ThingsboardException { checkParameter("edgeId", strEdgeId); checkParameter(RULE_CHAIN_ID, strRuleChainId); - try { - EdgeId edgeId = new EdgeId(toUUID(strEdgeId)); - Edge edge = checkEdgeId(edgeId, Operation.WRITE); - - RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId)); - checkRuleChain(ruleChainId, Operation.READ); - - RuleChain savedRuleChain = checkNotNull(ruleChainService.assignRuleChainToEdge(getCurrentUser().getTenantId(), ruleChainId, edgeId)); - - logEntityAction(ruleChainId, savedRuleChain, - null, - ActionType.ASSIGNED_TO_EDGE, null, strRuleChainId, strEdgeId, edge.getName()); - - sendEntityAssignToEdgeNotificationMsg(getTenantId(), edgeId, savedRuleChain.getId(), EdgeEventActionType.ASSIGNED_TO_EDGE); + EdgeId edgeId = new EdgeId(toUUID(strEdgeId)); + Edge edge = checkEdgeId(edgeId, Operation.WRITE); - return savedRuleChain; - } catch (Exception e) { + RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId)); + checkRuleChain(ruleChainId, Operation.READ); - logEntityAction(emptyId(EntityType.RULE_CHAIN), null, - null, - ActionType.ASSIGNED_TO_EDGE, e, strRuleChainId, strEdgeId); - - throw handleException(e); - } - } + return tbRuleChainNotifyService.assignRuleChainToEdge(getTenantId(), ruleChainId, edge, getCurrentUser()); + } @ApiOperation(value = "Unassign rule chain from edge (unassignRuleChainFromEdge)", notes = "Clears assignment of the rule chain to the edge. " + @@ -721,30 +559,13 @@ public class RuleChainController extends BaseController { @PathVariable(RULE_CHAIN_ID) String strRuleChainId) throws ThingsboardException { checkParameter("edgeId", strEdgeId); checkParameter(RULE_CHAIN_ID, strRuleChainId); - try { - EdgeId edgeId = new EdgeId(toUUID(strEdgeId)); - Edge edge = checkEdgeId(edgeId, Operation.WRITE); - RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId)); - RuleChain ruleChain = checkRuleChain(ruleChainId, Operation.READ); - - RuleChain savedRuleChain = checkNotNull(ruleChainService.unassignRuleChainFromEdge(getCurrentUser().getTenantId(), ruleChainId, edgeId, false)); - - logEntityAction(ruleChainId, ruleChain, - null, - ActionType.UNASSIGNED_FROM_EDGE, null, strRuleChainId, strEdgeId, edge.getName()); - - sendEntityAssignToEdgeNotificationMsg(getTenantId(), edgeId, savedRuleChain.getId(), EdgeEventActionType.UNASSIGNED_FROM_EDGE); - - return savedRuleChain; - } catch (Exception e) { - - logEntityAction(emptyId(EntityType.RULE_CHAIN), null, - null, - ActionType.UNASSIGNED_FROM_EDGE, e, strRuleChainId, strEdgeId); + EdgeId edgeId = new EdgeId(toUUID(strEdgeId)); + Edge edge = checkEdgeId(edgeId, Operation.WRITE); + RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId)); + RuleChain ruleChain = checkRuleChain(ruleChainId, Operation.READ); - throw handleException(e); - } - } + return tbRuleChainNotifyService.unassignRuleChainFromEdge(getTenantId(), ruleChain, edge, getCurrentUser()); + } @ApiOperation(value = "Get Edge Rule Chains (getEdgeRuleChains)", notes = "Returns a page of Rule Chains assigned to the specified edge. " + RULE_CHAIN_DESCRIPTION + PAGE_DATA_PARAMETERS + TENANT_AUTHORITY_PARAGRAPH) @@ -785,18 +606,10 @@ public class RuleChainController extends BaseController { public RuleChain setEdgeTemplateRootRuleChain(@ApiParam(value = RULE_CHAIN_ID_PARAM_DESCRIPTION) @PathVariable(RULE_CHAIN_ID) String strRuleChainId) throws ThingsboardException { checkParameter(RULE_CHAIN_ID, strRuleChainId); - try { - RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId)); - RuleChain ruleChain = checkRuleChain(ruleChainId, Operation.WRITE); - ruleChainService.setEdgeTemplateRootRuleChain(getTenantId(), ruleChainId); - return ruleChain; - } catch (Exception e) { - logEntityAction(emptyId(EntityType.RULE_CHAIN), - null, - null, - ActionType.UPDATED, e, strRuleChainId); - throw handleException(e); - } + RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId)); + RuleChain ruleChain = checkRuleChain(ruleChainId, Operation.WRITE); + TenantId tenantId = getTenantId(); + return tbRuleChainNotifyService.setEdgeRuleChain(getTenantId(), ruleChain, getCurrentUser(), true); } @ApiOperation(value = "Set Auto Assign To Edge Rule Chain (setAutoAssignToEdgeRuleChain)", @@ -808,19 +621,11 @@ public class RuleChainController extends BaseController { public RuleChain setAutoAssignToEdgeRuleChain(@ApiParam(value = RULE_CHAIN_ID_PARAM_DESCRIPTION) @PathVariable(RULE_CHAIN_ID) String strRuleChainId) throws ThingsboardException { checkParameter(RULE_CHAIN_ID, strRuleChainId); - try { - RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId)); - RuleChain ruleChain = checkRuleChain(ruleChainId, Operation.WRITE); - ruleChainService.setAutoAssignToEdgeRuleChain(getTenantId(), ruleChainId); - return ruleChain; - } catch (Exception e) { - logEntityAction(emptyId(EntityType.RULE_CHAIN), - null, - null, - ActionType.UPDATED, e, strRuleChainId); - throw handleException(e); - } - } + RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId)); + RuleChain ruleChain = checkRuleChain(ruleChainId, Operation.WRITE); + TenantId tenantId = getTenantId(); + return tbRuleChainNotifyService.setEdgeRuleChain(getTenantId(), ruleChain, getCurrentUser(), false); + } @ApiOperation(value = "Unset Auto Assign To Edge Rule Chain (unsetAutoAssignToEdgeRuleChain)", notes = "Removes the rule chain from the list of rule chains that are going to be automatically assigned for any new edge that will be created. " + @@ -831,19 +636,11 @@ public class RuleChainController extends BaseController { public RuleChain unsetAutoAssignToEdgeRuleChain(@ApiParam(value = RULE_CHAIN_ID_PARAM_DESCRIPTION) @PathVariable(RULE_CHAIN_ID) String strRuleChainId) throws ThingsboardException { checkParameter(RULE_CHAIN_ID, strRuleChainId); - try { - RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId)); - RuleChain ruleChain = checkRuleChain(ruleChainId, Operation.WRITE); - ruleChainService.unsetAutoAssignToEdgeRuleChain(getTenantId(), ruleChainId); - return ruleChain; - } catch (Exception e) { - logEntityAction(emptyId(EntityType.RULE_CHAIN), - null, - null, - ActionType.UPDATED, e, strRuleChainId); - throw handleException(e); - } - } + RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId)); + RuleChain ruleChain = checkRuleChain(ruleChainId, Operation.WRITE); + + return tbRuleChainNotifyService.unsetAutoAssignToEdgeRuleChain(getTenantId(), ruleChain, getCurrentUser()); + } // TODO: @voba refactor this - add new config to edge rule chain to set it as auto-assign @ApiOperation(value = "Get Auto Assign To Edge Rule Chains (getAutoAssignToEdgeRuleChains)", diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/AbstractTbEntityService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/AbstractTbEntityService.java index fdeb8ceb89..c1a0ff2a24 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/AbstractTbEntityService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/AbstractTbEntityService.java @@ -62,7 +62,9 @@ import org.thingsboard.server.dao.tenant.TenantService; import org.thingsboard.server.service.action.EntityActionService; import org.thingsboard.server.service.edge.EdgeNotificationService; import org.thingsboard.server.service.executors.DbCallbackExecutorService; +import org.thingsboard.server.service.install.InstallScripts; import org.thingsboard.server.service.ota.OtaPackageStateService; +import org.thingsboard.server.service.rule.TbRuleChainService; import org.thingsboard.server.service.security.permission.AccessControlService; import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService; @@ -114,6 +116,8 @@ public abstract class AbstractTbEntityService { @Autowired protected RuleChainService ruleChainService; @Autowired + protected TbRuleChainService tbRuleChainService; + @Autowired protected EdgeNotificationService edgeNotificationService; @Autowired protected DashboardService dashboardService; @@ -135,6 +139,8 @@ public abstract class AbstractTbEntityService { protected RelationService relationService; @Autowired protected OtaPackageService otaPackageService; + @Autowired + protected InstallScripts installScripts; protected ListenableFuture removeAlarmsByEntityId(TenantId tenantId, EntityId entityId) { ListenableFuture> alarmsFuture = diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/DefaultTbNotificationEntityService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/DefaultTbNotificationEntityService.java index f9af011013..2ed55ac126 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/DefaultTbNotificationEntityService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/DefaultTbNotificationEntityService.java @@ -36,9 +36,13 @@ 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.RuleChainId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; import org.thingsboard.server.common.data.relation.EntityRelation; +import org.thingsboard.server.common.data.rule.RuleChain; +import org.thingsboard.server.common.data.rule.RuleChainMetaData; +import org.thingsboard.server.common.data.rule.RuleChainType; import org.thingsboard.server.common.data.security.DeviceCredentials; import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsgDataType; @@ -78,14 +82,39 @@ public class DefaultTbNotificationEntityService implements TbNotificationEntityS } public void notifyDeleteAlarm(TenantId tenantId, Alarm alarm, EntityId originatorId, - CustomerId customerId, ActionType actionType, + CustomerId customerId, List relatedEdgeIds, SecurityUser user, String body, Object... additionalInfo) { - logEntityAction(tenantId, originatorId, alarm, customerId, actionType, user, additionalInfo); + logEntityAction(tenantId, originatorId, alarm, customerId, ActionType.DELETED, user, additionalInfo); sendAlarmDeleteNotificationMsg(tenantId, alarm, relatedEdgeIds, body); } + @Override + public void notifyDeleteRuleChane(TenantId tenantId, RuleChain ruleChain, + List relatedEdgeIds, SecurityUser user) { + RuleChainId ruleChainId = ruleChain.getId(); + logEntityAction(tenantId, ruleChainId, ruleChain, null, ActionType.DELETED, user, ruleChainId.toString()); + if (RuleChainType.EDGE.equals(ruleChain.getType())) { + sendDeleteNotificationMsg(tenantId, ruleChainId, relatedEdgeIds, null); + } + } + + @Override + public void notifySaveRuleChainMetaData(TenantId tenantId, RuleChain ruleChain, Object... others) { + if (others.length == 0) { + sendEntityNotificationMsg(tenantId, ruleChain.getId(), EdgeEventActionType.UPDATED); + } else { + RuleChain updatedRuleChain = (RuleChain)others[0]; + RuleChainMetaData updatedRuleChainMetaData = (RuleChainMetaData)others[1]; + SecurityUser user = (SecurityUser)others[2]; + logEntityAction(tenantId, updatedRuleChain.getId(), updatedRuleChain, null, ActionType.UPDATED, user, updatedRuleChainMetaData); + if (RuleChainType.EDGE.equals(ruleChain.getType())) { + sendEntityNotificationMsg(updatedRuleChain.getTenantId(), updatedRuleChain.getId(), EdgeEventActionType.UPDATED); + } + } + } + @Override public void notifyAssignOrUnassignEntityToCustomer(TenantId tenantId, I entityId, CustomerId customerId, E entity, @@ -104,10 +133,9 @@ public class DefaultTbNotificationEntityService implements TbNotificationEntityS public void notifyAssignOrUnassignEntityToEdge(TenantId tenantId, I entityId, CustomerId customerId, EdgeId edgeId, E entity, ActionType actionType, - EdgeEventActionType edgeActionType, SecurityUser user, Object... additionalInfo) { logEntityAction(tenantId, entityId, entity, customerId, actionType, user, additionalInfo); - sendEntityAssignToEdgeNotificationMsg(tenantId, edgeId, entityId, edgeActionType); + sendEntityAssignToEdgeNotificationMsg(tenantId, edgeId, entityId, edgeTypeByActionType(actionType)); } @Override @@ -207,10 +235,10 @@ public class DefaultTbNotificationEntityService implements TbNotificationEntityS @Override public void notifyCreateOrUpdateOrDelete(TenantId tenantId, CustomerId customerId, I entityId, E entity, SecurityUser user, - ActionType actionType, Exception e, + ActionType actionType, boolean isSendMsg, Exception e, Object... additionalInfo) { notifyEntity(tenantId, entityId, entity, customerId, actionType, user, e, additionalInfo); - if (e == null) { + if (isSendMsg) { sendEntityNotificationMsg(tenantId, entityId, edgeTypeByActionType(actionType)); } } @@ -334,6 +362,10 @@ public class DefaultTbNotificationEntityService implements TbNotificationEntityS return EdgeEventActionType.RELATION_ADD_OR_UPDATE; case RELATION_DELETED: return EdgeEventActionType.RELATION_DELETED; + case ASSIGNED_TO_EDGE: + return EdgeEventActionType.ASSIGNED_TO_EDGE; + case UNASSIGNED_FROM_EDGE: + return EdgeEventActionType.UNASSIGNED_FROM_EDGE; default: return null; } diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/TbNotificationEntityService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/TbNotificationEntityService.java index 97d9b0ca58..59ae1aca8e 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/TbNotificationEntityService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/TbNotificationEntityService.java @@ -29,6 +29,7 @@ import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; import org.thingsboard.server.common.data.relation.EntityRelation; +import org.thingsboard.server.common.data.rule.RuleChain; import org.thingsboard.server.common.data.security.DeviceCredentials; import org.thingsboard.server.service.security.model.SecurityUser; @@ -50,10 +51,14 @@ public interface TbNotificationEntityService { SecurityUser user, Object... additionalInfo); void notifyDeleteAlarm(TenantId tenantId, Alarm alarm, EntityId originatorId, - CustomerId customerId, ActionType actionType, - List relatedEdgeIds, + CustomerId customerId, List relatedEdgeIds, SecurityUser user, String body, Object... additionalInfo); + void notifyDeleteRuleChane(TenantId tenantId, RuleChain ruleChain, + List relatedEdgeIds, SecurityUser user); + + void notifySaveRuleChainMetaData(TenantId tenantId, RuleChain ruleChain, Object... others); + void notifyAssignOrUnassignEntityToCustomer(TenantId tenantId, I entityId, CustomerId customerId, E entity, ActionType actionType, @@ -64,7 +69,6 @@ public interface TbNotificationEntityService { void notifyAssignOrUnassignEntityToEdge(TenantId tenantId, I entityId, CustomerId customerId, EdgeId edgeId, E entity, ActionType actionType, - EdgeEventActionType edgeActionType, SecurityUser user, Object... additionalInfo); void notifyCreateOruUpdateTenant(Tenant tenant, ComponentLifecycleEvent event); @@ -91,7 +95,7 @@ public interface TbNotificationEntityService { void notifyCreateOrUpdateOrDelete(TenantId tenantId, CustomerId customerId, I entityId, E entity, SecurityUser user, - ActionType actionType, Exception e, + ActionType actionType, boolean isSendMsg, Exception e, Object... additionalInfo); void notifyCreateOrUpdateOrDeleteRelation(TenantId tenantId, CustomerId customerId, diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/alarm/DefaultTbAlarmService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/alarm/DefaultTbAlarmService.java index f00bd06de3..4930f54a5d 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/alarm/DefaultTbAlarmService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/alarm/DefaultTbAlarmService.java @@ -81,7 +81,7 @@ public class DefaultTbAlarmService extends AbstractTbEntityService implements Tb try { List relatedEdgeIds = findRelatedEdgeIds(user.getTenantId(), alarm.getOriginator()); notificationEntityService.notifyDeleteAlarm(user.getTenantId(), alarm, alarm.getOriginator(), user.getCustomerId(), - ActionType.DELETED, relatedEdgeIds, user, JacksonUtil.OBJECT_MAPPER.writeValueAsString(alarm)); + relatedEdgeIds, user, JacksonUtil.OBJECT_MAPPER.writeValueAsString(alarm)); return alarmService.deleteAlarm(user.getTenantId(), alarm.getId()).isSuccessful(); } catch (Exception e) { throw handleException(e); diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/asset/DefaultTbAssetService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/asset/DefaultTbAssetService.java index 4be68b75f9..492464f147 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/asset/DefaultTbAssetService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/asset/DefaultTbAssetService.java @@ -135,7 +135,7 @@ public class DefaultTbAssetService extends AbstractTbEntityService implements Tb try { Asset savedAsset = checkNotNull(assetService.assignAssetToEdge(tenantId, assetId, edgeId)); notificationEntityService.notifyAssignOrUnassignEntityToEdge(tenantId, assetId, savedAsset.getCustomerId(), - edgeId, savedAsset, actionType, EdgeEventActionType.ASSIGNED_TO_EDGE, user, assetId.toString(), edgeId.toString(), edge.getName()); + edgeId, savedAsset, actionType, user, assetId.toString(), edgeId.toString(), edge.getName()); return savedAsset; } catch (Exception e) { @@ -154,7 +154,7 @@ public class DefaultTbAssetService extends AbstractTbEntityService implements Tb Asset savedAsset = checkNotNull(assetService.unassignAssetFromEdge(tenantId, assetId, edgeId)); notificationEntityService.notifyAssignOrUnassignEntityToEdge(tenantId, assetId, asset.getCustomerId(), - edgeId, asset, actionType, EdgeEventActionType.UNASSIGNED_FROM_EDGE, user, assetId.toString(), edgeId.toString(), edge.getName()); + edgeId, asset, actionType, user, assetId.toString(), edgeId.toString(), edge.getName()); return savedAsset; } catch (Exception e) { notificationEntityService.notifyEntity(tenantId, emptyId(EntityType.ASSET), null, null, diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/customer/DefaultTbCustomerService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/customer/DefaultTbCustomerService.java index 838e32f427..d35cb06a85 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/customer/DefaultTbCustomerService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/customer/DefaultTbCustomerService.java @@ -17,7 +17,6 @@ package org.thingsboard.server.service.entitiy.customer; import lombok.AllArgsConstructor; import org.springframework.stereotype.Service; -import org.thingsboard.server.cluster.TbClusterService; import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.audit.ActionType; @@ -37,8 +36,6 @@ import java.util.List; @AllArgsConstructor public class DefaultTbCustomerService extends AbstractTbEntityService implements TbCustomerService { - private final TbClusterService tbClusterService; - @Override public Customer save(Customer customer, SecurityUser user) throws ThingsboardException { ActionType actionType = customer.getId() == null ? ActionType.ADDED : ActionType.UPDATED; diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/dashboard/DefaultTbDashboardService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/dashboard/DefaultTbDashboardService.java index 141963e2e3..f7110c4da7 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/dashboard/DefaultTbDashboardService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/dashboard/DefaultTbDashboardService.java @@ -226,7 +226,7 @@ public class DefaultTbDashboardService extends AbstractTbEntityService implement try { Dashboard savedDashboard = checkNotNull(dashboardService.assignDashboardToEdge(tenantId, dashboardId, edgeId)); notificationEntityService.notifyAssignOrUnassignEntityToEdge(tenantId, dashboardId, user.getCustomerId(), - edgeId, savedDashboard, actionType, EdgeEventActionType.ASSIGNED_TO_EDGE, user, dashboardId.toString(), + edgeId, savedDashboard, actionType, user, dashboardId.toString(), edgeId.toString(), edge.getName()); return savedDashboard; } catch (Exception e) { @@ -246,7 +246,7 @@ public class DefaultTbDashboardService extends AbstractTbEntityService implement Dashboard savedDevice = checkNotNull(dashboardService.unassignDashboardFromEdge(tenantId, dashboardId, edgeId)); notificationEntityService.notifyAssignOrUnassignEntityToEdge(tenantId, dashboardId, user.getCustomerId(), - edgeId, dashboard, actionType, EdgeEventActionType.UNASSIGNED_FROM_EDGE, user, dashboardId.toString(), + edgeId, dashboard, actionType, user, dashboardId.toString(), edgeId.toString(), edge.getName()); return savedDevice; } catch (Exception e) { diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/device/DefaultTbDeviceService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/device/DefaultTbDeviceService.java index 08e3a6f4cd..d74610a462 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/device/DefaultTbDeviceService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/device/DefaultTbDeviceService.java @@ -248,7 +248,7 @@ public class DefaultTbDeviceService extends AbstractTbEntityService implements T try { Device savedDevice = checkNotNull(deviceService.assignDeviceToEdge(tenantId, deviceId, edgeId)); notificationEntityService.notifyAssignOrUnassignEntityToEdge(tenantId, deviceId, savedDevice.getCustomerId(), - edgeId, savedDevice, actionType, EdgeEventActionType.ASSIGNED_TO_EDGE, user, deviceId.toString(), edgeId.toString(), edge.getName()); + edgeId, savedDevice, actionType, user, deviceId.toString(), edgeId.toString(), edge.getName()); return savedDevice; } catch (Exception e) { notificationEntityService.notifyEntity(tenantId, emptyId(EntityType.DEVICE), null, null, @@ -267,7 +267,7 @@ public class DefaultTbDeviceService extends AbstractTbEntityService implements T Device savedDevice = checkNotNull(deviceService.unassignDeviceFromEdge(tenantId, deviceId, edgeId)); notificationEntityService.notifyAssignOrUnassignEntityToEdge(tenantId, deviceId, device.getCustomerId(), - edgeId, device, actionType, EdgeEventActionType.UNASSIGNED_FROM_EDGE, user, deviceId.toString(), edgeId.toString(), edge.getName()); + edgeId, device, actionType, user, deviceId.toString(), edgeId.toString(), edge.getName()); return savedDevice; } catch (Exception e) { notificationEntityService.notifyEntity(tenantId, emptyId(EntityType.DEVICE), null, null, diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/deviceProfile/DefaultTbDeviceProfileService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/deviceProfile/DefaultTbDeviceProfileService.java index 1c2b3bf79c..ca73b0cb83 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/deviceProfile/DefaultTbDeviceProfileService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/deviceProfile/DefaultTbDeviceProfileService.java @@ -61,11 +61,10 @@ public class DefaultTbDeviceProfileService extends AbstractTbEntityService imple otaPackageStateService.update(savedDeviceProfile, isFirmwareChanged, isSoftwareChanged); - notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, null, savedDeviceProfile.getId(), savedDeviceProfile, user, actionType, null); + notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, null, savedDeviceProfile.getId(), savedDeviceProfile, user, actionType, true, null); return savedDeviceProfile; } catch (Exception e) { - notificationEntityService.notifyEntity(tenantId, emptyId(EntityType.DEVICE_PROFILE), deviceProfile, null, - actionType, user, e); + notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, null, emptyId(EntityType.DEVICE_PROFILE), deviceProfile, user, actionType, false, e); throw handleException(e); } } @@ -79,9 +78,9 @@ public class DefaultTbDeviceProfileService extends AbstractTbEntityService imple tbClusterService.onDeviceProfileDelete(deviceProfile, null); tbClusterService.broadcastEntityStateChangeEvent(tenantId, deviceProfileId, ComponentLifecycleEvent.DELETED); - notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, null, deviceProfileId, deviceProfile, user, ActionType.DELETED, null, deviceProfileId.toString()); + notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, null, deviceProfileId, deviceProfile, user, ActionType.DELETED, true, null, deviceProfileId.toString()); } catch (Exception e) { - notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, null, emptyId(EntityType.DEVICE_PROFILE), null, user, ActionType.DELETED, e, deviceProfileId.toString()); + notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, null, emptyId(EntityType.DEVICE_PROFILE), null, user, ActionType.DELETED, false, e, deviceProfileId.toString()); throw handleException(e); } } diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/entityView/DefaultTbEntityViewService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/entityView/DefaultTbEntityViewService.java index 7816330825..878c6d32bb 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/entityView/DefaultTbEntityViewService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/entityView/DefaultTbEntityViewService.java @@ -112,7 +112,7 @@ public class DefaultTbEntityViewService extends AbstractTbEntityService implemen try { List relatedEdgeIds = findRelatedEdgeIds(tenantId, entityViewId); entityViewService.deleteEntityView(tenantId, entityViewId); - notificationEntityService.notifyDeleteEntity(tenantId, entityViewId, entityView, user != null ? user.getCustomerId() : null, ActionType.DELETED, + notificationEntityService.notifyDeleteEntity(tenantId, entityViewId, entityView, entityView.getCustomerId(), ActionType.DELETED, relatedEdgeIds, user, entityViewId.toString()); } catch (Exception e) { notificationEntityService.notifyEntity(tenantId, emptyId(EntityType.ENTITY_VIEW), null, null, @@ -162,7 +162,7 @@ public class DefaultTbEntityViewService extends AbstractTbEntityService implemen EntityView savedEntityView = checkNotNull(entityViewService.assignEntityViewToEdge(tenantId, entityViewId, edgeId)); try { notificationEntityService.notifyAssignOrUnassignEntityToEdge(tenantId, entityViewId, customerId, - edgeId, savedEntityView, actionType, EdgeEventActionType.ASSIGNED_TO_EDGE, user, savedEntityView.getEntityId().toString(), + edgeId, savedEntityView, actionType, user, savedEntityView.getEntityId().toString(), edgeId.toString(), edge.getName()); return savedEntityView; } catch (Exception e) { @@ -181,7 +181,7 @@ public class DefaultTbEntityViewService extends AbstractTbEntityService implemen try { EntityView savedEntityView = checkNotNull(entityViewService.unassignEntityViewFromEdge(tenantId, entityViewId, edgeId)); notificationEntityService.notifyAssignOrUnassignEntityToEdge(tenantId, entityViewId, customerId, - edgeId, entityView, actionType, EdgeEventActionType.UNASSIGNED_FROM_EDGE, user, entityViewId.toString(), + edgeId, entityView, actionType, user, entityViewId.toString(), edgeId.toString(), edge.getName()); return savedEntityView; } catch (Exception e) { diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/ruleChain/DefaultTbRuleChainNotifyService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/ruleChain/DefaultTbRuleChainNotifyService.java new file mode 100644 index 0000000000..1e47409e54 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/ruleChain/DefaultTbRuleChainNotifyService.java @@ -0,0 +1,266 @@ +/** + * Copyright © 2016-2022 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.service.entitiy.ruleChain; + +import lombok.AllArgsConstructor; +import org.springframework.stereotype.Service; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.audit.ActionType; +import org.thingsboard.server.common.data.edge.Edge; +import org.thingsboard.server.common.data.exception.ThingsboardException; +import org.thingsboard.server.common.data.id.EdgeId; +import org.thingsboard.server.common.data.id.RuleChainId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; +import org.thingsboard.server.common.data.rule.DefaultRuleChainCreateRequest; +import org.thingsboard.server.common.data.rule.RuleChain; +import org.thingsboard.server.common.data.rule.RuleChainMetaData; +import org.thingsboard.server.common.data.rule.RuleChainType; +import org.thingsboard.server.common.data.rule.RuleChainUpdateResult; +import org.thingsboard.server.common.data.rule.RuleNode; +import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.entitiy.AbstractTbEntityService; +import org.thingsboard.server.service.security.model.SecurityUser; + +import java.util.Collections; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +@Service +@TbCoreComponent +@AllArgsConstructor +public class DefaultTbRuleChainNotifyService extends AbstractTbEntityService implements TbRuleChainNotifyService { + + @Override + public RuleChain save(RuleChain ruleChain, SecurityUser user) throws ThingsboardException { + TenantId tenantId = ruleChain.getTenantId(); + ActionType actionType = ruleChain.getId() == null ? ActionType.ADDED : ActionType.UPDATED; + try { + RuleChain savedRuleChain = checkNotNull(ruleChainService.saveRuleChain(ruleChain)); + + if (RuleChainType.CORE.equals(savedRuleChain.getType())) { + tbClusterService.broadcastEntityStateChangeEvent(tenantId, savedRuleChain.getId(), + actionType.equals(ActionType.ADDED) ? ComponentLifecycleEvent.CREATED : ComponentLifecycleEvent.UPDATED); + } + boolean isSendMsg = RuleChainType.EDGE.equals(savedRuleChain.getType()) && actionType.equals(ActionType.UPDATED); + notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, null, savedRuleChain.getId(), + savedRuleChain, user, actionType, isSendMsg, null); + return savedRuleChain; + } catch (Exception e) { + notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, null, emptyId(EntityType.RULE_CHAIN), + ruleChain, user, actionType, false, e); + throw handleException(e); + } + } + + @Override + public void delete(RuleChain ruleChain, SecurityUser user) throws ThingsboardException { + TenantId tenantId = ruleChain.getTenantId(); + RuleChainId ruleChainId = ruleChain.getId(); + try { + + + List referencingRuleNodes = ruleChainService.getReferencingRuleChainNodes(tenantId, ruleChainId); + + Set referencingRuleChainIds = referencingRuleNodes.stream().map(RuleNode::getRuleChainId).collect(Collectors.toSet()); + + List relatedEdgeIds = null; + if (RuleChainType.EDGE.equals(ruleChain.getType())) { + relatedEdgeIds = findRelatedEdgeIds(tenantId, ruleChainId); + } + + ruleChainService.deleteRuleChainById(tenantId, ruleChainId); + + referencingRuleChainIds.remove(ruleChain.getId()); + + if (RuleChainType.CORE.equals(ruleChain.getType())) { + referencingRuleChainIds.forEach(referencingRuleChainId -> + tbClusterService.broadcastEntityStateChangeEvent(tenantId, referencingRuleChainId, ComponentLifecycleEvent.UPDATED)); + + tbClusterService.broadcastEntityStateChangeEvent(tenantId, ruleChain.getId(), ComponentLifecycleEvent.DELETED); + } + + notificationEntityService.notifyDeleteRuleChane(tenantId, ruleChain, relatedEdgeIds, user); + } catch (Exception e) { + notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, null, emptyId(EntityType.RULE_CHAIN), + null, user, ActionType.DELETED, false, e, ruleChainId.toString()); + throw handleException(e); + } + } + + @Override + public RuleChain saveRuleChain(TenantId tenantId, DefaultRuleChainCreateRequest request, SecurityUser user) throws ThingsboardException { + try { + RuleChain savedRuleChain = installScripts.createDefaultRuleChain(tenantId, request.getName()); + tbClusterService.broadcastEntityStateChangeEvent(tenantId, savedRuleChain.getId(), ComponentLifecycleEvent.CREATED); + notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, null, savedRuleChain.getId(), + savedRuleChain, user, ActionType.ADDED, false, null); + return savedRuleChain; + } catch (Exception e) { + RuleChain ruleChain = new RuleChain(); + ruleChain.setName(request.getName()); + notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, null, emptyId(EntityType.RULE_CHAIN), + ruleChain, user, ActionType.ADDED, false, e); + throw handleException(e); + } + } + + @Override + public RuleChain setRootRuleChain(TenantId tenantId, RuleChain ruleChain, SecurityUser user) throws ThingsboardException { + RuleChain previousRootRuleChain = ruleChainService.getRootTenantRuleChain(tenantId); + RuleChainId ruleChainId = previousRootRuleChain.getId(); + try { + if (ruleChainService.setRootRuleChain(tenantId, ruleChainId)) { + if (previousRootRuleChain != null) { + previousRootRuleChain = ruleChainService.findRuleChainById(tenantId, previousRootRuleChain.getId()); + + tbClusterService.broadcastEntityStateChangeEvent(tenantId, previousRootRuleChain.getId(), + ComponentLifecycleEvent.UPDATED); + notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, null,ruleChainId, + previousRootRuleChain, user, ActionType.UPDATED, false, null); + } + ruleChain = ruleChainService.findRuleChainById(tenantId, ruleChainId); + + tbClusterService.broadcastEntityStateChangeEvent(tenantId, ruleChain.getId(), + ComponentLifecycleEvent.UPDATED); + notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, null, ruleChainId, + ruleChain, user, ActionType.UPDATED, false, null); + } + return ruleChain; + } catch (Exception e) { + notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, null, emptyId(EntityType.RULE_CHAIN), + ruleChain, user, ActionType.UPDATED, false, e, ruleChainId.toString()); + throw handleException(e); + } + } + + @Override + public RuleChainMetaData saveRuleChainMetaData(TenantId tenantId, RuleChain ruleChain, RuleChainMetaData ruleChainMetaData, + boolean updateRelated, SecurityUser user) throws ThingsboardException { + RuleChainId ruleChainId = ruleChain.getId(); + try { + + RuleChainUpdateResult result = ruleChainService.saveRuleChainMetaData(tenantId, ruleChainMetaData); + checkNotNull(result.isSuccess() ? true : null); + + List updatedRuleChains; + if (updateRelated && result.isSuccess()) { + updatedRuleChains = tbRuleChainService.updateRelatedRuleChains(tenantId, ruleChainMetaData.getRuleChainId(), result); + } else { + updatedRuleChains = Collections.emptyList(); + } + + RuleChainMetaData savedRuleChainMetaData = checkNotNull(ruleChainService.loadRuleChainMetaData(tenantId, ruleChainMetaData.getRuleChainId())); + + if (RuleChainType.CORE.equals(ruleChain.getType())) { + tbClusterService.broadcastEntityStateChangeEvent(ruleChain.getTenantId(), ruleChain.getId(), ComponentLifecycleEvent.UPDATED); + updatedRuleChains.forEach(updatedRuleChain -> { + tbClusterService.broadcastEntityStateChangeEvent(updatedRuleChain.getTenantId(), updatedRuleChain.getId(), ComponentLifecycleEvent.UPDATED); + }); + } + + notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, null, ruleChainId, + ruleChain, user, ActionType.UPDATED, false, null, ruleChainMetaData); + + if (RuleChainType.EDGE.equals(ruleChain.getType())) { + notificationEntityService.notifySaveRuleChainMetaData(tenantId, ruleChain); + } + + for (RuleChain updatedRuleChain : updatedRuleChains) { + RuleChainMetaData updatedRuleChainMetaData = checkNotNull(ruleChainService.loadRuleChainMetaData(tenantId, + updatedRuleChain.getId())); + notificationEntityService.notifySaveRuleChainMetaData(tenantId, ruleChain, updatedRuleChain, + updatedRuleChainMetaData, user); + } + + return savedRuleChainMetaData; + } catch (Exception e) { + notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, null, emptyId(EntityType.RULE_CHAIN), + null, user, ActionType.ADDED, false, e, ruleChainMetaData); + throw handleException(e); + } + } + + @Override + public RuleChain assignRuleChainToEdge(TenantId tenantId, RuleChainId ruleChainId, Edge edge, SecurityUser user) throws ThingsboardException { + try { + RuleChain savedRuleChain = checkNotNull(ruleChainService.assignRuleChainToEdge(tenantId, ruleChainId, edge.getId())); + notificationEntityService.notifyAssignOrUnassignEntityToEdge(tenantId, ruleChainId, + null, edge.getId(), + savedRuleChain, ActionType.ASSIGNED_TO_EDGE, + user, ruleChainId.toString(), edge.getId().toString(), edge.getName()); + return savedRuleChain; + } catch (Exception e) { + notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, null, emptyId(EntityType.RULE_CHAIN), + null, user, ActionType.ASSIGNED_TO_EDGE, false, e, ruleChainId.toString(), edge.getId().toString()); + throw handleException(e); + } + } + + @Override + public RuleChain unassignRuleChainFromEdge(TenantId tenantId, RuleChain ruleChain, Edge edge, SecurityUser user) throws ThingsboardException { + RuleChainId ruleChainId = ruleChain.getId(); + EdgeId edgeId = edge.getId(); + try { + RuleChain savedRuleChain = checkNotNull(ruleChainService.unassignRuleChainFromEdge(tenantId, ruleChainId, edgeId, false)); + notificationEntityService.notifyAssignOrUnassignEntityToEdge(tenantId, ruleChainId, + null, edge.getId(), + savedRuleChain, ActionType.UNASSIGNED_FROM_EDGE, + user, ruleChainId.toString(), edge.getId().toString(), edge.getName()); + return savedRuleChain; + } catch (Exception e) { + notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, null, emptyId(EntityType.RULE_CHAIN), + null, user, ActionType.UNASSIGNED_FROM_EDGE, false, e, ruleChainId.toString(), edge.getId().toString()); + throw handleException(e); + } + } + + @Override + public RuleChain setEdgeRuleChain(TenantId tenantId, RuleChain ruleChain, SecurityUser user, boolean isSetEdgeTemplateRootRuleChain) throws ThingsboardException { + RuleChainId ruleChainId = ruleChain.getId(); + try { + if (isSetEdgeTemplateRootRuleChain) { + ruleChainService.setEdgeTemplateRootRuleChain(tenantId, ruleChainId); + } else { + ruleChainService.setAutoAssignToEdgeRuleChain(tenantId, ruleChainId); + } + + notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, null, ruleChainId, + ruleChain, user, ActionType.UPDATED, false, null); + return ruleChain; + } catch (Exception e) { + notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, null, emptyId(EntityType.RULE_CHAIN), + null, user, ActionType.UPDATED, false, e, ruleChainId.toString()); + throw handleException(e); + } + } + + @Override + public RuleChain unsetAutoAssignToEdgeRuleChain(TenantId tenantId, RuleChain ruleChain, SecurityUser user) throws ThingsboardException { + RuleChainId ruleChainId = ruleChain.getId(); + try { + ruleChainService.unsetAutoAssignToEdgeRuleChain(tenantId, ruleChainId); + notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, null, ruleChainId, + ruleChain, user, ActionType.UPDATED, false, null); + return ruleChain; + } catch (Exception e) { + notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, null, emptyId(EntityType.RULE_CHAIN), + null, user, ActionType.UPDATED, false, e, ruleChainId.toString()); + throw handleException(e); + } + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/ruleChain/TbRuleChainNotifyService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/ruleChain/TbRuleChainNotifyService.java new file mode 100644 index 0000000000..b4cf9d5097 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/ruleChain/TbRuleChainNotifyService.java @@ -0,0 +1,44 @@ +/** + * Copyright © 2016-2022 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.service.entitiy.ruleChain; + +import org.thingsboard.server.common.data.edge.Edge; +import org.thingsboard.server.common.data.exception.ThingsboardException; +import org.thingsboard.server.common.data.id.RuleChainId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.rule.DefaultRuleChainCreateRequest; +import org.thingsboard.server.common.data.rule.RuleChain; +import org.thingsboard.server.common.data.rule.RuleChainMetaData; +import org.thingsboard.server.service.entitiy.SimpleTbEntityService; +import org.thingsboard.server.service.security.model.SecurityUser; + +public interface TbRuleChainNotifyService extends SimpleTbEntityService { + + RuleChain saveRuleChain(TenantId tenantId, DefaultRuleChainCreateRequest request, SecurityUser user) throws ThingsboardException; + + RuleChain setRootRuleChain(TenantId tenantId, RuleChain ruleChain, SecurityUser user) throws ThingsboardException; + + RuleChainMetaData saveRuleChainMetaData(TenantId tenantId, RuleChain ruleChain, RuleChainMetaData ruleChainMetaData, + boolean updateRelated, SecurityUser user) throws ThingsboardException; + + RuleChain assignRuleChainToEdge(TenantId tenantId, RuleChainId ruleChainId, Edge edge, SecurityUser user) throws ThingsboardException; + + RuleChain unassignRuleChainFromEdge(TenantId tenantId, RuleChain ruleChain, Edge edge, SecurityUser user) throws ThingsboardException; + + RuleChain setEdgeRuleChain(TenantId tenantId, RuleChain ruleChain, SecurityUser user, boolean isSetEdgeTemplateRootRuleChain) throws ThingsboardException; + + RuleChain unsetAutoAssignToEdgeRuleChain(TenantId tenantId, RuleChain ruleChain, SecurityUser user) throws ThingsboardException; +} From 6714f263ae282e904b460d98081e30a6c1207774 Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Sat, 28 May 2022 14:12:09 +0300 Subject: [PATCH 714/798] refactoring: RuleChainController del double code --- .../org/thingsboard/server/controller/RuleChainController.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java b/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java index 0fc91b2453..0ab6564c48 100644 --- a/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java +++ b/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java @@ -608,7 +608,6 @@ public class RuleChainController extends BaseController { checkParameter(RULE_CHAIN_ID, strRuleChainId); RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId)); RuleChain ruleChain = checkRuleChain(ruleChainId, Operation.WRITE); - TenantId tenantId = getTenantId(); return tbRuleChainNotifyService.setEdgeRuleChain(getTenantId(), ruleChain, getCurrentUser(), true); } @@ -623,7 +622,6 @@ public class RuleChainController extends BaseController { checkParameter(RULE_CHAIN_ID, strRuleChainId); RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId)); RuleChain ruleChain = checkRuleChain(ruleChainId, Operation.WRITE); - TenantId tenantId = getTenantId(); return tbRuleChainNotifyService.setEdgeRuleChain(getTenantId(), ruleChain, getCurrentUser(), false); } From f0d887b91aa8a9870302246ff92c0ce4d0dcb23a Mon Sep 17 00:00:00 2001 From: Andrii Shvaika Date: Mon, 30 May 2022 12:16:30 +0300 Subject: [PATCH 715/798] NPE fix in DefaultTbContext --- .../server/actors/ruleChain/DefaultTbContext.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java index 66fe935d68..9456e710df 100644 --- a/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java +++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java @@ -169,7 +169,9 @@ class DefaultTbContext implements TbContext { private void enqueue(TopicPartitionInfo tpi, TbMsg tbMsg, Consumer onFailure, Runnable onSuccess) { if (!tbMsg.isValid()) { log.trace("[{}] Skip invalid message: {}", getTenantId(), tbMsg); - onFailure.accept(new IllegalArgumentException("Source message is no longer valid!")); + if (onFailure != null) { + onFailure.accept(new IllegalArgumentException("Source message is no longer valid!")); + } return; } TransportProtos.ToRuleEngineMsg msg = TransportProtos.ToRuleEngineMsg.newBuilder() @@ -242,7 +244,9 @@ class DefaultTbContext implements TbContext { private void enqueueForTellNext(TopicPartitionInfo tpi, String queueName, TbMsg source, Set relationTypes, String failureMessage, Runnable onSuccess, Consumer onFailure) { if (!source.isValid()) { log.trace("[{}] Skip invalid message: {}", getTenantId(), source); - onFailure.accept(new IllegalArgumentException("Source message is no longer valid!")); + if (onFailure != null) { + onFailure.accept(new IllegalArgumentException("Source message is no longer valid!")); + } return; } RuleChainId ruleChainId = nodeCtx.getSelf().getRuleChainId(); From 65541b9aed2c7e2a87568f1f5ed08ae309001227 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Mon, 30 May 2022 13:00:01 +0300 Subject: [PATCH 716/798] UI: Fix style sms auth dialog --- .../authentication-dialog/sms-auth-dialog.component.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui-ngx/src/app/modules/home/pages/security/authentication-dialog/sms-auth-dialog.component.html b/ui-ngx/src/app/modules/home/pages/security/authentication-dialog/sms-auth-dialog.component.html index 25a444257e..d4fb60095a 100644 --- a/ui-ngx/src/app/modules/home/pages/security/authentication-dialog/sms-auth-dialog.component.html +++ b/ui-ngx/src/app/modules/home/pages/security/authentication-dialog/sms-auth-dialog.component.html @@ -36,7 +36,7 @@ {{ 'security.2fa.dialog.sms-step-label' | translate }}

security.2fa.dialog.sms-step-description

-
+
Date: Mon, 30 May 2022 13:21:22 +0300 Subject: [PATCH 717/798] refactoring: RuleChainController fix bug clientSideRpc() --- .../ruleChain/DefaultTbRuleChainNotifyService.java | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/ruleChain/DefaultTbRuleChainNotifyService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/ruleChain/DefaultTbRuleChainNotifyService.java index 1e47409e54..833f10e21b 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/ruleChain/DefaultTbRuleChainNotifyService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/ruleChain/DefaultTbRuleChainNotifyService.java @@ -122,20 +122,21 @@ public class DefaultTbRuleChainNotifyService extends AbstractTbEntityService imp @Override public RuleChain setRootRuleChain(TenantId tenantId, RuleChain ruleChain, SecurityUser user) throws ThingsboardException { RuleChain previousRootRuleChain = ruleChainService.getRootTenantRuleChain(tenantId); - RuleChainId ruleChainId = previousRootRuleChain.getId(); + RuleChainId previousRootRuleChainId = previousRootRuleChain.getId(); + RuleChainId ruleChainId = ruleChain.getId(); try { if (ruleChainService.setRootRuleChain(tenantId, ruleChainId)) { if (previousRootRuleChain != null) { - previousRootRuleChain = ruleChainService.findRuleChainById(tenantId, previousRootRuleChain.getId()); + previousRootRuleChain = ruleChainService.findRuleChainById(tenantId, previousRootRuleChainId); - tbClusterService.broadcastEntityStateChangeEvent(tenantId, previousRootRuleChain.getId(), + tbClusterService.broadcastEntityStateChangeEvent(tenantId, previousRootRuleChainId, ComponentLifecycleEvent.UPDATED); - notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, null,ruleChainId, + notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, null, previousRootRuleChainId, previousRootRuleChain, user, ActionType.UPDATED, false, null); } ruleChain = ruleChainService.findRuleChainById(tenantId, ruleChainId); - tbClusterService.broadcastEntityStateChangeEvent(tenantId, ruleChain.getId(), + tbClusterService.broadcastEntityStateChangeEvent(tenantId, ruleChainId, ComponentLifecycleEvent.UPDATED); notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, null, ruleChainId, ruleChain, user, ActionType.UPDATED, false, null); From a5c1cce884ef436be7efa2822377237e69f2c1ee Mon Sep 17 00:00:00 2001 From: YevhenBondarenko Date: Mon, 30 May 2022 12:30:00 +0200 Subject: [PATCH 718/798] added queue to rest client --- .../server/controller/QueueController.java | 10 -- .../thingsboard/rest/client/RestClient.java | 100 ++++++++++++------ 2 files changed, 70 insertions(+), 40 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/controller/QueueController.java b/application/src/main/java/org/thingsboard/server/controller/QueueController.java index 83ae71f766..39a971e6ed 100644 --- a/application/src/main/java/org/thingsboard/server/controller/QueueController.java +++ b/application/src/main/java/org/thingsboard/server/controller/QueueController.java @@ -15,10 +15,7 @@ */ package org.thingsboard.server.controller; -import io.swagger.annotations.ApiOperation; -import io.swagger.annotations.ApiParam; import lombok.RequiredArgsConstructor; -import org.springframework.http.MediaType; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestBody; @@ -38,14 +35,7 @@ import org.thingsboard.server.service.entitiy.queue.TbQueueService; import org.thingsboard.server.service.security.permission.Operation; import org.thingsboard.server.service.security.permission.Resource; -import java.util.Collections; -import java.util.Set; import java.util.UUID; -import java.util.stream.Collectors; - -import static org.thingsboard.server.controller.ControllerConstants.QUEUE_SERVICE_TYPE_ALLOWABLE_VALUES; -import static org.thingsboard.server.controller.ControllerConstants.QUEUE_SERVICE_TYPE_DESCRIPTION; -import static org.thingsboard.server.controller.ControllerConstants.TENANT_AUTHORITY_PARAGRAPH; @RestController @TbCoreComponent diff --git a/rest-client/src/main/java/org/thingsboard/rest/client/RestClient.java b/rest-client/src/main/java/org/thingsboard/rest/client/RestClient.java index fdbbb82464..fe78879125 100644 --- a/rest-client/src/main/java/org/thingsboard/rest/client/RestClient.java +++ b/rest-client/src/main/java/org/thingsboard/rest/client/RestClient.java @@ -37,7 +37,6 @@ import org.springframework.util.MultiValueMap; import org.springframework.util.StringUtils; import org.springframework.web.client.HttpClientErrorException; import org.springframework.web.client.RestTemplate; -import org.springframework.web.multipart.MultipartFile; import org.thingsboard.common.util.ThingsBoardExecutors; import org.thingsboard.rest.client.utils.RestJsonConverter; import org.thingsboard.server.common.data.AdminSettings; @@ -91,6 +90,7 @@ import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityViewId; import org.thingsboard.server.common.data.id.OAuth2ClientRegistrationTemplateId; import org.thingsboard.server.common.data.id.OtaPackageId; +import org.thingsboard.server.common.data.id.QueueId; import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.RuleNodeId; import org.thingsboard.server.common.data.id.TbResourceId; @@ -119,6 +119,7 @@ import org.thingsboard.server.common.data.query.AlarmDataQuery; import org.thingsboard.server.common.data.query.EntityCountQuery; import org.thingsboard.server.common.data.query.EntityData; import org.thingsboard.server.common.data.query.EntityDataQuery; +import org.thingsboard.server.common.data.queue.Queue; import org.thingsboard.server.common.data.relation.EntityRelation; import org.thingsboard.server.common.data.relation.EntityRelationInfo; import org.thingsboard.server.common.data.relation.EntityRelationsQuery; @@ -146,7 +147,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; -import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.Future; import java.util.stream.Collectors; @@ -328,11 +328,11 @@ public class RestClient implements ClientHttpRequestInterceptor, Closeable { params.put("entityType", entityId.getEntityType().name()); params.put("entityId", entityId.getId().toString()); params.put("fetchOriginator", String.valueOf(fetchOriginator)); - if(searchStatus != null) { + if (searchStatus != null) { params.put("searchStatus", searchStatus.name()); urlSecondPart += "&searchStatus={searchStatus}"; } - if(status != null) { + if (status != null) { params.put("status", status.name()); urlSecondPart += "&status={status}"; } @@ -340,7 +340,7 @@ public class RestClient implements ClientHttpRequestInterceptor, Closeable { addTimePageLinkToParam(params, pageLink); return restTemplate.exchange( - baseURL + urlSecondPart + "&" + getTimeUrlParams(pageLink), + baseURL + urlSecondPart + "&" + getTimeUrlParams(pageLink), HttpMethod.GET, HttpEntity.EMPTY, new ParameterizedTypeReference>() { @@ -523,12 +523,12 @@ public class RestClient implements ClientHttpRequestInterceptor, Closeable { public List getAssetsByIds(List assetIds) { return restTemplate.exchange( - baseURL + "/api/assets?assetIds={assetIds}", - HttpMethod.GET, - HttpEntity.EMPTY, - new ParameterizedTypeReference>() { - }, - listIdsToString(assetIds)) + baseURL + "/api/assets?assetIds={assetIds}", + HttpMethod.GET, + HttpEntity.EMPTY, + new ParameterizedTypeReference>() { + }, + listIdsToString(assetIds)) .getBody(); } @@ -543,7 +543,7 @@ public class RestClient implements ClientHttpRequestInterceptor, Closeable { public List getAssetTypes() { return restTemplate.exchange(URI.create( - baseURL + "/api/asset/types"), + baseURL + "/api/asset/types"), HttpMethod.GET, HttpEntity.EMPTY, new ParameterizedTypeReference>() { @@ -746,13 +746,13 @@ public class RestClient implements ClientHttpRequestInterceptor, Closeable { public List getComponentDescriptorsByTypes(List componentTypes, RuleChainType ruleChainType) { return restTemplate.exchange( - baseURL + "/api/components?componentTypes={componentTypes}&ruleChainType={ruleChainType}", - HttpMethod.GET, - HttpEntity.EMPTY, - new ParameterizedTypeReference>() { - }, - listEnumToString(componentTypes), - ruleChainType) + baseURL + "/api/components?componentTypes={componentTypes}&ruleChainType={ruleChainType}", + HttpMethod.GET, + HttpEntity.EMPTY, + new ParameterizedTypeReference>() { + }, + listEnumToString(componentTypes), + ruleChainType) .getBody(); } @@ -2904,7 +2904,8 @@ public class RestClient implements ClientHttpRequestInterceptor, Closeable { baseURL + "/api/resource/{resourceId}/download", HttpMethod.GET, HttpEntity.EMPTY, - new ParameterizedTypeReference<>() {}, + new ParameterizedTypeReference<>() { + }, params ); } @@ -2917,7 +2918,8 @@ public class RestClient implements ClientHttpRequestInterceptor, Closeable { baseURL + "/api/resource/info/{resourceId}", HttpMethod.GET, HttpEntity.EMPTY, - new ParameterizedTypeReference() {}, + new ParameterizedTypeReference() { + }, params ).getBody(); } @@ -2930,7 +2932,8 @@ public class RestClient implements ClientHttpRequestInterceptor, Closeable { baseURL + "/api/resource/{resourceId}", HttpMethod.GET, HttpEntity.EMPTY, - new ParameterizedTypeReference() {}, + new ParameterizedTypeReference() { + }, params ).getBody(); } @@ -2950,7 +2953,8 @@ public class RestClient implements ClientHttpRequestInterceptor, Closeable { baseURL + "/api/resource?" + getUrlParams(pageLink), HttpMethod.GET, HttpEntity.EMPTY, - new ParameterizedTypeReference>() {}, + new ParameterizedTypeReference>() { + }, params ).getBody(); } @@ -2967,7 +2971,8 @@ public class RestClient implements ClientHttpRequestInterceptor, Closeable { baseURL + "/api/otaPackage/{otaPackageId}/download", HttpMethod.GET, HttpEntity.EMPTY, - new ParameterizedTypeReference<>() {}, + new ParameterizedTypeReference<>() { + }, params ); } @@ -2980,7 +2985,8 @@ public class RestClient implements ClientHttpRequestInterceptor, Closeable { baseURL + "/api/otaPackage/info/{otaPackageId}", HttpMethod.GET, HttpEntity.EMPTY, - new ParameterizedTypeReference() {}, + new ParameterizedTypeReference() { + }, params ).getBody(); } @@ -2993,7 +2999,8 @@ public class RestClient implements ClientHttpRequestInterceptor, Closeable { baseURL + "/api/otaPackage/{otaPackageId}", HttpMethod.GET, HttpEntity.EMPTY, - new ParameterizedTypeReference() {}, + new ParameterizedTypeReference() { + }, params ).getBody(); } @@ -3004,13 +3011,13 @@ public class RestClient implements ClientHttpRequestInterceptor, Closeable { return restTemplate.postForEntity(baseURL + "/api/otaPackage?isUrl={isUrl}", otaPackageInfo, OtaPackageInfo.class, params).getBody(); } - public OtaPackageInfo saveOtaPackageData(OtaPackageId otaPackageId, String checkSum, ChecksumAlgorithm checksumAlgorithm, MultipartFile file) throws Exception { + public OtaPackageInfo saveOtaPackageData(OtaPackageId otaPackageId, String checkSum, ChecksumAlgorithm checksumAlgorithm, String fileName, byte[] fileBytes) throws Exception { HttpHeaders header = new HttpHeaders(); header.setContentType(MediaType.MULTIPART_FORM_DATA); MultiValueMap fileMap = new LinkedMultiValueMap<>(); - fileMap.add(HttpHeaders.CONTENT_DISPOSITION, "form-data; name=file; filename=" + file.getName()); - HttpEntity fileEntity = new HttpEntity<>(new ByteArrayResource(file.getBytes()), fileMap); + fileMap.add(HttpHeaders.CONTENT_DISPOSITION, "form-data; name=file; filename=" + fileName); + HttpEntity fileEntity = new HttpEntity<>(new ByteArrayResource(fileBytes), fileMap); MultiValueMap body = new LinkedMultiValueMap<>(); body.add("file", fileEntity); @@ -3021,7 +3028,7 @@ public class RestClient implements ClientHttpRequestInterceptor, Closeable { params.put("checksumAlgorithm", checksumAlgorithm.name()); String url = "/api/otaPackage/{otaPackageId}?checksumAlgorithm={checksumAlgorithm}"; - if(checkSum != null) { + if (checkSum != null) { url += "&checkSum={checkSum}"; } @@ -3068,6 +3075,39 @@ public class RestClient implements ClientHttpRequestInterceptor, Closeable { restTemplate.delete(baseURL + "/api/otaPackage/{otaPackageId}", otaPackageId.getId().toString()); } + public PageData getQueuesByServiceType(String serviceType, PageLink pageLink) { + Map params = new HashMap<>(); + params.put("serviceType", serviceType); + addPageLinkToParam(params, pageLink); + + return restTemplate.exchange( + baseURL + "/api/queues?{serviceType}&" + getUrlParams(pageLink), + HttpMethod.GET, + HttpEntity.EMPTY, + new ParameterizedTypeReference>() { + }, + params + ).getBody(); + } + + public Queue getQueueById(QueueId queueId) { + return restTemplate.exchange( + baseURL + "/api/queue/" + queueId, + HttpMethod.GET, + HttpEntity.EMPTY, + new ParameterizedTypeReference() { + } + ).getBody(); + } + + public Queue saveQueue(Queue queue, String serviceType) { + return restTemplate.postForEntity(baseURL + "/api/queues?serviceType=" + serviceType, queue, Queue.class).getBody(); + } + + public void deleteQueue(QueueId queueId) { + restTemplate.delete(baseURL + "/api/queues/" + queueId); + } + @Deprecated public Optional getAttributes(String accessToken, String clientKeys, String sharedKeys) { Map params = new HashMap<>(); From 19c313dbedfa9cca37f0c42aaba7f2064fa32686 Mon Sep 17 00:00:00 2001 From: fe-dev Date: Mon, 30 May 2022 14:54:41 +0300 Subject: [PATCH 719/798] Refactoring --- .../components/phone-input.component.html | 3 +- .../components/phone-input.component.ts | 34 ++++++++++++++----- .../src/app/shared/models/country.models.ts | 16 +++++++++ .../assets/locale/locale.constant-en_US.json | 2 +- 4 files changed, 44 insertions(+), 11 deletions(-) diff --git a/ui-ngx/src/app/shared/components/phone-input.component.html b/ui-ngx/src/app/shared/components/phone-input.component.html index 9001e04ce8..15ed9bf2df 100644 --- a/ui-ngx/src/app/shared/components/phone-input.component.html +++ b/ui-ngx/src/app/shared/components/phone-input.component.html @@ -27,7 +27,7 @@
- + phone-input.phone-input-label diff --git a/ui-ngx/src/app/shared/components/phone-input.component.ts b/ui-ngx/src/app/shared/components/phone-input.component.ts index 6618811e19..536b1cc723 100644 --- a/ui-ngx/src/app/shared/components/phone-input.component.ts +++ b/ui-ngx/src/app/shared/components/phone-input.component.ts @@ -59,6 +59,15 @@ export class PhoneInputComponent implements OnInit, ControlValueAccessor, Valida @Input() enableFlagsSelect = true; @Input() required = true; + private floatLabel = 'always'; + get showLabel(): string { + return this.floatLabel; + } + @Input() + set showLabel(value) { + this.floatLabel = value ? 'always' : 'never'; + } + allCountries: Array = []; phonePlaceholder: string; countryCallingCode: string; @@ -82,11 +91,9 @@ export class PhoneInputComponent implements OnInit, ControlValueAccessor, Valida } this.phoneFormGroup = this.fb.group({ country: [this.defaultCountry, []], - phoneNumber: [ - this.countryCallingCode, - this.required ? [Validators.required, Validators.pattern(phoneNumberPattern), this.validatePhoneNumber()] : [] - ] + phoneNumber: [null, this.required ? [Validators.required] : []] }); + this.phoneFormGroup.get('phoneNumber').setValidators([Validators.pattern(phoneNumberPattern), this.validatePhoneNumber()]); this.valueChange$ = this.phoneFormGroup.get('phoneNumber').valueChanges.subscribe(() => { this.updateModel(); @@ -97,12 +104,14 @@ export class PhoneInputComponent implements OnInit, ControlValueAccessor, Valida const code = this.countryCallingCode; this.getFlagAndPhoneNumberData(value); let phoneNumber = this.phoneFormGroup.get('phoneNumber').value; - if (code !== this.countryCallingCode && phoneNumber) { - phoneNumber = phoneNumber.replace(code, this.countryCallingCode); - } else { - phoneNumber = this.countryCallingCode; + if (phoneNumber) { + if (code !== this.countryCallingCode && phoneNumber.includes(code)) { + phoneNumber = phoneNumber.replace(code, this.countryCallingCode); + } else { + phoneNumber = this.countryCallingCode; + } + this.phoneFormGroup.get('phoneNumber').patchValue(phoneNumber); } - this.phoneFormGroup.get('phoneNumber').patchValue(phoneNumber); } }); @@ -123,6 +132,13 @@ export class PhoneInputComponent implements OnInit, ControlValueAccessor, Valida } } + focus() { + const phoneNumber = this.phoneFormGroup.get('phoneNumber'); + if (!phoneNumber.value) { + phoneNumber.patchValue(this.countryCallingCode); + } + } + getFlagAndPhoneNumberData(country) { if (this.enableFlagsSelect) { this.flagIcon = this.getFlagIcon(country); diff --git a/ui-ngx/src/app/shared/models/country.models.ts b/ui-ngx/src/app/shared/models/country.models.ts index a4ed4d38d9..10612de015 100644 --- a/ui-ngx/src/app/shared/models/country.models.ts +++ b/ui-ngx/src/app/shared/models/country.models.ts @@ -1,3 +1,19 @@ +/// +/// Copyright © 2016-2022 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + import { Injectable } from '@angular/core'; export interface Country { diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index 050080d5ba..b720594fe0 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -3355,7 +3355,7 @@ "phone-input-label": "Phone number", "phone-input-required": "Phone number is required", "phone-input-validation": "Phone number is invalid or not possible", - "phone-input-pattern": "Invalid phone number. Should be in E.164 format, ex. +19995550123." + "phone-input-pattern": "Invalid phone number. Should be in E.164 format, ex. {{phoneNumber}}" }, "custom": { "widget-action": { From a6a58a3256421e32823f474f46db1c82b907f6e8 Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Mon, 30 May 2022 15:12:22 +0300 Subject: [PATCH 720/798] refactoring: RuleChainController comments1 --- .../controller/RuleChainController.java | 19 +++---- .../DefaultTbNotificationEntityService.java | 35 +++++------- .../entitiy/TbNotificationEntityService.java | 6 +- .../DefaultTbRuleChainNotifyService.java | 57 ++++++++----------- .../ruleChain/TbRuleChainNotifyService.java | 11 ++-- 5 files changed, 58 insertions(+), 70 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java b/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java index 0ab6564c48..e3dc0c0a98 100644 --- a/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java +++ b/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java @@ -43,6 +43,7 @@ import org.thingsboard.server.actors.tenant.DebugTbRateLimits; import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.Event; import org.thingsboard.server.common.data.StringUtils; +import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.edge.Edge; import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.EdgeId; @@ -66,7 +67,6 @@ import org.thingsboard.server.common.msg.TbMsgMetaData; import org.thingsboard.server.dao.event.EventService; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.entitiy.ruleChain.TbRuleChainNotifyService; -import org.thingsboard.server.service.install.InstallScripts; import org.thingsboard.server.service.rule.TbRuleChainService; import org.thingsboard.server.service.script.JsInvokeService; import org.thingsboard.server.service.script.RuleNodeJsScriptEngine; @@ -142,9 +142,6 @@ public class RuleChainController extends BaseController { @Autowired protected TbRuleChainService tbRuleChainService; - @Autowired - private InstallScripts installScripts; - @Autowired private EventService eventService; @@ -260,7 +257,7 @@ public class RuleChainController extends BaseController { checkNotNull(request); checkParameter(request.getName(), "name"); - return tbRuleChainNotifyService.saveRuleChain(getTenantId(), request, getCurrentUser()); + return tbRuleChainNotifyService.saveDefaultByName(getTenantId(), request, getCurrentUser()); } @ApiOperation(value = "Set Root Rule Chain (setRootRuleChain)", @@ -540,9 +537,10 @@ public class RuleChainController extends BaseController { Edge edge = checkEdgeId(edgeId, Operation.WRITE); RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId)); - checkRuleChain(ruleChainId, Operation.READ); + RuleChain ruleChain = checkRuleChain(ruleChainId, Operation.READ); - return tbRuleChainNotifyService.assignRuleChainToEdge(getTenantId(), ruleChainId, edge, getCurrentUser()); + return tbRuleChainNotifyService.assignUnassignRuleChainToEdge(getTenantId(), ruleChain, edge, + ActionType.ASSIGNED_TO_EDGE, getCurrentUser()); } @ApiOperation(value = "Unassign rule chain from edge (unassignRuleChainFromEdge)", @@ -564,7 +562,8 @@ public class RuleChainController extends BaseController { RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId)); RuleChain ruleChain = checkRuleChain(ruleChainId, Operation.READ); - return tbRuleChainNotifyService.unassignRuleChainFromEdge(getTenantId(), ruleChain, edge, getCurrentUser()); + return tbRuleChainNotifyService.assignUnassignRuleChainToEdge(getTenantId(), ruleChain, edge, + ActionType.UNASSIGNED_FROM_EDGE, getCurrentUser()); } @ApiOperation(value = "Get Edge Rule Chains (getEdgeRuleChains)", @@ -608,7 +607,7 @@ public class RuleChainController extends BaseController { checkParameter(RULE_CHAIN_ID, strRuleChainId); RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId)); RuleChain ruleChain = checkRuleChain(ruleChainId, Operation.WRITE); - return tbRuleChainNotifyService.setEdgeRuleChain(getTenantId(), ruleChain, getCurrentUser(), true); + return tbRuleChainNotifyService.setEdgeTemplateRootRuleChain(getTenantId(), ruleChain, getCurrentUser()); } @ApiOperation(value = "Set Auto Assign To Edge Rule Chain (setAutoAssignToEdgeRuleChain)", @@ -622,7 +621,7 @@ public class RuleChainController extends BaseController { checkParameter(RULE_CHAIN_ID, strRuleChainId); RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId)); RuleChain ruleChain = checkRuleChain(ruleChainId, Operation.WRITE); - return tbRuleChainNotifyService.setEdgeRuleChain(getTenantId(), ruleChain, getCurrentUser(), false); + return tbRuleChainNotifyService.setAutoAssignToEdgeRuleChain(getTenantId(), ruleChain, getCurrentUser()); } @ApiOperation(value = "Unset Auto Assign To Edge Rule Chain (unsetAutoAssignToEdgeRuleChain)", diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/DefaultTbNotificationEntityService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/DefaultTbNotificationEntityService.java index 2ed55ac126..c6526476d2 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/DefaultTbNotificationEntityService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/DefaultTbNotificationEntityService.java @@ -77,7 +77,7 @@ public class DefaultTbNotificationEntityService implements TbNotificationEntityS CustomerId customerId, ActionType actionType, List relatedEdgeIds, SecurityUser user, Object... additionalInfo) { - logEntityAction(tenantId, entityId, entity, customerId, actionType, user, additionalInfo); + logEntityAction(tenantId, entityId, entity, customerId, actionType, user, null, additionalInfo); sendDeleteNotificationMsg(tenantId, entityId, entity, relatedEdgeIds); } @@ -86,15 +86,15 @@ public class DefaultTbNotificationEntityService implements TbNotificationEntityS List relatedEdgeIds, SecurityUser user, String body, Object... additionalInfo) { - logEntityAction(tenantId, originatorId, alarm, customerId, ActionType.DELETED, user, additionalInfo); + logEntityAction(tenantId, originatorId, alarm, customerId, ActionType.DELETED, user, null, additionalInfo); sendAlarmDeleteNotificationMsg(tenantId, alarm, relatedEdgeIds, body); } @Override - public void notifyDeleteRuleChane(TenantId tenantId, RuleChain ruleChain, + public void notifyDeleteRuleChain(TenantId tenantId, RuleChain ruleChain, List relatedEdgeIds, SecurityUser user) { RuleChainId ruleChainId = ruleChain.getId(); - logEntityAction(tenantId, ruleChainId, ruleChain, null, ActionType.DELETED, user, ruleChainId.toString()); + logEntityAction(tenantId, ruleChainId, ruleChain, null, ActionType.DELETED, user, null, ruleChainId.toString()); if (RuleChainType.EDGE.equals(ruleChain.getType())) { sendDeleteNotificationMsg(tenantId, ruleChainId, relatedEdgeIds, null); } @@ -108,7 +108,7 @@ public class DefaultTbNotificationEntityService implements TbNotificationEntityS RuleChain updatedRuleChain = (RuleChain)others[0]; RuleChainMetaData updatedRuleChainMetaData = (RuleChainMetaData)others[1]; SecurityUser user = (SecurityUser)others[2]; - logEntityAction(tenantId, updatedRuleChain.getId(), updatedRuleChain, null, ActionType.UPDATED, user, updatedRuleChainMetaData); + logEntityAction(tenantId, updatedRuleChain.getId(), updatedRuleChain, null, ActionType.UPDATED, user, null, updatedRuleChainMetaData); if (RuleChainType.EDGE.equals(ruleChain.getType())) { sendEntityNotificationMsg(updatedRuleChain.getTenantId(), updatedRuleChain.getId(), EdgeEventActionType.UPDATED); } @@ -122,7 +122,7 @@ public class DefaultTbNotificationEntityService implements TbNotificationEntityS EdgeEventActionType edgeActionType, SecurityUser user, boolean sendToEdge, Object... additionalInfo) { - logEntityAction(tenantId, entityId, entity, customerId, actionType, user, additionalInfo); + logEntityAction(tenantId, entityId, entity, customerId, actionType, user, null, additionalInfo); if (sendToEdge) { sendEntityAssignToCustomerNotificationMsg(tenantId, entityId, customerId, edgeActionType); @@ -134,7 +134,7 @@ public class DefaultTbNotificationEntityService implements TbNotificationEntityS CustomerId customerId, EdgeId edgeId, E entity, ActionType actionType, SecurityUser user, Object... additionalInfo) { - logEntityAction(tenantId, entityId, entity, customerId, actionType, user, additionalInfo); + logEntityAction(tenantId, entityId, entity, customerId, actionType, user, null, additionalInfo); sendEntityAssignToEdgeNotificationMsg(tenantId, edgeId, entityId, edgeTypeByActionType(actionType)); } @@ -155,7 +155,7 @@ public class DefaultTbNotificationEntityService implements TbNotificationEntityS Device device, Device oldDevice, ActionType actionType, SecurityUser user, Object... additionalInfo) { tbClusterService.onDeviceUpdated(device, oldDevice); - logEntityAction(tenantId, deviceId, device, customerId, actionType, user, additionalInfo); + logEntityAction(tenantId, deviceId, device, customerId, actionType, user, null, additionalInfo); } @Override @@ -172,19 +172,19 @@ public class DefaultTbNotificationEntityService implements TbNotificationEntityS DeviceCredentials deviceCredentials, SecurityUser user) { tbClusterService.pushMsgToCore(new DeviceCredentialsUpdateNotificationMsg(tenantId, deviceCredentials.getDeviceId(), deviceCredentials), null); sendEntityNotificationMsg(tenantId, deviceId, EdgeEventActionType.CREDENTIALS_UPDATED); - logEntityAction(tenantId, deviceId, device, customerId, ActionType.CREDENTIALS_UPDATED, user, deviceCredentials); + logEntityAction(tenantId, deviceId, device, customerId, ActionType.CREDENTIALS_UPDATED, user, null, deviceCredentials); } @Override public void notifyAssignDeviceToTenant(TenantId tenantId, TenantId newTenantId, DeviceId deviceId, CustomerId customerId, Device device, Tenant tenant, SecurityUser user, Object... additionalInfo) { - logEntityAction(tenantId, deviceId, device, customerId, ActionType.ASSIGNED_TO_TENANT, user, additionalInfo); + logEntityAction(tenantId, deviceId, device, customerId, ActionType.ASSIGNED_TO_TENANT, user, null, additionalInfo); pushAssignedFromNotification(tenant, newTenantId, device); } @Override public void notifyCreateOrUpdateEntity(TenantId tenantId, I entityId, E entity, CustomerId customerId, ActionType actionType, SecurityUser user, Object... additionalInfo) { - logEntityAction(tenantId, entityId, entity, customerId, actionType, user, additionalInfo); + logEntityAction(tenantId, entityId, entity, customerId, actionType, user, null, additionalInfo); if (actionType == ActionType.UPDATED) { sendEntityNotificationMsg(tenantId, entityId, EdgeEventActionType.UPDATED); } @@ -218,7 +218,7 @@ public class DefaultTbNotificationEntityService implements TbNotificationEntityS } tbClusterService.broadcastEntityStateChangeEvent(tenantId, edgeId, lifecycleEvent); - logEntityAction(tenantId, edgeId, edge, customerId, actionType, user, additionalInfo); + logEntityAction(tenantId, edgeId, edge, customerId, actionType, user, null, additionalInfo); //Send notification to edge if (edgeEventActionType != null) { @@ -228,17 +228,17 @@ public class DefaultTbNotificationEntityService implements TbNotificationEntityS @Override public void notifyCreateOrUpdateAlarm(Alarm alarm, ActionType actionType, SecurityUser user, Object... additionalInfo) { - logEntityAction(alarm.getTenantId(), alarm.getOriginator(), alarm, alarm.getCustomerId(), actionType, user, additionalInfo); + logEntityAction(alarm.getTenantId(), alarm.getOriginator(), alarm, alarm.getCustomerId(), actionType, user, null, additionalInfo); sendEntityNotificationMsg(alarm.getTenantId(), alarm.getId(), edgeTypeByActionType(actionType)); } @Override public void notifyCreateOrUpdateOrDelete(TenantId tenantId, CustomerId customerId, I entityId, E entity, SecurityUser user, - ActionType actionType, boolean isSendMsg, Exception e, + ActionType actionType, boolean sendNotifyMsgToEdge, Exception e, Object... additionalInfo) { notifyEntity(tenantId, entityId, entity, customerId, actionType, user, e, additionalInfo); - if (isSendMsg) { + if (sendNotifyMsgToEdge) { sendEntityNotificationMsg(tenantId, entityId, edgeTypeByActionType(actionType)); } } @@ -263,11 +263,6 @@ public class DefaultTbNotificationEntityService implements TbNotificationEntityS } } - private void logEntityAction(TenantId tenantId, I entityId, E entity, CustomerId customerId, - ActionType actionType, SecurityUser user, Object... additionalInfo) { - logEntityAction(tenantId, entityId, entity, customerId, actionType, user, null, additionalInfo); - } - private void logEntityAction(TenantId tenantId, I entityId, E entity, CustomerId customerId, ActionType actionType, SecurityUser user, Exception e, Object... additionalInfo) { if (user != null) { diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/TbNotificationEntityService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/TbNotificationEntityService.java index 59ae1aca8e..49510a4738 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/TbNotificationEntityService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/TbNotificationEntityService.java @@ -54,8 +54,8 @@ public interface TbNotificationEntityService { CustomerId customerId, List relatedEdgeIds, SecurityUser user, String body, Object... additionalInfo); - void notifyDeleteRuleChane(TenantId tenantId, RuleChain ruleChain, - List relatedEdgeIds, SecurityUser user); + void notifyDeleteRuleChain(TenantId tenantId, RuleChain ruleChain, + List relatedEdgeIds, SecurityUser user); void notifySaveRuleChainMetaData(TenantId tenantId, RuleChain ruleChain, Object... others); @@ -95,7 +95,7 @@ public interface TbNotificationEntityService { void notifyCreateOrUpdateOrDelete(TenantId tenantId, CustomerId customerId, I entityId, E entity, SecurityUser user, - ActionType actionType, boolean isSendMsg, Exception e, + ActionType actionType, boolean sendNotifyMsgToEdge, Exception e, Object... additionalInfo); void notifyCreateOrUpdateOrDeleteRelation(TenantId tenantId, CustomerId customerId, diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/ruleChain/DefaultTbRuleChainNotifyService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/ruleChain/DefaultTbRuleChainNotifyService.java index 833f10e21b..7291f77f8d 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/ruleChain/DefaultTbRuleChainNotifyService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/ruleChain/DefaultTbRuleChainNotifyService.java @@ -72,8 +72,6 @@ public class DefaultTbRuleChainNotifyService extends AbstractTbEntityService imp TenantId tenantId = ruleChain.getTenantId(); RuleChainId ruleChainId = ruleChain.getId(); try { - - List referencingRuleNodes = ruleChainService.getReferencingRuleChainNodes(tenantId, ruleChainId); Set referencingRuleChainIds = referencingRuleNodes.stream().map(RuleNode::getRuleChainId).collect(Collectors.toSet()); @@ -94,16 +92,16 @@ public class DefaultTbRuleChainNotifyService extends AbstractTbEntityService imp tbClusterService.broadcastEntityStateChangeEvent(tenantId, ruleChain.getId(), ComponentLifecycleEvent.DELETED); } - notificationEntityService.notifyDeleteRuleChane(tenantId, ruleChain, relatedEdgeIds, user); + notificationEntityService.notifyDeleteRuleChain(tenantId, ruleChain, relatedEdgeIds, user); } catch (Exception e) { notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, null, emptyId(EntityType.RULE_CHAIN), - null, user, ActionType.DELETED, false, e, ruleChainId.toString()); + null, user, ActionType.DELETED, false, e, ruleChainId.toString()); throw handleException(e); } } @Override - public RuleChain saveRuleChain(TenantId tenantId, DefaultRuleChainCreateRequest request, SecurityUser user) throws ThingsboardException { + public RuleChain saveDefaultByName(TenantId tenantId, DefaultRuleChainCreateRequest request, SecurityUser user) throws ThingsboardException { try { RuleChain savedRuleChain = installScripts.createDefaultRuleChain(tenantId, request.getName()); tbClusterService.broadcastEntityStateChangeEvent(tenantId, savedRuleChain.getId(), ComponentLifecycleEvent.CREATED); @@ -144,7 +142,7 @@ public class DefaultTbRuleChainNotifyService extends AbstractTbEntityService imp return ruleChain; } catch (Exception e) { notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, null, emptyId(EntityType.RULE_CHAIN), - ruleChain, user, ActionType.UPDATED, false, e, ruleChainId.toString()); + ruleChain, user, ActionType.UPDATED, false, e, ruleChainId.toString()); throw handleException(e); } } @@ -153,24 +151,24 @@ public class DefaultTbRuleChainNotifyService extends AbstractTbEntityService imp public RuleChainMetaData saveRuleChainMetaData(TenantId tenantId, RuleChain ruleChain, RuleChainMetaData ruleChainMetaData, boolean updateRelated, SecurityUser user) throws ThingsboardException { RuleChainId ruleChainId = ruleChain.getId(); + RuleChainId ruleChainMetaDataId = ruleChainMetaData.getRuleChainId(); try { - RuleChainUpdateResult result = ruleChainService.saveRuleChainMetaData(tenantId, ruleChainMetaData); checkNotNull(result.isSuccess() ? true : null); List updatedRuleChains; if (updateRelated && result.isSuccess()) { - updatedRuleChains = tbRuleChainService.updateRelatedRuleChains(tenantId, ruleChainMetaData.getRuleChainId(), result); + updatedRuleChains = tbRuleChainService.updateRelatedRuleChains(tenantId, ruleChainMetaDataId, result); } else { updatedRuleChains = Collections.emptyList(); } - RuleChainMetaData savedRuleChainMetaData = checkNotNull(ruleChainService.loadRuleChainMetaData(tenantId, ruleChainMetaData.getRuleChainId())); + RuleChainMetaData savedRuleChainMetaData = checkNotNull(ruleChainService.loadRuleChainMetaData(tenantId, ruleChainMetaDataId)); if (RuleChainType.CORE.equals(ruleChain.getType())) { - tbClusterService.broadcastEntityStateChangeEvent(ruleChain.getTenantId(), ruleChain.getId(), ComponentLifecycleEvent.UPDATED); + tbClusterService.broadcastEntityStateChangeEvent(tenantId, ruleChainId, ComponentLifecycleEvent.UPDATED); updatedRuleChains.forEach(updatedRuleChain -> { - tbClusterService.broadcastEntityStateChangeEvent(updatedRuleChain.getTenantId(), updatedRuleChain.getId(), ComponentLifecycleEvent.UPDATED); + tbClusterService.broadcastEntityStateChangeEvent(tenantId, updatedRuleChain.getId(), ComponentLifecycleEvent.UPDATED); }); } @@ -191,56 +189,51 @@ public class DefaultTbRuleChainNotifyService extends AbstractTbEntityService imp return savedRuleChainMetaData; } catch (Exception e) { notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, null, emptyId(EntityType.RULE_CHAIN), - null, user, ActionType.ADDED, false, e, ruleChainMetaData); + null, user, ActionType.ADDED, false, e, ruleChainMetaData); throw handleException(e); } } @Override - public RuleChain assignRuleChainToEdge(TenantId tenantId, RuleChainId ruleChainId, Edge edge, SecurityUser user) throws ThingsboardException { + public RuleChain assignUnassignRuleChainToEdge(TenantId tenantId, RuleChain ruleChain, Edge edge, ActionType actionType, SecurityUser user) throws ThingsboardException { + RuleChainId ruleChainId = ruleChain.getId(); try { RuleChain savedRuleChain = checkNotNull(ruleChainService.assignRuleChainToEdge(tenantId, ruleChainId, edge.getId())); notificationEntityService.notifyAssignOrUnassignEntityToEdge(tenantId, ruleChainId, null, edge.getId(), - savedRuleChain, ActionType.ASSIGNED_TO_EDGE, + savedRuleChain, actionType, user, ruleChainId.toString(), edge.getId().toString(), edge.getName()); return savedRuleChain; } catch (Exception e) { notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, null, emptyId(EntityType.RULE_CHAIN), - null, user, ActionType.ASSIGNED_TO_EDGE, false, e, ruleChainId.toString(), edge.getId().toString()); + null, user, actionType, false, e, ruleChainId.toString(), edge.getId().toString()); throw handleException(e); } } @Override - public RuleChain unassignRuleChainFromEdge(TenantId tenantId, RuleChain ruleChain, Edge edge, SecurityUser user) throws ThingsboardException { + public RuleChain setEdgeTemplateRootRuleChain(TenantId tenantId, RuleChain ruleChain, SecurityUser user) throws ThingsboardException { RuleChainId ruleChainId = ruleChain.getId(); - EdgeId edgeId = edge.getId(); try { - RuleChain savedRuleChain = checkNotNull(ruleChainService.unassignRuleChainFromEdge(tenantId, ruleChainId, edgeId, false)); - notificationEntityService.notifyAssignOrUnassignEntityToEdge(tenantId, ruleChainId, - null, edge.getId(), - savedRuleChain, ActionType.UNASSIGNED_FROM_EDGE, - user, ruleChainId.toString(), edge.getId().toString(), edge.getName()); - return savedRuleChain; + ruleChainService.setEdgeTemplateRootRuleChain(tenantId, ruleChainId); + + notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, null, ruleChainId, + ruleChain, user, ActionType.UPDATED, false, null); + return ruleChain; } catch (Exception e) { notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, null, emptyId(EntityType.RULE_CHAIN), - null, user, ActionType.UNASSIGNED_FROM_EDGE, false, e, ruleChainId.toString(), edge.getId().toString()); + null, user, ActionType.UPDATED, false, e, ruleChainId.toString()); throw handleException(e); } } @Override - public RuleChain setEdgeRuleChain(TenantId tenantId, RuleChain ruleChain, SecurityUser user, boolean isSetEdgeTemplateRootRuleChain) throws ThingsboardException { + public RuleChain setAutoAssignToEdgeRuleChain(TenantId tenantId, RuleChain ruleChain, SecurityUser user) throws ThingsboardException { RuleChainId ruleChainId = ruleChain.getId(); try { - if (isSetEdgeTemplateRootRuleChain) { - ruleChainService.setEdgeTemplateRootRuleChain(tenantId, ruleChainId); - } else { - ruleChainService.setAutoAssignToEdgeRuleChain(tenantId, ruleChainId); - } + ruleChainService.setAutoAssignToEdgeRuleChain(tenantId, ruleChainId); - notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, null, ruleChainId, + notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, null, ruleChainId, ruleChain, user, ActionType.UPDATED, false, null); return ruleChain; } catch (Exception e) { @@ -255,7 +248,7 @@ public class DefaultTbRuleChainNotifyService extends AbstractTbEntityService imp RuleChainId ruleChainId = ruleChain.getId(); try { ruleChainService.unsetAutoAssignToEdgeRuleChain(tenantId, ruleChainId); - notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, null, ruleChainId, + notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, null, ruleChainId, ruleChain, user, ActionType.UPDATED, false, null); return ruleChain; } catch (Exception e) { diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/ruleChain/TbRuleChainNotifyService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/ruleChain/TbRuleChainNotifyService.java index b4cf9d5097..a6f5011507 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/ruleChain/TbRuleChainNotifyService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/ruleChain/TbRuleChainNotifyService.java @@ -15,9 +15,9 @@ */ package org.thingsboard.server.service.entitiy.ruleChain; +import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.edge.Edge; import org.thingsboard.server.common.data.exception.ThingsboardException; -import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.rule.DefaultRuleChainCreateRequest; import org.thingsboard.server.common.data.rule.RuleChain; @@ -27,18 +27,19 @@ import org.thingsboard.server.service.security.model.SecurityUser; public interface TbRuleChainNotifyService extends SimpleTbEntityService { - RuleChain saveRuleChain(TenantId tenantId, DefaultRuleChainCreateRequest request, SecurityUser user) throws ThingsboardException; + RuleChain saveDefaultByName(TenantId tenantId, DefaultRuleChainCreateRequest request, SecurityUser user) throws ThingsboardException; RuleChain setRootRuleChain(TenantId tenantId, RuleChain ruleChain, SecurityUser user) throws ThingsboardException; RuleChainMetaData saveRuleChainMetaData(TenantId tenantId, RuleChain ruleChain, RuleChainMetaData ruleChainMetaData, boolean updateRelated, SecurityUser user) throws ThingsboardException; - RuleChain assignRuleChainToEdge(TenantId tenantId, RuleChainId ruleChainId, Edge edge, SecurityUser user) throws ThingsboardException; + RuleChain assignUnassignRuleChainToEdge(TenantId tenantId, RuleChain ruleChain, Edge edge, ActionType actionType, + SecurityUser user) throws ThingsboardException; - RuleChain unassignRuleChainFromEdge(TenantId tenantId, RuleChain ruleChain, Edge edge, SecurityUser user) throws ThingsboardException; + RuleChain setEdgeTemplateRootRuleChain(TenantId tenantId, RuleChain ruleChain, SecurityUser user) throws ThingsboardException; - RuleChain setEdgeRuleChain(TenantId tenantId, RuleChain ruleChain, SecurityUser user, boolean isSetEdgeTemplateRootRuleChain) throws ThingsboardException; + RuleChain setAutoAssignToEdgeRuleChain(TenantId tenantId, RuleChain ruleChain, SecurityUser user) throws ThingsboardException; RuleChain unsetAutoAssignToEdgeRuleChain(TenantId tenantId, RuleChain ruleChain, SecurityUser user) throws ThingsboardException; } From f68bbd58d7bf2ebd4588a6b4a2b17fc7ce3fb011 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Mon, 30 May 2022 15:35:07 +0300 Subject: [PATCH 721/798] Change 2fa mail pattern to replace code --- .../org/thingsboard/server/service/mail/DefaultMailService.java | 2 +- .../src/main/resources/templates/2fa.verification.code.ftl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/mail/DefaultMailService.java b/application/src/main/java/org/thingsboard/server/service/mail/DefaultMailService.java index b0c77f2794..f33f6d6a4e 100644 --- a/application/src/main/java/org/thingsboard/server/service/mail/DefaultMailService.java +++ b/application/src/main/java/org/thingsboard/server/service/mail/DefaultMailService.java @@ -316,7 +316,7 @@ public class DefaultMailService implements MailService { String subject = messages.getMessage("2fa.verification.code.subject", null, Locale.US); String message = mergeTemplateIntoString("2fa.verification.code.ftl", Map.of( TARGET_EMAIL, email, - "verificationCode", verificationCode, + "сode", verificationCode, "expirationTimeSeconds", expirationTimeSeconds )); diff --git a/application/src/main/resources/templates/2fa.verification.code.ftl b/application/src/main/resources/templates/2fa.verification.code.ftl index 994215ebcb..3c20b13ae8 100644 --- a/application/src/main/resources/templates/2fa.verification.code.ftl +++ b/application/src/main/resources/templates/2fa.verification.code.ftl @@ -76,7 +76,7 @@
From 151086c3946063636e4d231a7411d6e1df684dc5 Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Mon, 30 May 2022 16:26:17 +0300 Subject: [PATCH 722/798] refactoring: RuleChainController metadata --- .../DefaultTbNotificationEntityService.java | 17 +++----------- .../entitiy/TbNotificationEntityService.java | 2 +- .../DefaultTbRuleChainNotifyService.java | 23 ++++++++++++------- 3 files changed, 19 insertions(+), 23 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/DefaultTbNotificationEntityService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/DefaultTbNotificationEntityService.java index c6526476d2..1565fad175 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/DefaultTbNotificationEntityService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/DefaultTbNotificationEntityService.java @@ -41,7 +41,6 @@ import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; import org.thingsboard.server.common.data.relation.EntityRelation; import org.thingsboard.server.common.data.rule.RuleChain; -import org.thingsboard.server.common.data.rule.RuleChainMetaData; import org.thingsboard.server.common.data.rule.RuleChainType; import org.thingsboard.server.common.data.security.DeviceCredentials; import org.thingsboard.server.common.msg.TbMsg; @@ -101,19 +100,9 @@ public class DefaultTbNotificationEntityService implements TbNotificationEntityS } @Override - public void notifySaveRuleChainMetaData(TenantId tenantId, RuleChain ruleChain, Object... others) { - if (others.length == 0) { - sendEntityNotificationMsg(tenantId, ruleChain.getId(), EdgeEventActionType.UPDATED); - } else { - RuleChain updatedRuleChain = (RuleChain)others[0]; - RuleChainMetaData updatedRuleChainMetaData = (RuleChainMetaData)others[1]; - SecurityUser user = (SecurityUser)others[2]; - logEntityAction(tenantId, updatedRuleChain.getId(), updatedRuleChain, null, ActionType.UPDATED, user, null, updatedRuleChainMetaData); - if (RuleChainType.EDGE.equals(ruleChain.getType())) { - sendEntityNotificationMsg(updatedRuleChain.getTenantId(), updatedRuleChain.getId(), EdgeEventActionType.UPDATED); - } - } - } + public void notifySendMsgToEdgeService(TenantId tenantId, RuleChain ruleChain, EdgeEventActionType edgeEventActionType) { + sendEntityNotificationMsg(tenantId, ruleChain.getId(), edgeEventActionType); + } @Override public void notifyAssignOrUnassignEntityToCustomer(TenantId tenantId, I entityId, diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/TbNotificationEntityService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/TbNotificationEntityService.java index 49510a4738..009ea26676 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/TbNotificationEntityService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/TbNotificationEntityService.java @@ -57,7 +57,7 @@ public interface TbNotificationEntityService { void notifyDeleteRuleChain(TenantId tenantId, RuleChain ruleChain, List relatedEdgeIds, SecurityUser user); - void notifySaveRuleChainMetaData(TenantId tenantId, RuleChain ruleChain, Object... others); + void notifySendMsgToEdgeService(TenantId tenantId, RuleChain ruleChain, EdgeEventActionType edgeEventActionType); void notifyAssignOrUnassignEntityToCustomer(TenantId tenantId, I entityId, CustomerId customerId, E entity, diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/ruleChain/DefaultTbRuleChainNotifyService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/ruleChain/DefaultTbRuleChainNotifyService.java index 7291f77f8d..6c4059806f 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/ruleChain/DefaultTbRuleChainNotifyService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/ruleChain/DefaultTbRuleChainNotifyService.java @@ -20,6 +20,7 @@ import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.edge.Edge; +import org.thingsboard.server.common.data.edge.EdgeEventActionType; import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.EdgeId; import org.thingsboard.server.common.data.id.RuleChainId; @@ -176,16 +177,22 @@ public class DefaultTbRuleChainNotifyService extends AbstractTbEntityService imp ruleChain, user, ActionType.UPDATED, false, null, ruleChainMetaData); if (RuleChainType.EDGE.equals(ruleChain.getType())) { - notificationEntityService.notifySaveRuleChainMetaData(tenantId, ruleChain); - } - - for (RuleChain updatedRuleChain : updatedRuleChains) { - RuleChainMetaData updatedRuleChainMetaData = checkNotNull(ruleChainService.loadRuleChainMetaData(tenantId, - updatedRuleChain.getId())); - notificationEntityService.notifySaveRuleChainMetaData(tenantId, ruleChain, updatedRuleChain, - updatedRuleChainMetaData, user); + notificationEntityService.notifySendMsgToEdgeService(tenantId, ruleChain, EdgeEventActionType.UPDATED); } + updatedRuleChains.forEach(updatedRuleChain -> { + if (RuleChainType.EDGE.equals(ruleChain.getType())) { + notificationEntityService.notifySendMsgToEdgeService(tenantId, updatedRuleChain, EdgeEventActionType.UPDATED); + } else { + try { + RuleChainMetaData updatedRuleChainMetaData = checkNotNull(ruleChainService.loadRuleChainMetaData(tenantId, updatedRuleChain.getId())); + notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, null, updatedRuleChain.getId(), + updatedRuleChain, user, ActionType.UPDATED, false, null, updatedRuleChainMetaData); + } catch (ThingsboardException e) { + e.printStackTrace(); + } + } + }); return savedRuleChainMetaData; } catch (Exception e) { notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, null, emptyId(EntityType.RULE_CHAIN), From 993052bbe3ffda1703a241c4778fa3f6652c4728 Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Mon, 30 May 2022 16:46:30 +0300 Subject: [PATCH 723/798] refactoring: RuleChainController redundant empty lines --- .../org/thingsboard/server/controller/RuleChainController.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java b/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java index e3dc0c0a98..c6cc7eb086 100644 --- a/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java +++ b/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java @@ -294,7 +294,6 @@ public class RuleChainController extends BaseController { debugPerTenantLimits.remove(tenantId, debugTbRateLimits); } } - RuleChain ruleChain = checkRuleChain(ruleChainMetaData.getRuleChainId(), Operation.WRITE); return tbRuleChainNotifyService.saveRuleChainMetaData(tenantId, ruleChain, ruleChainMetaData, updateRelated, @@ -659,5 +658,4 @@ public class RuleChainController extends BaseController { throw handleException(e); } } - } From b050468e7f2d85994e3b7a4300804ab5d4a18808 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Mon, 30 May 2022 18:01:02 +0300 Subject: [PATCH 724/798] UI: Move resend code in login 2fa page --- .../service/mail/DefaultMailService.java | 2 +- .../templates/2fa.verification.code.ftl | 2 +- .../two-factor-auth-login.component.html | 43 +++++++++---------- .../two-factor-auth-login.component.scss | 3 +- .../assets/locale/locale.constant-en_US.json | 2 +- 5 files changed, 25 insertions(+), 27 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/mail/DefaultMailService.java b/application/src/main/java/org/thingsboard/server/service/mail/DefaultMailService.java index f33f6d6a4e..7e1a3d7fa7 100644 --- a/application/src/main/java/org/thingsboard/server/service/mail/DefaultMailService.java +++ b/application/src/main/java/org/thingsboard/server/service/mail/DefaultMailService.java @@ -316,7 +316,7 @@ public class DefaultMailService implements MailService { String subject = messages.getMessage("2fa.verification.code.subject", null, Locale.US); String message = mergeTemplateIntoString("2fa.verification.code.ftl", Map.of( TARGET_EMAIL, email, - "сode", verificationCode, + "code", verificationCode, "expirationTimeSeconds", expirationTimeSeconds )); diff --git a/application/src/main/resources/templates/2fa.verification.code.ftl b/application/src/main/resources/templates/2fa.verification.code.ftl index 3c20b13ae8..0db3cd8215 100644 --- a/application/src/main/resources/templates/2fa.verification.code.ftl +++ b/application/src/main/resources/templates/2fa.verification.code.ftl @@ -76,7 +76,7 @@ diff --git a/ui-ngx/src/app/modules/login/pages/login/two-factor-auth-login.component.html b/ui-ngx/src/app/modules/login/pages/login/two-factor-auth-login.component.html index cf9e3186ba..b59139b36a 100644 --- a/ui-ngx/src/app/modules/login/pages/login/two-factor-auth-login.component.html +++ b/ui-ngx/src/app/modules/login/pages/login/two-factor-auth-login.component.html @@ -46,33 +46,32 @@ [attr.inputmode]="inputMode" [pattern]="pattern" autocomplete="off" placeholder="{{ providersData.get(selectedProvider).placeholder | translate }}"/> - - + {{ 'security.2fa.dialog.verification-code-invalid' | translate }} - - {{ 'login.resend-code-wait' | translate : {time: countDownTime} }} - - - {{ 'login.resend-code-wait' | translate : {time: countDownTime} }} - -
- - + +
+
+
+ {{ 'login.resend-code-wait' | translate : {time: countDownTime} }} +
+ +
+
@@ -59,12 +68,12 @@ {{ 'action.continue' | translate }} -
-
+
+
{{ 'login.resend-code-wait' | translate : {time: countDownTime} }}
-
diff --git a/ui-ngx/src/app/modules/login/pages/login/two-factor-auth-login.component.scss b/ui-ngx/src/app/modules/login/pages/login/two-factor-auth-login.component.scss index 547b3b88a6..248d719b7b 100644 --- a/ui-ngx/src/app/modules/login/pages/login/two-factor-auth-login.component.scss +++ b/ui-ngx/src/app/modules/login/pages/login/two-factor-auth-login.component.scss @@ -60,6 +60,14 @@ font: 500 12px / 14px Roboto, "Helvetica Neue", sans-serif; color: rgba(255, 255, 255, 0.8); } + + .action-row:nth-child(n) { + min-height: 36px; + + .action-resend { + min-width: 50%; + } + } } } diff --git a/ui-ngx/src/app/modules/login/pages/login/two-factor-auth-login.component.ts b/ui-ngx/src/app/modules/login/pages/login/two-factor-auth-login.component.ts index 33ebd4d979..048fb83bbe 100644 --- a/ui-ngx/src/app/modules/login/pages/login/two-factor-auth-login.component.ts +++ b/ui-ngx/src/app/modules/login/pages/login/two-factor-auth-login.component.ts @@ -28,6 +28,7 @@ import { } from '@shared/models/two-factor-auth.models'; import { TranslateService } from '@ngx-translate/core'; import { interval, Subscription } from 'rxjs'; +import { isEqual } from '@core/utils'; @Component({ selector: 'tb-two-factor-auth-login', @@ -40,14 +41,15 @@ export class TwoFactorAuthLoginComponent extends PageComponent implements OnInit private prevProvider: TwoFactorAuthProviderType; private timer: Subscription; private minVerificationPeriod = 0; + private timerID: NodeJS.Timeout; - showResendButton = false; + showResendAction = false; selectedProvider: TwoFactorAuthProviderType; allowProviders: TwoFactorAuthProviderType[] = []; providersData = twoFactorAuthProvidersLoginData; providerDescription = ''; - disabledResendButton = true; + hideResendButton = true; countDownTime = 0; maxLengthInput = 6; @@ -88,7 +90,7 @@ export class TwoFactorAuthLoginComponent extends PageComponent implements OnInit }); if (this.selectedProvider !== TwoFactorAuthProviderType.TOTP) { this.sendCode(); - this.showResendButton = true; + this.showResendAction = true; } this.timer = interval(1000).subscribe(() => this.updatedTime()); } @@ -96,12 +98,28 @@ export class TwoFactorAuthLoginComponent extends PageComponent implements OnInit ngOnDestroy() { super.ngOnDestroy(); this.timer.unsubscribe(); + clearTimeout(this.timerID); } sendVerificationCode() { if (this.verificationForm.valid && this.selectedProvider) { this.authService.checkTwoFaVerificationCode(this.selectedProvider, this.verificationForm.get('verificationCode').value).subscribe( - () => {} + () => {}, + (error) => { + if (error.status === 400) { + this.verificationForm.get('verificationCode').setErrors({incorrectCode: true}); + } else if (error.status === 429) { + this.verificationForm.get('verificationCode').setErrors({tooManyRequest: true}); + this.timerID = setTimeout(() => { + let errors = this.verificationForm.get('verificationCode').errors; + delete errors.tooManyRequest; + if (isEqual(errors, {})) { + errors = null; + } + this.verificationForm.get('verificationCode').setErrors(errors); + }, 5000); + } + } ); } } @@ -109,7 +127,7 @@ export class TwoFactorAuthLoginComponent extends PageComponent implements OnInit selectProvider(type: TwoFactorAuthProviderType) { this.prevProvider = type === null ? this.selectedProvider : null; this.selectedProvider = type; - this.showResendButton = false; + this.showResendAction = false; if (type !== null) { this.verificationForm.get('verificationCode').reset(); const providerConfig = this.providersInfo.find(config => config.type === type); @@ -118,7 +136,7 @@ export class TwoFactorAuthLoginComponent extends PageComponent implements OnInit }); if (type !== TwoFactorAuthProviderType.TOTP && type !== TwoFactorAuthProviderType.BACKUP_CODE) { this.sendCode(); - this.showResendButton = true; + this.showResendAction = true; this.minVerificationPeriod = providerConfig?.minVerificationCodeSendPeriod || 30; } if (type === TwoFactorAuthProviderType.BACKUP_CODE) { @@ -150,7 +168,7 @@ export class TwoFactorAuthLoginComponent extends PageComponent implements OnInit if ($event) { $event.stopPropagation(); } - this.disabledResendButton = true; + this.hideResendButton = true; this.twoFactorAuthService.requestTwoFaVerificationCodeSend(this.selectedProvider).subscribe(() => { this.countDownTime = this.minVerificationPeriod; }, () => { @@ -171,7 +189,7 @@ export class TwoFactorAuthLoginComponent extends PageComponent implements OnInit if (this.countDownTime > 0) { this.countDownTime--; if (this.countDownTime === 0) { - this.disabledResendButton = false; + this.hideResendButton = false; } } } diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index a316993329..25f27ed346 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -2633,6 +2633,8 @@ "totp-step-label": "Get app", "verification-code": "6-digit code", "verification-code-invalid": "Invalid verification code format", + "verification-code-incorrect": "Verification code is incorrect", + "verification-code-many-request": "Too many requests check verification code", "verification-step-description": "Enter a 6-digit code we just sent to {{address}}", "verification-step-label": "Verification" }, From da058ccfc1f5edafa51ef00be444cc1a9f6682a5 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Tue, 31 May 2022 17:22:08 +0300 Subject: [PATCH 730/798] UI: Fixed validation --- .../data/security/model/mfa/PlatformTwoFaSettings.java | 2 +- .../home/pages/admin/two-factor-auth-settings.component.html | 5 +++-- .../home/pages/admin/two-factor-auth-settings.component.scss | 4 ++++ .../home/pages/admin/two-factor-auth-settings.component.ts | 4 ++-- .../app/modules/home/pages/security/security.component.ts | 3 +++ .../login/pages/login/two-factor-auth-login.component.ts | 3 ++- ui-ngx/src/assets/locale/locale.constant-en_US.json | 2 +- 7 files changed, 16 insertions(+), 7 deletions(-) diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/security/model/mfa/PlatformTwoFaSettings.java b/common/data/src/main/java/org/thingsboard/server/common/data/security/model/mfa/PlatformTwoFaSettings.java index 6f4b8d806f..fd72e7a027 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/security/model/mfa/PlatformTwoFaSettings.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/security/model/mfa/PlatformTwoFaSettings.java @@ -41,7 +41,7 @@ public class PlatformTwoFaSettings { private String verificationCodeCheckRateLimit; @Min(value = 0, message = "maximum number of verification failure before user lockout must be positive") private Integer maxVerificationFailuresBeforeUserLockout; - @Min(value = 1, message = "total amount of time allotted for verification must be greater than 0") + @Min(value = 60, message = "total amount of time allotted for verification must be greater than or equal 60") private Integer totalAllowedTimeForVerification; diff --git a/ui-ngx/src/app/modules/home/pages/admin/two-factor-auth-settings.component.html b/ui-ngx/src/app/modules/home/pages/admin/two-factor-auth-settings.component.html index 9e0f54243f..1c6124bdc7 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/two-factor-auth-settings.component.html +++ b/ui-ngx/src/app/modules/home/pages/admin/two-factor-auth-settings.component.html @@ -51,7 +51,8 @@ admin.2fa.issuer-name - + {{ "admin.2fa.issuer-name-required" | translate }} @@ -118,7 +119,7 @@
admin.2fa.total-allowed-time-for-verification - + {{ 'admin.2fa.total-allowed-time-for-verification-required' | translate }} diff --git a/ui-ngx/src/app/modules/home/pages/admin/two-factor-auth-settings.component.scss b/ui-ngx/src/app/modules/home/pages/admin/two-factor-auth-settings.component.scss index 86254926b7..254cc51c22 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/two-factor-auth-settings.component.scss +++ b/ui-ngx/src/app/modules/home/pages/admin/two-factor-auth-settings.component.scss @@ -73,6 +73,10 @@ :host ::ng-deep { .mat-expansion-panel { + .mat-expansion-panel-content { + font-size: 16px; + } + &.provider { .mat-expansion-panel-header > .mat-content { overflow: inherit; diff --git a/ui-ngx/src/app/modules/home/pages/admin/two-factor-auth-settings.component.ts b/ui-ngx/src/app/modules/home/pages/admin/two-factor-auth-settings.component.ts index 895b80ed08..f4c7c2f1c9 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/two-factor-auth-settings.component.ts +++ b/ui-ngx/src/app/modules/home/pages/admin/two-factor-auth-settings.component.ts @@ -122,7 +122,7 @@ export class TwoFactorAuthSettingsComponent extends PageComponent implements OnI ]], totalAllowedTimeForVerification: [3600, [ Validators.required, - Validators.min(1), + Validators.min(60), Validators.pattern(/^\d*$/) ]], verificationCodeCheckRateLimitEnable: [false], @@ -192,7 +192,7 @@ export class TwoFactorAuthSettingsComponent extends PageComponent implements OnI }; switch (provider) { case TwoFactorAuthProviderType.TOTP: - formControlConfig.issuerName = [{value: 'ThingsBoard', disabled: true}, Validators.required]; + formControlConfig.issuerName = [{value: 'ThingsBoard', disabled: true}, [Validators.required, Validators.pattern(/^\S+$/)]]; break; case TwoFactorAuthProviderType.SMS: formControlConfig.smsVerificationMessageTemplate = [{value: 'Verification code: ${code}', disabled: true}, [ diff --git a/ui-ngx/src/app/modules/home/pages/security/security.component.ts b/ui-ngx/src/app/modules/home/pages/security/security.component.ts index 34eda9781a..ab7d470e9e 100644 --- a/ui-ngx/src/app/modules/home/pages/security/security.component.ts +++ b/ui-ngx/src/app/modules/home/pages/security/security.component.ts @@ -170,6 +170,9 @@ export class SecurityComponent extends PageComponent implements OnInit, OnDestro confirm2FAChange(event: MouseEvent, provider: TwoFactorAuthProviderType) { event.stopPropagation(); event.preventDefault(); + if (this.twoFactorAuth.get(provider).disabled) { + return; + } if (this.twoFactorAuth.get(provider).value) { const providerName = this.translate.instant(`security.2fa.provider.${provider.toLowerCase()}`); this.dialogService.confirm( diff --git a/ui-ngx/src/app/modules/login/pages/login/two-factor-auth-login.component.ts b/ui-ngx/src/app/modules/login/pages/login/two-factor-auth-login.component.ts index 048fb83bbe..73401f2a0c 100644 --- a/ui-ngx/src/app/modules/login/pages/login/two-factor-auth-login.component.ts +++ b/ui-ngx/src/app/modules/login/pages/login/two-factor-auth-login.component.ts @@ -169,10 +169,11 @@ export class TwoFactorAuthLoginComponent extends PageComponent implements OnInit $event.stopPropagation(); } this.hideResendButton = true; + this.countDownTime = 0; this.twoFactorAuthService.requestTwoFaVerificationCodeSend(this.selectedProvider).subscribe(() => { this.countDownTime = this.minVerificationPeriod; }, () => { - this.countDownTime = 30; + this.countDownTime = this.minVerificationPeriod; }); } diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index 25f27ed346..f54ec4c3a1 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -332,7 +332,7 @@ "retry-verification-code-period-pattern": "Minimal period time is 5 sec", "retry-verification-code-period-required": "Retry verification code period is required.", "total-allowed-time-for-verification": "Total allowed time for verification (sec)", - "total-allowed-time-for-verification-pattern": "Total allowed time must be a positive integer.", + "total-allowed-time-for-verification-pattern": "Minimal total allowed time is 60 sec", "total-allowed-time-for-verification-required": "Total allowed time is required.", "use-system-two-factor-auth-settings": "Use system two factor auth settings", "verification-code-check-rate-limit": "Verification code check rate limit", From 00bd0e1bc043be528fc92c923894287cceb58aeb Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Tue, 31 May 2022 18:04:12 +0300 Subject: [PATCH 731/798] refactoring: OtaPackageController: comments5 --- .../DefaultTbNotificationEntityService.java | 25 +++++++++++-------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/DefaultTbNotificationEntityService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/DefaultTbNotificationEntityService.java index 1565fad175..a556caa16c 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/DefaultTbNotificationEntityService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/DefaultTbNotificationEntityService.java @@ -76,7 +76,7 @@ public class DefaultTbNotificationEntityService implements TbNotificationEntityS CustomerId customerId, ActionType actionType, List relatedEdgeIds, SecurityUser user, Object... additionalInfo) { - logEntityAction(tenantId, entityId, entity, customerId, actionType, user, null, additionalInfo); + logEntityAction(tenantId, entityId, entity, customerId, actionType, user, additionalInfo); sendDeleteNotificationMsg(tenantId, entityId, entity, relatedEdgeIds); } @@ -85,7 +85,7 @@ public class DefaultTbNotificationEntityService implements TbNotificationEntityS List relatedEdgeIds, SecurityUser user, String body, Object... additionalInfo) { - logEntityAction(tenantId, originatorId, alarm, customerId, ActionType.DELETED, user, null, additionalInfo); + logEntityAction(tenantId, originatorId, alarm, customerId, ActionType.DELETED, user, additionalInfo); sendAlarmDeleteNotificationMsg(tenantId, alarm, relatedEdgeIds, body); } @@ -111,7 +111,7 @@ public class DefaultTbNotificationEntityService implements TbNotificationEntityS EdgeEventActionType edgeActionType, SecurityUser user, boolean sendToEdge, Object... additionalInfo) { - logEntityAction(tenantId, entityId, entity, customerId, actionType, user, null, additionalInfo); + logEntityAction(tenantId, entityId, entity, customerId, actionType, user, additionalInfo); if (sendToEdge) { sendEntityAssignToCustomerNotificationMsg(tenantId, entityId, customerId, edgeActionType); @@ -123,7 +123,7 @@ public class DefaultTbNotificationEntityService implements TbNotificationEntityS CustomerId customerId, EdgeId edgeId, E entity, ActionType actionType, SecurityUser user, Object... additionalInfo) { - logEntityAction(tenantId, entityId, entity, customerId, actionType, user, null, additionalInfo); + logEntityAction(tenantId, entityId, entity, customerId, actionType, user, additionalInfo); sendEntityAssignToEdgeNotificationMsg(tenantId, edgeId, entityId, edgeTypeByActionType(actionType)); } @@ -144,7 +144,7 @@ public class DefaultTbNotificationEntityService implements TbNotificationEntityS Device device, Device oldDevice, ActionType actionType, SecurityUser user, Object... additionalInfo) { tbClusterService.onDeviceUpdated(device, oldDevice); - logEntityAction(tenantId, deviceId, device, customerId, actionType, user, null, additionalInfo); + logEntityAction(tenantId, deviceId, device, customerId, actionType, user, additionalInfo); } @Override @@ -161,19 +161,19 @@ public class DefaultTbNotificationEntityService implements TbNotificationEntityS DeviceCredentials deviceCredentials, SecurityUser user) { tbClusterService.pushMsgToCore(new DeviceCredentialsUpdateNotificationMsg(tenantId, deviceCredentials.getDeviceId(), deviceCredentials), null); sendEntityNotificationMsg(tenantId, deviceId, EdgeEventActionType.CREDENTIALS_UPDATED); - logEntityAction(tenantId, deviceId, device, customerId, ActionType.CREDENTIALS_UPDATED, user, null, deviceCredentials); + logEntityAction(tenantId, deviceId, device, customerId, ActionType.CREDENTIALS_UPDATED, user, deviceCredentials); } @Override public void notifyAssignDeviceToTenant(TenantId tenantId, TenantId newTenantId, DeviceId deviceId, CustomerId customerId, Device device, Tenant tenant, SecurityUser user, Object... additionalInfo) { - logEntityAction(tenantId, deviceId, device, customerId, ActionType.ASSIGNED_TO_TENANT, user, null, additionalInfo); + logEntityAction(tenantId, deviceId, device, customerId, ActionType.ASSIGNED_TO_TENANT, user, additionalInfo); pushAssignedFromNotification(tenant, newTenantId, device); } @Override public void notifyCreateOrUpdateEntity(TenantId tenantId, I entityId, E entity, CustomerId customerId, ActionType actionType, SecurityUser user, Object... additionalInfo) { - logEntityAction(tenantId, entityId, entity, customerId, actionType, user, null, additionalInfo); + logEntityAction(tenantId, entityId, entity, customerId, actionType, user, additionalInfo); if (actionType == ActionType.UPDATED) { sendEntityNotificationMsg(tenantId, entityId, EdgeEventActionType.UPDATED); } @@ -207,7 +207,7 @@ public class DefaultTbNotificationEntityService implements TbNotificationEntityS } tbClusterService.broadcastEntityStateChangeEvent(tenantId, edgeId, lifecycleEvent); - logEntityAction(tenantId, edgeId, edge, customerId, actionType, user, null, additionalInfo); + logEntityAction(tenantId, edgeId, edge, customerId, actionType, user, additionalInfo); //Send notification to edge if (edgeEventActionType != null) { @@ -217,7 +217,7 @@ public class DefaultTbNotificationEntityService implements TbNotificationEntityS @Override public void notifyCreateOrUpdateAlarm(Alarm alarm, ActionType actionType, SecurityUser user, Object... additionalInfo) { - logEntityAction(alarm.getTenantId(), alarm.getOriginator(), alarm, alarm.getCustomerId(), actionType, user, null, additionalInfo); + logEntityAction(alarm.getTenantId(), alarm.getOriginator(), alarm, alarm.getCustomerId(), actionType, user, additionalInfo); sendEntityNotificationMsg(alarm.getTenantId(), alarm.getId(), edgeTypeByActionType(actionType)); } @@ -252,6 +252,11 @@ public class DefaultTbNotificationEntityService implements TbNotificationEntityS } } + private void logEntityAction(TenantId tenantId, I entityId, E entity, CustomerId customerId, + ActionType actionType, SecurityUser user, Object... additionalInfo) { + logEntityAction(tenantId, entityId, entity, customerId, actionType, user, null, additionalInfo); + } + private void logEntityAction(TenantId tenantId, I entityId, E entity, CustomerId customerId, ActionType actionType, SecurityUser user, Exception e, Object... additionalInfo) { if (user != null) { From 1a2c3144b6501cac37e3821766962799b0475dac Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Tue, 31 May 2022 19:08:27 +0300 Subject: [PATCH 732/798] refactoring: 09_RbResourceController --- .../controller/TbResourceController.java | 38 +++++------------- .../resource/DefaultTbResourceService.java | 40 ++++++++++++++++++- .../service/resource/TbResourceService.java | 3 +- 3 files changed, 52 insertions(+), 29 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/controller/TbResourceController.java b/application/src/main/java/org/thingsboard/server/controller/TbResourceController.java index 24373f54a4..120449c137 100644 --- a/application/src/main/java/org/thingsboard/server/controller/TbResourceController.java +++ b/application/src/main/java/org/thingsboard/server/controller/TbResourceController.java @@ -17,6 +17,7 @@ package org.thingsboard.server.controller; import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiParam; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.core.io.ByteArrayResource; import org.springframework.http.HttpHeaders; @@ -30,10 +31,8 @@ import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; -import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.TbResource; import org.thingsboard.server.common.data.TbResourceInfo; -import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.TbResourceId; import org.thingsboard.server.common.data.lwm2m.LwM2mObject; @@ -41,6 +40,7 @@ import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.resource.TbResourceService; import org.thingsboard.server.service.security.permission.Operation; import org.thingsboard.server.service.security.permission.Resource; @@ -68,8 +68,11 @@ import static org.thingsboard.server.controller.ControllerConstants.UUID_WIKI_LI @RestController @TbCoreComponent @RequestMapping("/api") +@RequiredArgsConstructor public class TbResourceController extends BaseController { + private final TbResourceService tbResourceService; + public static final String RESOURCE_ID = "resourceId"; @ApiOperation(value = "Download Resource (downloadResource)", notes = "Download Resource based on the provided Resource Id." + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH) @@ -144,20 +147,9 @@ public class TbResourceController extends BaseController { @ResponseBody public TbResource saveResource(@ApiParam(value = "A JSON value representing the Resource.") @RequestBody TbResource resource) throws ThingsboardException { - boolean created = resource.getId() == null; - try { - resource.setTenantId(getTenantId()); - checkEntity(resource.getId(), resource, Resource.TB_RESOURCE); - TbResource savedResource = checkNotNull(resourceService.saveResource(resource)); - tbClusterService.onResourceChange(savedResource, null); - logEntityAction(savedResource.getId(), savedResource, - null, created ? ActionType.ADDED : ActionType.UPDATED, null); - return savedResource; - } catch (Exception e) { - logEntityAction(emptyId(EntityType.TB_RESOURCE), resource, - null, created ? ActionType.ADDED : ActionType.UPDATED, e); - throw handleException(e); - } + resource.setTenantId(getTenantId()); + checkEntity(resource.getId(), resource, Resource.TB_RESOURCE); + return tbResourceService.save(resource, getCurrentUser()); } @ApiOperation(value = "Get Resource Infos (getResources)", @@ -242,16 +234,8 @@ public class TbResourceController extends BaseController { public void deleteResource(@ApiParam(value = RESOURCE_ID_PARAM_DESCRIPTION) @PathVariable("resourceId") String strResourceId) throws ThingsboardException { checkParameter(RESOURCE_ID, strResourceId); - try { - TbResourceId resourceId = new TbResourceId(toUUID(strResourceId)); - TbResource tbResource = checkResourceId(resourceId, Operation.DELETE); - resourceService.deleteResource(getTenantId(), resourceId); - tbClusterService.onResourceDeleted(tbResource, null); - logEntityAction(resourceId, tbResource, null, ActionType.DELETED, null, strResourceId); - } catch (Exception e) { - logEntityAction(emptyId(EntityType.TB_RESOURCE), null, null, ActionType.DELETED, e, strResourceId); - throw handleException(e); - } + TbResourceId resourceId = new TbResourceId(toUUID(strResourceId)); + TbResource tbResource = checkResourceId(resourceId, Operation.DELETE); + tbResourceService.delete(tbResource, getCurrentUser()); } - } \ No newline at end of file diff --git a/application/src/main/java/org/thingsboard/server/service/resource/DefaultTbResourceService.java b/application/src/main/java/org/thingsboard/server/service/resource/DefaultTbResourceService.java index fab78afc70..811eaf1774 100644 --- a/application/src/main/java/org/thingsboard/server/service/resource/DefaultTbResourceService.java +++ b/application/src/main/java/org/thingsboard/server/service/resource/DefaultTbResourceService.java @@ -22,9 +22,11 @@ import org.eclipse.leshan.core.model.DefaultDDFFileValidator; import org.eclipse.leshan.core.model.InvalidDDFFileException; import org.eclipse.leshan.core.model.ObjectModel; import org.springframework.stereotype.Service; +import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.ResourceType; import org.thingsboard.server.common.data.TbResource; import org.thingsboard.server.common.data.TbResourceInfo; +import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.TbResourceId; @@ -36,6 +38,8 @@ import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.resource.ResourceService; +import org.thingsboard.server.service.entitiy.AbstractTbEntityService; +import org.thingsboard.server.service.security.model.SecurityUser; import java.io.ByteArrayInputStream; import java.io.IOException; @@ -53,7 +57,7 @@ import static org.thingsboard.server.dao.service.Validator.validateId; @Slf4j @Service -public class DefaultTbResourceService implements TbResourceService { +public class DefaultTbResourceService extends AbstractTbEntityService implements TbResourceService { private final ResourceService resourceService; private final DDFFileParser ddfFileParser; @@ -215,4 +219,38 @@ public class DefaultTbResourceService implements TbResourceService { return null; } } + + @Override + public TbResource save(TbResource tbResource, SecurityUser user) throws ThingsboardException { + ActionType actionType = tbResource.getId() == null ? ActionType.ADDED : ActionType.UPDATED; + TenantId tenantId = tbResource.getTenantId(); + try { + + TbResource savedResource = checkNotNull(resourceService.saveResource(tbResource)); + tbClusterService.onResourceChange(savedResource, null); + notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, null, savedResource.getId(), + savedResource, user, actionType, false, null); + return savedResource; + } catch (Exception e) { + notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, null, emptyId(EntityType.TB_RESOURCE), + tbResource, user, actionType, false, e); + throw handleException(e); + } + } + + @Override + public void delete(TbResource tbResource, SecurityUser user) throws ThingsboardException { + TbResourceId resourceId = tbResource.getId(); + TenantId tenantId = tbResource.getTenantId(); + try { + resourceService.deleteResource(tenantId, resourceId); + tbClusterService.onResourceDeleted(tbResource, null); + notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, null, resourceId, tbResource, user, ActionType.DELETED, + false, null, resourceId.toString()); + } catch (Exception e) { + notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, null, emptyId(EntityType.TB_RESOURCE), null, user, ActionType.DELETED, + false, e, resourceId.toString()); + throw handleException(e); + } + } } diff --git a/application/src/main/java/org/thingsboard/server/service/resource/TbResourceService.java b/application/src/main/java/org/thingsboard/server/service/resource/TbResourceService.java index bfa04d4cd6..06120f3b49 100644 --- a/application/src/main/java/org/thingsboard/server/service/resource/TbResourceService.java +++ b/application/src/main/java/org/thingsboard/server/service/resource/TbResourceService.java @@ -24,10 +24,11 @@ import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.lwm2m.LwM2mObject; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; +import org.thingsboard.server.service.entitiy.SimpleTbEntityService; import java.util.List; -public interface TbResourceService { +public interface TbResourceService extends SimpleTbEntityService { TbResource saveResource(TbResource resource) throws ThingsboardException; From 6b3a1db18e7708d6e7e8383b270acb7286400978 Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Tue, 31 May 2022 20:02:31 +0300 Subject: [PATCH 733/798] refactoring: 10_UserController --- .../server/controller/UserController.java | 83 +++----------- .../entitiy/AbstractTbEntityService.java | 3 + .../entitiy/user/DefaultUserService.java | 101 ++++++++++++++++++ .../service/entitiy/user/TbUserService.java | 28 +++++ 4 files changed, 146 insertions(+), 69 deletions(-) create mode 100644 application/src/main/java/org/thingsboard/server/service/entitiy/user/DefaultUserService.java create mode 100644 application/src/main/java/org/thingsboard/server/service/entitiy/user/TbUserService.java diff --git a/application/src/main/java/org/thingsboard/server/controller/UserController.java b/application/src/main/java/org/thingsboard/server/controller/UserController.java index 250f5392b5..626f326ae6 100644 --- a/application/src/main/java/org/thingsboard/server/controller/UserController.java +++ b/application/src/main/java/org/thingsboard/server/controller/UserController.java @@ -33,14 +33,10 @@ import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestController; import org.thingsboard.rule.engine.api.MailService; -import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.User; -import org.thingsboard.server.common.data.audit.ActionType; -import org.thingsboard.server.common.data.edge.EdgeEventActionType; import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; 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.TenantId; import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.page.PageData; @@ -50,6 +46,7 @@ import org.thingsboard.server.common.data.security.UserCredentials; import org.thingsboard.server.common.data.security.event.UserAuthDataChangedEvent; import org.thingsboard.server.common.data.security.model.JwtToken; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.entitiy.user.TbUserService; import org.thingsboard.server.service.security.auth.jwt.RefreshTokenRepository; import org.thingsboard.server.service.security.model.JwtTokenPair; import org.thingsboard.server.service.security.model.SecurityUser; @@ -60,7 +57,6 @@ import org.thingsboard.server.service.security.permission.Resource; import org.thingsboard.server.service.security.system.SystemSecurityService; import javax.servlet.http.HttpServletRequest; -import java.util.List; import static org.thingsboard.server.controller.ControllerConstants.CUSTOMER_ID; import static org.thingsboard.server.controller.ControllerConstants.CUSTOMER_ID_PARAM_DESCRIPTION; @@ -102,6 +98,7 @@ public class UserController extends BaseController { private final RefreshTokenRepository refreshTokenRepository; private final SystemSecurityService systemSecurityService; private final ApplicationEventPublisher eventPublisher; + private final TbUserService tbUserService; @ApiOperation(value = "Get User (getUserById)", notes = "Fetch the User object based on the provided User Id. " + @@ -188,47 +185,14 @@ public class UserController extends BaseController { @RequestBody User user, @ApiParam(value = "Send activation email (or use activation link)", defaultValue = "true") @RequestParam(required = false, defaultValue = "true") boolean sendActivationMail, HttpServletRequest request) throws ThingsboardException { - try { - - if (Authority.TENANT_ADMIN.equals(getCurrentUser().getAuthority())) { - user.setTenantId(getCurrentUser().getTenantId()); - } - - checkEntity(user.getId(), user, Resource.USER); - - boolean sendEmail = user.getId() == null && sendActivationMail; - User savedUser = checkNotNull(userService.saveUser(user)); - if (sendEmail) { - SecurityUser authUser = getCurrentUser(); - UserCredentials userCredentials = userService.findUserCredentialsByUserId(authUser.getTenantId(), savedUser.getId()); - String baseUrl = systemSecurityService.getBaseUrl(getTenantId(), getCurrentUser().getCustomerId(), request); - String activateUrl = String.format(ACTIVATE_URL_PATTERN, baseUrl, - userCredentials.getActivateToken()); - String email = savedUser.getEmail(); - try { - mailService.sendActivationEmail(activateUrl, email); - } catch (ThingsboardException e) { - userService.deleteUser(authUser.getTenantId(), savedUser.getId()); - throw e; - } - } - - logEntityAction(savedUser.getId(), savedUser, - savedUser.getCustomerId(), - user.getId() == null ? ActionType.ADDED : ActionType.UPDATED, null); - - sendEntityNotificationMsg(getTenantId(), savedUser.getId(), - user.getId() == null ? EdgeEventActionType.ADDED : EdgeEventActionType.UPDATED); - - return savedUser; - } catch (Exception e) { - - logEntityAction(emptyId(EntityType.USER), user, - null, user.getId() == null ? ActionType.ADDED : ActionType.UPDATED, e); - - throw handleException(e); + if (Authority.TENANT_ADMIN.equals(getCurrentUser().getAuthority())) { + user.setTenantId(getCurrentUser().getTenantId()); } - } + checkEntity(user.getId(), user, Resource.USER); + user.setTenantId(getTenantId()); + user.setCustomerId(getCurrentUser().getCustomerId()); + return tbUserService.save(user, sendActivationMail, request, getCurrentUser()); + } @ApiOperation(value = "Send or re-send the activation email", notes = "Force send the activation email to the user. Useful to resend the email if user has accidentally deleted it. " + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH) @@ -298,31 +262,12 @@ public class UserController extends BaseController { @ApiParam(value = USER_ID_PARAM_DESCRIPTION) @PathVariable(USER_ID) String strUserId) throws ThingsboardException { checkParameter(USER_ID, strUserId); - try { - UserId userId = new UserId(toUUID(strUserId)); - User user = checkUserId(userId, Operation.DELETE); - - if (user.getAuthority() == Authority.SYS_ADMIN && getCurrentUser().getId().equals(userId)) { - throw new ThingsboardException("Sysadmin is not allowed to delete himself", ThingsboardErrorCode.PERMISSION_DENIED); - } - - List relatedEdgeIds = findRelatedEdgeIds(getTenantId(), userId); - - userService.deleteUser(getCurrentUser().getTenantId(), userId); - - logEntityAction(userId, user, - user.getCustomerId(), - ActionType.DELETED, null, strUserId); - - sendDeleteNotificationMsg(getTenantId(), userId, relatedEdgeIds); - - } catch (Exception e) { - logEntityAction(emptyId(EntityType.USER), - null, - null, - ActionType.DELETED, e, strUserId); - throw handleException(e); + UserId userId = new UserId(toUUID(strUserId)); + User user = checkUserId(userId, Operation.DELETE); + if (user.getAuthority() == Authority.SYS_ADMIN && getCurrentUser().getId().equals(userId)) { + throw new ThingsboardException("Sysadmin is not allowed to delete himself", ThingsboardErrorCode.PERMISSION_DENIED); } + tbUserService.delete(user, getCurrentUser()); } @ApiOperation(value = "Get Users (getUsers)", diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/AbstractTbEntityService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/AbstractTbEntityService.java index c1a0ff2a24..7694f3f8f4 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/AbstractTbEntityService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/AbstractTbEntityService.java @@ -59,6 +59,7 @@ import org.thingsboard.server.dao.relation.RelationService; import org.thingsboard.server.dao.rule.RuleChainService; import org.thingsboard.server.dao.tenant.TbTenantProfileCache; import org.thingsboard.server.dao.tenant.TenantService; +import org.thingsboard.server.dao.user.UserService; import org.thingsboard.server.service.action.EntityActionService; import org.thingsboard.server.service.edge.EdgeNotificationService; import org.thingsboard.server.service.executors.DbCallbackExecutorService; @@ -141,6 +142,8 @@ public abstract class AbstractTbEntityService { protected OtaPackageService otaPackageService; @Autowired protected InstallScripts installScripts; + @Autowired + protected UserService userService; protected ListenableFuture removeAlarmsByEntityId(TenantId tenantId, EntityId entityId) { ListenableFuture> alarmsFuture = diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/user/DefaultUserService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/user/DefaultUserService.java new file mode 100644 index 0000000000..4443fed709 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/user/DefaultUserService.java @@ -0,0 +1,101 @@ +/** + * Copyright © 2016-2022 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.service.entitiy.user; + +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.thingsboard.rule.engine.api.MailService; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.User; +import org.thingsboard.server.common.data.audit.ActionType; +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.TenantId; +import org.thingsboard.server.common.data.id.UserId; +import org.thingsboard.server.common.data.security.UserCredentials; +import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.entitiy.AbstractTbEntityService; +import org.thingsboard.server.service.security.model.SecurityUser; +import org.thingsboard.server.service.security.system.SystemSecurityService; + +import javax.servlet.http.HttpServletRequest; +import java.util.List; + +import static org.thingsboard.server.controller.UserController.ACTIVATE_URL_PATTERN; + +@Service +@TbCoreComponent +@AllArgsConstructor +@Slf4j +public class DefaultUserService extends AbstractTbEntityService implements TbUserService { + + private final MailService mailService; + private final SystemSecurityService systemSecurityService; + + @Override + public User save(User tbUser, boolean sendActivationMail, + HttpServletRequest request, SecurityUser user) throws ThingsboardException { + ActionType actionType = tbUser.getId() == null ? ActionType.ADDED : ActionType.UPDATED; + TenantId tenantId = tbUser.getTenantId(); + CustomerId customerId = tbUser.getCustomerId(); + try { + boolean sendEmail = tbUser.getId() == null && sendActivationMail; + User savedUser = checkNotNull(userService.saveUser(tbUser)); + if (sendEmail) { + SecurityUser authUser = user; + UserCredentials userCredentials = userService.findUserCredentialsByUserId(authUser.getTenantId(), savedUser.getId()); + String baseUrl = systemSecurityService.getBaseUrl(tenantId, customerId, request); + String activateUrl = String.format(ACTIVATE_URL_PATTERN, baseUrl, + userCredentials.getActivateToken()); + String email = savedUser.getEmail(); + try { + mailService.sendActivationEmail(activateUrl, email); + } catch (ThingsboardException e) { + userService.deleteUser(authUser.getTenantId(), savedUser.getId()); + throw e; + } + } + notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, customerId, savedUser.getId(), + savedUser, user, actionType, true, null); + return savedUser; + } catch (Exception e) { + notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, null, emptyId(EntityType.USER), + tbUser, user, actionType, false, e); + throw handleException(e); + } + } + + @Override + public void delete(User tbUser, SecurityUser user) throws ThingsboardException { + TenantId tenantId = tbUser.getTenantId(); + UserId userId = tbUser.getId(); + try { + + + List relatedEdgeIds = findRelatedEdgeIds(tenantId, userId); + + userService.deleteUser(tenantId, userId); + notificationEntityService.notifyDeleteEntity(tenantId, userId, tbUser, tbUser.getCustomerId(), + ActionType.DELETED, relatedEdgeIds, user, userId.toString()); + } catch (Exception e) { + notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, null, emptyId(EntityType.USER), + null, user, ActionType.DELETED, false, e); + throw handleException(e); + } + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/user/TbUserService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/user/TbUserService.java new file mode 100644 index 0000000000..e6e0c0e658 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/user/TbUserService.java @@ -0,0 +1,28 @@ +/** + * Copyright © 2016-2022 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.service.entitiy.user; + +import org.thingsboard.server.common.data.User; +import org.thingsboard.server.common.data.exception.ThingsboardException; +import org.thingsboard.server.service.security.model.SecurityUser; + +import javax.servlet.http.HttpServletRequest; + +public interface TbUserService { + User save(User tbUser, boolean sendActivationMail, HttpServletRequest request, SecurityUser user) throws ThingsboardException; + + void delete (User tbUser, SecurityUser user) throws ThingsboardException; +} From cc8cbc5005749aa7de2ac574462c5487a078727f Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Tue, 31 May 2022 22:47:50 +0300 Subject: [PATCH 734/798] refactoring: 10_UserController: fix bug testGateWayClaimDevice... --- .../server/controller/UserController.java | 6 ++---- .../entitiy/user/DefaultUserService.java | 18 ++++++------------ .../service/entitiy/user/TbUserService.java | 6 ++++-- 3 files changed, 12 insertions(+), 18 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/controller/UserController.java b/application/src/main/java/org/thingsboard/server/controller/UserController.java index 626f326ae6..d6fda64d31 100644 --- a/application/src/main/java/org/thingsboard/server/controller/UserController.java +++ b/application/src/main/java/org/thingsboard/server/controller/UserController.java @@ -189,9 +189,7 @@ public class UserController extends BaseController { user.setTenantId(getCurrentUser().getTenantId()); } checkEntity(user.getId(), user, Resource.USER); - user.setTenantId(getTenantId()); - user.setCustomerId(getCurrentUser().getCustomerId()); - return tbUserService.save(user, sendActivationMail, request, getCurrentUser()); + return tbUserService.save(getTenantId(), getCurrentUser().getCustomerId(), user, sendActivationMail, request, getCurrentUser()); } @ApiOperation(value = "Send or re-send the activation email", @@ -267,7 +265,7 @@ public class UserController extends BaseController { if (user.getAuthority() == Authority.SYS_ADMIN && getCurrentUser().getId().equals(userId)) { throw new ThingsboardException("Sysadmin is not allowed to delete himself", ThingsboardErrorCode.PERMISSION_DENIED); } - tbUserService.delete(user, getCurrentUser()); + tbUserService.delete(getTenantId(), getCurrentUser().getCustomerId(), user, getCurrentUser()); } @ApiOperation(value = "Get Users (getUsers)", diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/user/DefaultUserService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/user/DefaultUserService.java index 4443fed709..592496e6f8 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/user/DefaultUserService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/user/DefaultUserService.java @@ -48,17 +48,14 @@ public class DefaultUserService extends AbstractTbEntityService implements TbUse private final SystemSecurityService systemSecurityService; @Override - public User save(User tbUser, boolean sendActivationMail, + public User save(TenantId tenantId, CustomerId customerId, User tbUser, boolean sendActivationMail, HttpServletRequest request, SecurityUser user) throws ThingsboardException { ActionType actionType = tbUser.getId() == null ? ActionType.ADDED : ActionType.UPDATED; - TenantId tenantId = tbUser.getTenantId(); - CustomerId customerId = tbUser.getCustomerId(); try { boolean sendEmail = tbUser.getId() == null && sendActivationMail; User savedUser = checkNotNull(userService.saveUser(tbUser)); if (sendEmail) { - SecurityUser authUser = user; - UserCredentials userCredentials = userService.findUserCredentialsByUserId(authUser.getTenantId(), savedUser.getId()); + UserCredentials userCredentials = userService.findUserCredentialsByUserId(tenantId, savedUser.getId()); String baseUrl = systemSecurityService.getBaseUrl(tenantId, customerId, request); String activateUrl = String.format(ACTIVATE_URL_PATTERN, baseUrl, userCredentials.getActivateToken()); @@ -66,7 +63,7 @@ public class DefaultUserService extends AbstractTbEntityService implements TbUse try { mailService.sendActivationEmail(activateUrl, email); } catch (ThingsboardException e) { - userService.deleteUser(authUser.getTenantId(), savedUser.getId()); + userService.deleteUser(tenantId, savedUser.getId()); throw e; } } @@ -81,20 +78,17 @@ public class DefaultUserService extends AbstractTbEntityService implements TbUse } @Override - public void delete(User tbUser, SecurityUser user) throws ThingsboardException { - TenantId tenantId = tbUser.getTenantId(); + public void delete(TenantId tenantId, CustomerId customerId, User tbUser, SecurityUser user) throws ThingsboardException { UserId userId = tbUser.getId(); try { - - List relatedEdgeIds = findRelatedEdgeIds(tenantId, userId); userService.deleteUser(tenantId, userId); - notificationEntityService.notifyDeleteEntity(tenantId, userId, tbUser, tbUser.getCustomerId(), + notificationEntityService.notifyDeleteEntity(tenantId, userId, tbUser, customerId, ActionType.DELETED, relatedEdgeIds, user, userId.toString()); } catch (Exception e) { notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, null, emptyId(EntityType.USER), - null, user, ActionType.DELETED, false, e); + null, user, ActionType.DELETED, false, e, userId.toString()); throw handleException(e); } } diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/user/TbUserService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/user/TbUserService.java index e6e0c0e658..177aad17cc 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/user/TbUserService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/user/TbUserService.java @@ -17,12 +17,14 @@ package org.thingsboard.server.service.entitiy.user; import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.exception.ThingsboardException; +import org.thingsboard.server.common.data.id.CustomerId; +import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.service.security.model.SecurityUser; import javax.servlet.http.HttpServletRequest; public interface TbUserService { - User save(User tbUser, boolean sendActivationMail, HttpServletRequest request, SecurityUser user) throws ThingsboardException; + User save(TenantId tenantId, CustomerId customerId, User tbUser, boolean sendActivationMail, HttpServletRequest request, SecurityUser user) throws ThingsboardException; - void delete (User tbUser, SecurityUser user) throws ThingsboardException; + void delete (TenantId tenantId, CustomerId customerId, User tbUser, SecurityUser user) throws ThingsboardException; } From 38e628643047227484aeabcf90061c4627cb4cb6 Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Wed, 1 Jun 2022 00:32:16 +0300 Subject: [PATCH 735/798] refactoring: 09_TbResourceController: fix bug test save resource --- .../controller/TbResourceController.java | 4 +- .../entitiy/AbstractTbEntityService.java | 3 + .../DefaultTbResourceNotifyService.java | 71 +++++++++++++++++++ .../resource/TbResourceNotifyService.java | 22 ++++++ .../resource/DefaultTbResourceService.java | 40 +---------- .../service/resource/TbResourceService.java | 3 +- 6 files changed, 100 insertions(+), 43 deletions(-) create mode 100644 application/src/main/java/org/thingsboard/server/service/entitiy/resource/DefaultTbResourceNotifyService.java create mode 100644 application/src/main/java/org/thingsboard/server/service/entitiy/resource/TbResourceNotifyService.java diff --git a/application/src/main/java/org/thingsboard/server/controller/TbResourceController.java b/application/src/main/java/org/thingsboard/server/controller/TbResourceController.java index 120449c137..21d7eadb69 100644 --- a/application/src/main/java/org/thingsboard/server/controller/TbResourceController.java +++ b/application/src/main/java/org/thingsboard/server/controller/TbResourceController.java @@ -40,7 +40,7 @@ import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.queue.util.TbCoreComponent; -import org.thingsboard.server.service.resource.TbResourceService; +import org.thingsboard.server.service.entitiy.resource.TbResourceNotifyService; import org.thingsboard.server.service.security.permission.Operation; import org.thingsboard.server.service.security.permission.Resource; @@ -71,7 +71,7 @@ import static org.thingsboard.server.controller.ControllerConstants.UUID_WIKI_LI @RequiredArgsConstructor public class TbResourceController extends BaseController { - private final TbResourceService tbResourceService; + private final TbResourceNotifyService tbResourceService; public static final String RESOURCE_ID = "resourceId"; diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/AbstractTbEntityService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/AbstractTbEntityService.java index 7694f3f8f4..f3e33076c7 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/AbstractTbEntityService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/AbstractTbEntityService.java @@ -65,6 +65,7 @@ import org.thingsboard.server.service.edge.EdgeNotificationService; import org.thingsboard.server.service.executors.DbCallbackExecutorService; import org.thingsboard.server.service.install.InstallScripts; import org.thingsboard.server.service.ota.OtaPackageStateService; +import org.thingsboard.server.service.resource.TbResourceService; import org.thingsboard.server.service.rule.TbRuleChainService; import org.thingsboard.server.service.security.permission.AccessControlService; import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService; @@ -144,6 +145,8 @@ public abstract class AbstractTbEntityService { protected InstallScripts installScripts; @Autowired protected UserService userService; + @Autowired + protected TbResourceService resourceService; protected ListenableFuture removeAlarmsByEntityId(TenantId tenantId, EntityId entityId) { ListenableFuture> alarmsFuture = diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/resource/DefaultTbResourceNotifyService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/resource/DefaultTbResourceNotifyService.java new file mode 100644 index 0000000000..beb1707214 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/resource/DefaultTbResourceNotifyService.java @@ -0,0 +1,71 @@ +/** + * Copyright © 2016-2022 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.service.entitiy.resource; + + +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.TbResource; +import org.thingsboard.server.common.data.audit.ActionType; +import org.thingsboard.server.common.data.exception.ThingsboardException; +import org.thingsboard.server.common.data.id.TbResourceId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.entitiy.AbstractTbEntityService; +import org.thingsboard.server.service.security.model.SecurityUser; + +@Service +@TbCoreComponent +@AllArgsConstructor +@Slf4j +public class DefaultTbResourceNotifyService extends AbstractTbEntityService implements TbResourceNotifyService { + + @Override + public TbResource save(TbResource tbResource, SecurityUser user) throws ThingsboardException { + ActionType actionType = tbResource.getId() == null ? ActionType.ADDED : ActionType.UPDATED; + TenantId tenantId = tbResource.getTenantId(); + try { + + TbResource savedResource = checkNotNull(resourceService.saveResource(tbResource)); + tbClusterService.onResourceChange(savedResource, null); + notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, null, savedResource.getId(), + savedResource, user, actionType, false, null); + return savedResource; + } catch (Exception e) { + notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, null, emptyId(EntityType.TB_RESOURCE), + tbResource, user, actionType, false, e); + throw handleException(e); + } + } + + @Override + public void delete(TbResource tbResource, SecurityUser user) throws ThingsboardException { + TbResourceId resourceId = tbResource.getId(); + TenantId tenantId = tbResource.getTenantId(); + try { + resourceService.deleteResource(tenantId, resourceId); + tbClusterService.onResourceDeleted(tbResource, null); + notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, null, resourceId, tbResource, user, ActionType.DELETED, + false, null, resourceId.toString()); + } catch (Exception e) { + notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, null, emptyId(EntityType.TB_RESOURCE), null, user, ActionType.DELETED, + false, e, resourceId.toString()); + throw handleException(e); + } + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/resource/TbResourceNotifyService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/resource/TbResourceNotifyService.java new file mode 100644 index 0000000000..1605d745c2 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/resource/TbResourceNotifyService.java @@ -0,0 +1,22 @@ +/** + * Copyright © 2016-2022 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.service.entitiy.resource; + +import org.thingsboard.server.common.data.TbResource; +import org.thingsboard.server.service.entitiy.SimpleTbEntityService; + +public interface TbResourceNotifyService extends SimpleTbEntityService { +} diff --git a/application/src/main/java/org/thingsboard/server/service/resource/DefaultTbResourceService.java b/application/src/main/java/org/thingsboard/server/service/resource/DefaultTbResourceService.java index 811eaf1774..fab78afc70 100644 --- a/application/src/main/java/org/thingsboard/server/service/resource/DefaultTbResourceService.java +++ b/application/src/main/java/org/thingsboard/server/service/resource/DefaultTbResourceService.java @@ -22,11 +22,9 @@ import org.eclipse.leshan.core.model.DefaultDDFFileValidator; import org.eclipse.leshan.core.model.InvalidDDFFileException; import org.eclipse.leshan.core.model.ObjectModel; import org.springframework.stereotype.Service; -import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.ResourceType; import org.thingsboard.server.common.data.TbResource; import org.thingsboard.server.common.data.TbResourceInfo; -import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.TbResourceId; @@ -38,8 +36,6 @@ import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.resource.ResourceService; -import org.thingsboard.server.service.entitiy.AbstractTbEntityService; -import org.thingsboard.server.service.security.model.SecurityUser; import java.io.ByteArrayInputStream; import java.io.IOException; @@ -57,7 +53,7 @@ import static org.thingsboard.server.dao.service.Validator.validateId; @Slf4j @Service -public class DefaultTbResourceService extends AbstractTbEntityService implements TbResourceService { +public class DefaultTbResourceService implements TbResourceService { private final ResourceService resourceService; private final DDFFileParser ddfFileParser; @@ -219,38 +215,4 @@ public class DefaultTbResourceService extends AbstractTbEntityService implements return null; } } - - @Override - public TbResource save(TbResource tbResource, SecurityUser user) throws ThingsboardException { - ActionType actionType = tbResource.getId() == null ? ActionType.ADDED : ActionType.UPDATED; - TenantId tenantId = tbResource.getTenantId(); - try { - - TbResource savedResource = checkNotNull(resourceService.saveResource(tbResource)); - tbClusterService.onResourceChange(savedResource, null); - notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, null, savedResource.getId(), - savedResource, user, actionType, false, null); - return savedResource; - } catch (Exception e) { - notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, null, emptyId(EntityType.TB_RESOURCE), - tbResource, user, actionType, false, e); - throw handleException(e); - } - } - - @Override - public void delete(TbResource tbResource, SecurityUser user) throws ThingsboardException { - TbResourceId resourceId = tbResource.getId(); - TenantId tenantId = tbResource.getTenantId(); - try { - resourceService.deleteResource(tenantId, resourceId); - tbClusterService.onResourceDeleted(tbResource, null); - notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, null, resourceId, tbResource, user, ActionType.DELETED, - false, null, resourceId.toString()); - } catch (Exception e) { - notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, null, emptyId(EntityType.TB_RESOURCE), null, user, ActionType.DELETED, - false, e, resourceId.toString()); - throw handleException(e); - } - } } diff --git a/application/src/main/java/org/thingsboard/server/service/resource/TbResourceService.java b/application/src/main/java/org/thingsboard/server/service/resource/TbResourceService.java index 06120f3b49..bfa04d4cd6 100644 --- a/application/src/main/java/org/thingsboard/server/service/resource/TbResourceService.java +++ b/application/src/main/java/org/thingsboard/server/service/resource/TbResourceService.java @@ -24,11 +24,10 @@ import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.lwm2m.LwM2mObject; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; -import org.thingsboard.server.service.entitiy.SimpleTbEntityService; import java.util.List; -public interface TbResourceService extends SimpleTbEntityService { +public interface TbResourceService { TbResource saveResource(TbResource resource) throws ThingsboardException; From b129b96a8cdecdfd2d38d18c651af49952bd248a Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Wed, 1 Jun 2022 07:05:09 +0300 Subject: [PATCH 736/798] refactoring: 11_WidgetsBundleController --- .../controller/WidgetsBundleController.java | 30 ++++-------- .../entitiy/AbstractTbEntityService.java | 3 ++ .../DefaultTbNotificationEntityService.java | 4 +- .../entitiy/TbNotificationEntityService.java | 2 +- .../DefaultWidgetsBundleService.java | 47 +++++++++++++++++++ .../widgetsBundle/TbWidgetsBundleService.java | 22 +++++++++ .../rule/DefaultTbRuleChainService.java | 4 +- 7 files changed, 87 insertions(+), 25 deletions(-) create mode 100644 application/src/main/java/org/thingsboard/server/service/entitiy/widgetsBundle/DefaultWidgetsBundleService.java create mode 100644 application/src/main/java/org/thingsboard/server/service/entitiy/widgetsBundle/TbWidgetsBundleService.java diff --git a/application/src/main/java/org/thingsboard/server/controller/WidgetsBundleController.java b/application/src/main/java/org/thingsboard/server/controller/WidgetsBundleController.java index 7ec3e2e9aa..4a69b203ed 100644 --- a/application/src/main/java/org/thingsboard/server/controller/WidgetsBundleController.java +++ b/application/src/main/java/org/thingsboard/server/controller/WidgetsBundleController.java @@ -17,6 +17,7 @@ package org.thingsboard.server.controller; import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiParam; +import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.PathVariable; @@ -27,7 +28,6 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestController; -import org.thingsboard.server.common.data.edge.EdgeEventActionType; import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.WidgetsBundleId; @@ -36,6 +36,7 @@ import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.common.data.widget.WidgetsBundle; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.entitiy.widgetsBundle.TbWidgetsBundleService; import org.thingsboard.server.service.security.permission.Operation; import org.thingsboard.server.service.security.permission.Resource; @@ -57,8 +58,11 @@ import static org.thingsboard.server.controller.ControllerConstants.WIDGET_BUNDL @RestController @TbCoreComponent @RequestMapping("/api") +@RequiredArgsConstructor public class WidgetsBundleController extends BaseController { + private final TbWidgetsBundleService tbWidgetsBundleService; + private static final String WIDGET_BUNDLE_DESCRIPTION = "Widget Bundle represents a group(bundle) of widgets. Widgets are grouped into bundle by type or use case. "; @ApiOperation(value = "Get Widget Bundle (getWidgetsBundleById)", @@ -93,7 +97,7 @@ public class WidgetsBundleController extends BaseController { public WidgetsBundle saveWidgetsBundle( @ApiParam(value = "A JSON value representing the Widget Bundle.", required = true) @RequestBody WidgetsBundle widgetsBundle) throws ThingsboardException { - try { + if (Authority.SYS_ADMIN.equals(getCurrentUser().getAuthority())) { widgetsBundle.setTenantId(TenantId.SYS_TENANT_ID); } else { @@ -101,15 +105,8 @@ public class WidgetsBundleController extends BaseController { } checkEntity(widgetsBundle.getId(), widgetsBundle, Resource.WIDGETS_BUNDLE); - WidgetsBundle savedWidgetsBundle = widgetsBundleService.saveWidgetsBundle(widgetsBundle); - - sendEntityNotificationMsg(getTenantId(), savedWidgetsBundle.getId(), - widgetsBundle.getId() == null ? EdgeEventActionType.ADDED : EdgeEventActionType.UPDATED); - return checkNotNull(savedWidgetsBundle); - } catch (Exception e) { - throw handleException(e); - } + return tbWidgetsBundleService.save(widgetsBundle, getCurrentUser()); } @ApiOperation(value = "Delete widgets bundle (deleteWidgetsBundle)", @@ -121,16 +118,9 @@ public class WidgetsBundleController extends BaseController { @ApiParam(value = WIDGET_BUNDLE_ID_PARAM_DESCRIPTION, required = true) @PathVariable("widgetsBundleId") String strWidgetsBundleId) throws ThingsboardException { checkParameter("widgetsBundleId", strWidgetsBundleId); - try { - WidgetsBundleId widgetsBundleId = new WidgetsBundleId(toUUID(strWidgetsBundleId)); - checkWidgetsBundleId(widgetsBundleId, Operation.DELETE); - widgetsBundleService.deleteWidgetsBundle(getTenantId(), widgetsBundleId); - - sendEntityNotificationMsg(getTenantId(), widgetsBundleId, EdgeEventActionType.DELETED); - - } catch (Exception e) { - throw handleException(e); - } + WidgetsBundleId widgetsBundleId = new WidgetsBundleId(toUUID(strWidgetsBundleId)); + WidgetsBundle widgetsBundle = checkWidgetsBundleId(widgetsBundleId, Operation.DELETE); + tbWidgetsBundleService.delete(widgetsBundle, getCurrentUser()); } @ApiOperation(value = "Get Widget Bundles (getWidgetsBundles)", diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/AbstractTbEntityService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/AbstractTbEntityService.java index f3e33076c7..aeba451b9b 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/AbstractTbEntityService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/AbstractTbEntityService.java @@ -60,6 +60,7 @@ import org.thingsboard.server.dao.rule.RuleChainService; import org.thingsboard.server.dao.tenant.TbTenantProfileCache; import org.thingsboard.server.dao.tenant.TenantService; import org.thingsboard.server.dao.user.UserService; +import org.thingsboard.server.dao.widget.WidgetsBundleService; import org.thingsboard.server.service.action.EntityActionService; import org.thingsboard.server.service.edge.EdgeNotificationService; import org.thingsboard.server.service.executors.DbCallbackExecutorService; @@ -147,6 +148,8 @@ public abstract class AbstractTbEntityService { protected UserService userService; @Autowired protected TbResourceService resourceService; + @Autowired + protected WidgetsBundleService widgetsBundleService; protected ListenableFuture removeAlarmsByEntityId(TenantId tenantId, EntityId entityId) { ListenableFuture> alarmsFuture = diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/DefaultTbNotificationEntityService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/DefaultTbNotificationEntityService.java index a556caa16c..d61459a294 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/DefaultTbNotificationEntityService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/DefaultTbNotificationEntityService.java @@ -100,8 +100,8 @@ public class DefaultTbNotificationEntityService implements TbNotificationEntityS } @Override - public void notifySendMsgToEdgeService(TenantId tenantId, RuleChain ruleChain, EdgeEventActionType edgeEventActionType) { - sendEntityNotificationMsg(tenantId, ruleChain.getId(), edgeEventActionType); + public void notifySendMsgToEdgeService(TenantId tenantId, I entityId, EdgeEventActionType edgeEventActionType) { + sendEntityNotificationMsg(tenantId, entityId, edgeEventActionType); } @Override diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/TbNotificationEntityService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/TbNotificationEntityService.java index 009ea26676..c1131761dc 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/TbNotificationEntityService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/TbNotificationEntityService.java @@ -57,7 +57,7 @@ public interface TbNotificationEntityService { void notifyDeleteRuleChain(TenantId tenantId, RuleChain ruleChain, List relatedEdgeIds, SecurityUser user); - void notifySendMsgToEdgeService(TenantId tenantId, RuleChain ruleChain, EdgeEventActionType edgeEventActionType); + void notifySendMsgToEdgeService(TenantId tenantId, I entityId, EdgeEventActionType edgeEventActionType); void notifyAssignOrUnassignEntityToCustomer(TenantId tenantId, I entityId, CustomerId customerId, E entity, diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/widgetsBundle/DefaultWidgetsBundleService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/widgetsBundle/DefaultWidgetsBundleService.java new file mode 100644 index 0000000000..5d393dc958 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/widgetsBundle/DefaultWidgetsBundleService.java @@ -0,0 +1,47 @@ +/** + * Copyright © 2016-2022 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.service.entitiy.widgetsBundle; + +import org.thingsboard.server.common.data.edge.EdgeEventActionType; +import org.thingsboard.server.common.data.exception.ThingsboardException; +import org.thingsboard.server.common.data.widget.WidgetsBundle; +import org.thingsboard.server.service.entitiy.AbstractTbEntityService; +import org.thingsboard.server.service.security.model.SecurityUser; + +public class DefaultWidgetsBundleService extends AbstractTbEntityService implements TbWidgetsBundleService{ + @Override + public WidgetsBundle save(WidgetsBundle widgetsBundle, SecurityUser user) throws ThingsboardException { + try { + WidgetsBundle savedWidgetsBundle = checkNotNull(widgetsBundleService.saveWidgetsBundle(widgetsBundle)); + notificationEntityService.notifySendMsgToEdgeService(widgetsBundle.getTenantId(), savedWidgetsBundle.getId(), + widgetsBundle.getId() == null ? EdgeEventActionType.ADDED : EdgeEventActionType.UPDATED); + return savedWidgetsBundle; + } catch (Exception e) { + throw handleException(e); + } + } + + @Override + public void delete(WidgetsBundle widgetsBundle, SecurityUser user) throws ThingsboardException { + try { + widgetsBundleService.deleteWidgetsBundle(widgetsBundle.getTenantId(), widgetsBundle.getId()); + notificationEntityService.notifySendMsgToEdgeService(widgetsBundle.getTenantId(), widgetsBundle.getId(), + EdgeEventActionType.DELETED); + } catch (Exception e) { + throw handleException(e); + } + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/widgetsBundle/TbWidgetsBundleService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/widgetsBundle/TbWidgetsBundleService.java new file mode 100644 index 0000000000..7672db1a71 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/widgetsBundle/TbWidgetsBundleService.java @@ -0,0 +1,22 @@ +/** + * Copyright © 2016-2022 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.service.entitiy.widgetsBundle; + +import org.thingsboard.server.common.data.widget.WidgetsBundle; +import org.thingsboard.server.service.entitiy.SimpleTbEntityService; + +public interface TbWidgetsBundleService extends SimpleTbEntityService { +} diff --git a/application/src/main/java/org/thingsboard/server/service/rule/DefaultTbRuleChainService.java b/application/src/main/java/org/thingsboard/server/service/rule/DefaultTbRuleChainService.java index 97720cad4a..1ef64d4f89 100644 --- a/application/src/main/java/org/thingsboard/server/service/rule/DefaultTbRuleChainService.java +++ b/application/src/main/java/org/thingsboard/server/service/rule/DefaultTbRuleChainService.java @@ -294,12 +294,12 @@ public class DefaultTbRuleChainService extends AbstractTbEntityService implement ruleChain, user, ActionType.UPDATED, false, null, ruleChainMetaData); if (RuleChainType.EDGE.equals(ruleChain.getType())) { - notificationEntityService.notifySendMsgToEdgeService(tenantId, ruleChain, EdgeEventActionType.UPDATED); + notificationEntityService.notifySendMsgToEdgeService(tenantId, ruleChain.getId(), EdgeEventActionType.UPDATED); } updatedRuleChains.forEach(updatedRuleChain -> { if (RuleChainType.EDGE.equals(ruleChain.getType())) { - notificationEntityService.notifySendMsgToEdgeService(tenantId, updatedRuleChain, EdgeEventActionType.UPDATED); + notificationEntityService.notifySendMsgToEdgeService(tenantId, updatedRuleChain.getId(), EdgeEventActionType.UPDATED); } else { try { RuleChainMetaData updatedRuleChainMetaData = checkNotNull(ruleChainService.loadRuleChainMetaData(tenantId, updatedRuleChain.getId())); From fd6ead6c481c4b7b28dbb24435467e3632fed163 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Wed, 1 Jun 2022 10:02:23 +0300 Subject: [PATCH 737/798] Fixed test --- .../thingsboard/server/controller/TwoFactorAuthConfigTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/src/test/java/org/thingsboard/server/controller/TwoFactorAuthConfigTest.java b/application/src/test/java/org/thingsboard/server/controller/TwoFactorAuthConfigTest.java index ec5b128efd..ee93cadaa4 100644 --- a/application/src/test/java/org/thingsboard/server/controller/TwoFactorAuthConfigTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/TwoFactorAuthConfigTest.java @@ -124,7 +124,7 @@ public abstract class TwoFactorAuthConfigTest extends AbstractControllerTest { assertThat(errorMessage).contains( "verification code check rate limit configuration is invalid", "maximum number of verification failure before user lockout must be positive", - "total amount of time allotted for verification must be greater than 0" + "total amount of time allotted for verification must be greater than or equal 60" ); } From 9fd78e595caa8071d06f9fb6c8a10edd59201de4 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Wed, 1 Jun 2022 10:43:58 +0300 Subject: [PATCH 738/798] =?UTF-8?q?Fixed=20test=D1=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../thingsboard/server/controller/TwoFactorAuthTest.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/application/src/test/java/org/thingsboard/server/controller/TwoFactorAuthTest.java b/application/src/test/java/org/thingsboard/server/controller/TwoFactorAuthTest.java index 2f7ee93cee..b825571694 100644 --- a/application/src/test/java/org/thingsboard/server/controller/TwoFactorAuthTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/TwoFactorAuthTest.java @@ -156,14 +156,14 @@ public abstract class TwoFactorAuthTest extends AbstractControllerTest { @Test public void testTwoFaPreVerificationTokenLifetime() throws Exception { configureTotpTwoFa(twoFaSettings -> { - twoFaSettings.setTotalAllowedTimeForVerification(5); + twoFaSettings.setTotalAllowedTimeForVerification(65); }); logInWithPreVerificationToken(username, password); await("expiration of the pre-verification token") - .atLeast(Duration.ofSeconds(3).plusMillis(500)) - .atMost(Duration.ofSeconds(6)) + .atLeast(Duration.ofSeconds(30).plusMillis(500)) + .atMost(Duration.ofSeconds(70)) .untilAsserted(() -> { doPost("/api/auth/2fa/verification/send?providerType=TOTP") .andExpect(status().isUnauthorized()); From c79b9cad01066ac15b71e5e1cf03a0e330783122 Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Wed, 1 Jun 2022 12:30:09 +0300 Subject: [PATCH 739/798] refactoring: 11_WidgetsBundleController - fix bug test save --- .../entitiy/widgetsBundle/DefaultWidgetsBundleService.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/widgetsBundle/DefaultWidgetsBundleService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/widgetsBundle/DefaultWidgetsBundleService.java index 5d393dc958..f3d8dfc138 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/widgetsBundle/DefaultWidgetsBundleService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/widgetsBundle/DefaultWidgetsBundleService.java @@ -15,12 +15,18 @@ */ package org.thingsboard.server.service.entitiy.widgetsBundle; +import lombok.AllArgsConstructor; +import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.edge.EdgeEventActionType; import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.widget.WidgetsBundle; +import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.entitiy.AbstractTbEntityService; import org.thingsboard.server.service.security.model.SecurityUser; +@Service +@TbCoreComponent +@AllArgsConstructor public class DefaultWidgetsBundleService extends AbstractTbEntityService implements TbWidgetsBundleService{ @Override public WidgetsBundle save(WidgetsBundle widgetsBundle, SecurityUser user) throws ThingsboardException { From 0bf8749ca52b2e57c2027554bbcd0ebd41ab57b1 Mon Sep 17 00:00:00 2001 From: YevhenBondarenko Date: Wed, 1 Jun 2022 12:05:00 +0200 Subject: [PATCH 740/798] fixed race condition during queue creating --- .../java/org/thingsboard/server/common/data/TenantProfile.java | 2 ++ .../server/queue/discovery/HashPartitionService.java | 2 -- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/TenantProfile.java b/common/data/src/main/java/org/thingsboard/server/common/data/TenantProfile.java index 308500a6bd..e5cbf23179 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/TenantProfile.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/TenantProfile.java @@ -21,6 +21,7 @@ import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import lombok.EqualsAndHashCode; +import lombok.ToString; import lombok.extern.slf4j.Slf4j; import org.thingsboard.server.common.data.id.TenantProfileId; import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; @@ -36,6 +37,7 @@ import static org.thingsboard.server.common.data.SearchTextBasedWithAdditionalIn @ApiModel @Data +@ToString(exclude = {"profileDataBytes"}) @EqualsAndHashCode(callSuper = true) @Slf4j public class TenantProfile extends SearchTextBased implements HasName { diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/discovery/HashPartitionService.java b/common/queue/src/main/java/org/thingsboard/server/queue/discovery/HashPartitionService.java index ccf8b9dd92..e7db76bead 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/discovery/HashPartitionService.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/discovery/HashPartitionService.java @@ -214,8 +214,6 @@ public class HashPartitionService implements PartitionService { @Override public synchronized void recalculatePartitions(ServiceInfo currentService, List otherServices) { - partitionsInit(); - tbTransportServicesByType.clear(); logServiceInfo(currentService); otherServices.forEach(this::logServiceInfo); From 98cd0aeea65b5a4394c77a2c931cd877d8fbabe3 Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Wed, 1 Jun 2022 16:01:50 +0300 Subject: [PATCH 741/798] refactoring: comments6 --- .../DefaultTbOtaPackageService.java | 2 +- .../service/rule/DefaultTbRuleChainService.java | 17 +++++++---------- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/otaPackageController/DefaultTbOtaPackageService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/otaPackageController/DefaultTbOtaPackageService.java index df67456116..555041ed8f 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/otaPackageController/DefaultTbOtaPackageService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/otaPackageController/DefaultTbOtaPackageService.java @@ -17,7 +17,7 @@ package org.thingsboard.server.service.entitiy.otaPackageController; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.StringUtils; +import org.thingsboard.server.common.data.StringUtils; import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.OtaPackage; diff --git a/application/src/main/java/org/thingsboard/server/service/rule/DefaultTbRuleChainService.java b/application/src/main/java/org/thingsboard/server/service/rule/DefaultTbRuleChainService.java index 1ef64d4f89..b54578107f 100644 --- a/application/src/main/java/org/thingsboard/server/service/rule/DefaultTbRuleChainService.java +++ b/application/src/main/java/org/thingsboard/server/service/rule/DefaultTbRuleChainService.java @@ -297,19 +297,15 @@ public class DefaultTbRuleChainService extends AbstractTbEntityService implement notificationEntityService.notifySendMsgToEdgeService(tenantId, ruleChain.getId(), EdgeEventActionType.UPDATED); } - updatedRuleChains.forEach(updatedRuleChain -> { + for (RuleChain updatedRuleChain : updatedRuleChains) { if (RuleChainType.EDGE.equals(ruleChain.getType())) { - notificationEntityService.notifySendMsgToEdgeService(tenantId, updatedRuleChain.getId(), EdgeEventActionType.UPDATED); + notificationEntityService.notifySendMsgToEdgeService(tenantId, updatedRuleChain.getId(), EdgeEventActionType.UPDATED); } else { - try { - RuleChainMetaData updatedRuleChainMetaData = checkNotNull(ruleChainService.loadRuleChainMetaData(tenantId, updatedRuleChain.getId())); - notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, null, updatedRuleChain.getId(), - updatedRuleChain, user, ActionType.UPDATED, false, null, updatedRuleChainMetaData); - } catch (ThingsboardException e) { - e.printStackTrace(); - } + RuleChainMetaData updatedRuleChainMetaData = checkNotNull(ruleChainService.loadRuleChainMetaData(tenantId, updatedRuleChain.getId())); + notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, null, updatedRuleChain.getId(), + updatedRuleChain, user, ActionType.UPDATED, false, null, updatedRuleChainMetaData); } - }); + } return savedRuleChainMetaData; } catch (Exception e) { notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, null, emptyId(EntityType.RULE_CHAIN), @@ -398,6 +394,7 @@ public class DefaultTbRuleChainService extends AbstractTbEntityService implement throw handleException(e); } } + public Set updateRelatedRuleChains(TenantId tenantId, RuleChainId ruleChainId, Map labelsMap) { Set updatedRuleChains = new HashSet<>(); List usageList = getOutputLabelUsage(tenantId, ruleChainId); From c377f80caa7b221e40303430db946d2ea7d91f55 Mon Sep 17 00:00:00 2001 From: fe-dev Date: Wed, 1 Jun 2022 19:14:09 +0300 Subject: [PATCH 742/798] UI: Fix bugs --- .../tenant-profile-queues.component.html | 26 +- .../queue/tenant-profile-queues.component.ts | 10 +- .../profile/tenant-profile.component.ts | 4 +- .../queue/queue-form.component.html | 341 +++++++++--------- .../components/queue/queue-form.component.ts | 47 +-- .../tenant-profiles-table-config.resolver.ts | 15 +- ui-ngx/src/app/shared/models/queue.models.ts | 61 +++- .../assets/locale/locale.constant-en_US.json | 27 +- 8 files changed, 302 insertions(+), 229 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/profile/queue/tenant-profile-queues.component.html b/ui-ngx/src/app/modules/home/components/profile/queue/tenant-profile-queues.component.html index 9339438726..b755e02d8b 100644 --- a/ui-ngx/src/app/modules/home/components/profile/queue/tenant-profile-queues.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/queue/tenant-profile-queues.component.html @@ -20,12 +20,26 @@ *ngFor="let queuesControl of queuesFormArray.controls; trackBy: trackByQueue; let $index = index; last as isLast;" [ngStyle]="!isLast ? {paddingBottom: '8px'} : {}"> - - + + +
+ + {{ queuesControl.value.name }} + + + +
+
+ + + +
{ - this.updateModel(); - }); + this.valueChangeSubscription$ = this.tenantProfileQueuesFormGroup.valueChanges.subscribe(() => + this.updateModel() + ); } public trackByQueue(index: number, queueControl: AbstractControl) { @@ -183,10 +183,6 @@ export class TenantProfileQueuesComponent implements ControlValueAccessor, Valid }; } - getName(value) { - return this.utils.customTranslation(value, value); - } - private updateModel() { const queues: Array = this.tenantProfileQueuesFormGroup.get('queues').value; this.propagateChange(queues); diff --git a/ui-ngx/src/app/modules/home/components/profile/tenant-profile.component.ts b/ui-ngx/src/app/modules/home/components/profile/tenant-profile.component.ts index ed88fe9fba..413055fede 100644 --- a/ui-ngx/src/app/modules/home/components/profile/tenant-profile.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/tenant-profile.component.ts @@ -109,7 +109,9 @@ export class TenantProfileComponent extends EntityComponent { this.entityForm.patchValue({name: entity.name}, {emitEvent: false}); this.entityForm.patchValue({isolatedTbCore: entity.isolatedTbCore}, {emitEvent: false}); this.entityForm.patchValue({isolatedTbRuleEngine: entity.isolatedTbRuleEngine}, {emitEvent: false}); - this.entityForm.get('profileData').patchValue({configuration: entity.profileData?.configuration}, {emitEvent: false}); + this.entityForm.get('profileData').patchValue({ + configuration: !this.isAdd ? entity.profileData?.configuration : createTenantProfileConfiguration(TenantProfileType.DEFAULT) + }, {emitEvent: false}); this.entityForm.get('profileData').patchValue({queueConfiguration: entity.profileData?.queueConfiguration}, {emitEvent: false}); this.entityForm.patchValue({description: entity.description}, {emitEvent: false}); } diff --git a/ui-ngx/src/app/modules/home/components/queue/queue-form.component.html b/ui-ngx/src/app/modules/home/components/queue/queue-form.component.html index 920ad2f162..5dee2afa87 100644 --- a/ui-ngx/src/app/modules/home/components/queue/queue-form.component.html +++ b/ui-ngx/src/app/modules/home/components/queue/queue-form.component.html @@ -15,182 +15,165 @@ limitations under the License. --> - - -
- - {{ queueTitle }} - - - -
-
- - - - admin.queue-name - - - {{ 'queue.name-required' | translate }} - - - {{ 'queue.name-unique' | translate }} - - - - queue.poll-interval - - - {{ 'queue.poll-interval-required' | translate }} - - - {{ 'queue.poll-interval-min-value' | translate }} - - - - queue.partitions - - - {{ 'queue.partitions-required' | translate }} - - - {{ 'queue.partitions-min-value' | translate }} - - - -
{{ 'queue.consumer-per-partition' | translate }}
-
{{'queue.consumer-per-partition-hint' | translate}}
-
- - queue.processing-timeout - - - {{ 'queue.pack-processing-timeout-required' | translate }} - - - {{ 'queue.pack-processing-timeout-min-value' | translate }} - - - - - - - queue.submit-strategy - - - -
- - queue.submit-strategy - - - {{ strategy }} - - - - {{ 'queue.submit-strategy-type-required' | translate }} - - - - queue.batch-size - - - {{ 'queue.batch-size-required' | translate }} - - - {{ 'queue.batch-size-min-value' | translate }} - - -
-
-
- - - - queue.processing-strategy - - - -
- - queue.processing-strategy - - - {{ strategy }} - - - - {{ 'queue.processing-strategy-type-required' | translate }} - - - - queue.retries - - - {{ 'queue.retries-required' | translate }} - - - {{ 'queue.retries-min-value' | translate }} - - - - queue.failure-percentage - - - {{ 'queue.failure-percentage-required' | translate }} - - - {{ 'queue.failure-percentage-min-value' | translate }} - - - {{ 'queue.failure-percentage-max-value' | translate }} - - - - queue.pause-between-retries - - - {{ 'queue.pause-between-retries-required' | translate }} - - - {{ 'queue.pause-between-retries-min-value' | translate }} - - - - queue.max-pause-between-retries - - - {{ 'queue.max-pause-between-retries-required' | translate }} - - - {{ 'queue.max-pause-between-retries-min-value' | translate }} - - -
-
-
-
- - queue.description - - - -
-
+ +
+ + admin.queue-name + + + {{ 'queue.name-required' | translate }} + + + {{ 'queue.name-unique' | translate }} + + + + queue.poll-interval + + + {{ 'queue.poll-interval-required' | translate }} + + + {{ 'queue.poll-interval-min-value' | translate }} + + + + queue.partitions + + + {{ 'queue.partitions-required' | translate }} + + + {{ 'queue.partitions-min-value' | translate }} + + + +
{{ 'queue.consumer-per-partition' | translate }}
+
{{'queue.consumer-per-partition-hint' | translate}}
+
+ + queue.processing-timeout + + + {{ 'queue.pack-processing-timeout-required' | translate }} + + + {{ 'queue.pack-processing-timeout-min-value' | translate }} + + + + + + + queue.submit-strategy + + + +
+ + queue.submit-strategy + + + {{ queueSubmitStrategyTypesMap.get(queueSubmitStrategyTypes[strategy]).label | translate }} + + + + {{ 'queue.submit-strategy-type-required' | translate }} + + + + queue.batch-size + + + {{ 'queue.batch-size-required' | translate }} + + + {{ 'queue.batch-size-min-value' | translate }} + + +
+
+
+ + + + queue.processing-strategy + + + +
+ + queue.processing-strategy + + + {{ queueProcessingStrategyTypesMap.get(queueProcessingStrategyTypes[strategy]).label | translate }} + + + + {{ 'queue.processing-strategy-type-required' | translate }} + + + + queue.retries + + + {{ 'queue.retries-required' | translate }} + + + {{ 'queue.retries-min-value' | translate }} + + + + queue.failure-percentage + + + {{ 'queue.failure-percentage-required' | translate }} + + + {{ 'queue.failure-percentage-min-value' | translate }} + + + {{ 'queue.failure-percentage-max-value' | translate }} + + + + queue.pause-between-retries + + + {{ 'queue.pause-between-retries-required' | translate }} + + + {{ 'queue.pause-between-retries-min-value' | translate }} + + + + queue.max-pause-between-retries + + + {{ 'queue.max-pause-between-retries-required' | translate }} + + + {{ 'queue.max-pause-between-retries-min-value' | translate }} + + +
+
+
+
+ + queue.description + + queue.description-hint + + diff --git a/ui-ngx/src/app/modules/home/components/queue/queue-form.component.ts b/ui-ngx/src/app/modules/home/components/queue/queue-form.component.ts index 6a437799b5..cb2cf06b88 100644 --- a/ui-ngx/src/app/modules/home/components/queue/queue-form.component.ts +++ b/ui-ngx/src/app/modules/home/components/queue/queue-form.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, forwardRef, Input, OnInit, Output, EventEmitter, OnDestroy } from '@angular/core'; +import { Component, forwardRef, Input, OnDestroy, OnInit } from '@angular/core'; import { ControlValueAccessor, FormBuilder, @@ -25,9 +25,14 @@ import { Validator, Validators } from '@angular/forms'; -import { MatDialog } from '@angular/material/dialog'; import { UtilsService } from '@core/services/utils.service'; -import { QueueInfo, QueueProcessingStrategyTypes, QueueSubmitStrategyTypes } from '@shared/models/queue.models'; +import { + QueueInfo, + QueueProcessingStrategyTypes, + QueueProcessingStrategyTypesMap, + QueueSubmitStrategyTypes, + QueueSubmitStrategyTypesMap +} from '@shared/models/queue.models'; import { isDefinedAndNotNull } from '@core/utils'; import { Subscription } from 'rxjs'; @@ -56,31 +61,25 @@ export class QueueFormComponent implements ControlValueAccessor, OnInit, OnDestr @Input() newQueue = false; - @Input() - mainQueue = false; - @Input() systemQueue = false; - @Input() - expanded = false; - - @Output() - removeQueue = new EventEmitter(); - queueFormGroup: FormGroup; - submitStrategies: string[] = []; - processingStrategies: string[] = []; - queueTitle = ''; hideBatchSize = false; + queueSubmitStrategyTypes = QueueSubmitStrategyTypes; + queueProcessingStrategyTypes = QueueProcessingStrategyTypes; + submitStrategies: string[] = Object.values(this.queueSubmitStrategyTypes); + processingStrategies: string[] = Object.values(this.queueProcessingStrategyTypes); + queueSubmitStrategyTypesMap = QueueSubmitStrategyTypesMap; + queueProcessingStrategyTypesMap = QueueProcessingStrategyTypesMap; + private modelValue: QueueInfo; private propagateChange = null; private propagateChangePending = false; private valueChange$: Subscription = null; - constructor(private dialog: MatDialog, - private utils: UtilsService, + constructor(private utils: UtilsService, private fb: FormBuilder) { } @@ -98,8 +97,6 @@ export class QueueFormComponent implements ControlValueAccessor, OnInit, OnDestr } ngOnInit() { - this.submitStrategies = Object.values(QueueSubmitStrategyTypes); - this.processingStrategies = Object.values(QueueProcessingStrategyTypes); this.queueFormGroup = this.fb.group( { name: ['', [Validators.required]], @@ -128,7 +125,6 @@ export class QueueFormComponent implements ControlValueAccessor, OnInit, OnDestr }); this.queueFormGroup.get('name').valueChanges.subscribe((value) => { this.queueFormGroup.patchValue({topic: `tb_rule_engine.${value}`}); - this.queueTitle = this.utils.customTranslation(value, value); }); this.queueFormGroup.get('submitStrategy').get('type').valueChanges.subscribe(() => { this.submitStrategyTypeChanged(); @@ -160,14 +156,13 @@ export class QueueFormComponent implements ControlValueAccessor, OnInit, OnDestr writeValue(value: QueueInfo): void { this.propagateChangePending = false; this.modelValue = value; - if (!this.modelValue.name) { - this.expanded = true; - } - this.queueTitle = this.utils.customTranslation(value.name, value.name); if (isDefinedAndNotNull(this.modelValue)) { this.queueFormGroup.patchValue(this.modelValue, {emitEvent: false}); + this.submitStrategyTypeChanged(); + if (!this.modelValue.name) { + this.queueFormGroup.get('name').enable({emitEvent: false}); + } } - this.submitStrategyTypeChanged(); if (!this.disabled && !this.queueFormGroup.valid) { this.updateModel(); } @@ -209,13 +204,11 @@ export class QueueFormComponent implements ControlValueAccessor, OnInit, OnDestr const type: QueueSubmitStrategyTypes = form.get('type').value; const batchSizeField = form.get('batchSize'); if (type === QueueSubmitStrategyTypes.BATCH) { - batchSizeField.enable({emitEvent: false}); batchSizeField.patchValue(1000, {emitEvent: false}); batchSizeField.setValidators([Validators.min(1), Validators.required]); this.hideBatchSize = true; } else { batchSizeField.patchValue(null, {emitEvent: false}); - batchSizeField.disable({emitEvent: false}); batchSizeField.clearValidators(); this.hideBatchSize = false; } diff --git a/ui-ngx/src/app/modules/home/pages/tenant-profile/tenant-profiles-table-config.resolver.ts b/ui-ngx/src/app/modules/home/pages/tenant-profile/tenant-profiles-table-config.resolver.ts index 7484d8b403..d9f7efc602 100644 --- a/ui-ngx/src/app/modules/home/pages/tenant-profile/tenant-profiles-table-config.resolver.ts +++ b/ui-ngx/src/app/modules/home/pages/tenant-profile/tenant-profiles-table-config.resolver.ts @@ -101,12 +101,15 @@ export class TenantProfilesTableConfigResolver implements Resolve { - value.id = guid(); - queuesWithId.push(value); - }); - return queuesWithId; + if (queues) { + const queuesWithId = []; + queues.forEach(value => { + value.id = guid(); + queuesWithId.push(value); + }); + return queuesWithId; + } + return null; } resolve(): EntityTableConfig { diff --git a/ui-ngx/src/app/shared/models/queue.models.ts b/ui-ngx/src/app/shared/models/queue.models.ts index 6c67b4db1b..dc90781d30 100644 --- a/ui-ngx/src/app/shared/models/queue.models.ts +++ b/ui-ngx/src/app/shared/models/queue.models.ts @@ -14,9 +14,9 @@ /// limitations under the License. /// -import { BaseData, HasId } from '@shared/models/base-data'; +import { BaseData } from '@shared/models/base-data'; import { TenantId } from '@shared/models/id/tenant-id'; -import {QueueId} from '@shared/models/id/queue-id'; +import { QueueId } from '@shared/models/id/queue-id'; export enum ServiceType { TB_CORE = 'TB_CORE', @@ -33,6 +33,35 @@ export enum QueueSubmitStrategyTypes { BATCH = 'BATCH' } +export interface QueueStrategyData { + label: string; + hint: string; +} + +export const QueueSubmitStrategyTypesMap = new Map( + [ + [QueueSubmitStrategyTypes.SEQUENTIAL_BY_ORIGINATOR, { + label: 'queue.strategies.sequential-by-originator-label', + hint: 'queue.strategies.sequential-by-originator-hint', + }], + [QueueSubmitStrategyTypes.SEQUENTIAL_BY_TENANT, { + label: 'queue.strategies.sequential-by-tenant-label', + hint: 'queue.strategies.sequential-by-tenant-hint', + }], + [QueueSubmitStrategyTypes.SEQUENTIAL, { + label: 'queue.strategies.sequential-label', + hint: 'queue.strategies.sequential-hint', + }], + [QueueSubmitStrategyTypes.BURST, { + label: 'queue.strategies.burst-label', + hint: 'queue.strategies.burst-hint', + }], + [QueueSubmitStrategyTypes.BATCH, { + label: 'queue.strategies.batch-label', + hint: 'queue.strategies.batch-hint', + }] + ]); + export enum QueueProcessingStrategyTypes { RETRY_FAILED_AND_TIMED_OUT = 'RETRY_FAILED_AND_TIMED_OUT', SKIP_ALL_FAILURES = 'SKIP_ALL_FAILURES', @@ -42,6 +71,34 @@ export enum QueueProcessingStrategyTypes { RETRY_TIMED_OUT = 'RETRY_TIMED_OUT' } +export const QueueProcessingStrategyTypesMap = new Map( + [ + [QueueProcessingStrategyTypes.RETRY_FAILED_AND_TIMED_OUT, { + label: 'queue.strategies.retry-failed-and-timeout-label', + hint: 'queue.strategies.retry-failed-and-timout-hint', + }], + [QueueProcessingStrategyTypes.SKIP_ALL_FAILURES, { + label: 'queue.strategies.skip-all-failures-label', + hint: 'queue.strategies.skip-all-failures-hint', + }], + [QueueProcessingStrategyTypes.SKIP_ALL_FAILURES_AND_TIMED_OUT, { + label: 'queue.strategies.skip-all-failures-and-timeouts-label', + hint: 'queue.strategies.skip-all-failures-and-timeouts-hint', + }], + [QueueProcessingStrategyTypes.RETRY_ALL, { + label: 'queue.strategies.retry-all-label', + hint: 'queue.strategies.retry-all-hint', + }], + [QueueProcessingStrategyTypes.RETRY_FAILED, { + label: 'queue.strategies.retry-failed-label', + hint: 'queue.strategies.retry-failed-hint', + }], + [QueueProcessingStrategyTypes.RETRY_TIMED_OUT, { + label: 'queue.strategies.retry-timeout-label', + hint: 'queue.strategies.retry-timeout-hint', + }] + ]); + export interface QueueInfo extends BaseData { generatedId?: string; name: string; diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index ff1f85b1f0..12d35b6e0f 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -2821,7 +2821,32 @@ "copyId": "Copy queue Id", "idCopiedMessage": "Queue Id has been copied to clipboard", "description": "Description", - "alt-description": "Submit Strategy: {{submitStrategy}}, Processing Strategy: {{processingStrategy}}" + "description-hint": "This text will be displayed in the Queue description instead of the selected strategy", + "alt-description": "Submit Strategy: {{submitStrategy}}, Processing Strategy: {{processingStrategy}}", + "strategies": { + "sequential-by-originator-label": "Sequential by originator", + "sequential-by-originator-hint": "New message for e.g. device A is not submitted until previous message for device A is acknowledged", + "sequential-by-tenant-label": "Sequential by tenant", + "sequential-by-tenant-hint": "New message for e.g tenant A is not submitted until previous message for tenant A is acknowledged", + "sequential-label": "Sequential", + "sequential-hint": "New message is not submitted until previous message is acknowledged", + "burst-label": "Burst", + "burst-hint": "All messages are submitted to the rule chains in the order they arrive", + "batch-label": "Batch", + "batch-hint": "New batch is not submitted until previous batch is acknowledged", + "skip-all-failures-label": "Skip all failures", + "skip-all-failures-hint": "Ignore all failures", + "skip-all-failures-and-timeouts-label": "Skip all failures and timeouts", + "skip-all-failures-and-timeouts-hint": "Ignore all failures and timeouts", + "retry-all-label": "Retry all", + "retry-all-hint": "Retry all messages from processing pack", + "retry-failed-label": "Retry failed", + "retry-failed-hint": "Retry all failed messages from processing pack", + "retry-timeout-label": "Retry timeout", + "retry-timeout-hint": "Retry all timed-out messages from processing pack", + "retry-failed-and-timeout-label": "Retry failed and timeout", + "retry-failed-and-timeout-hint": "Retry all failed and timed-out messages from processing pack" + } }, "tenant": { "tenant": "Tenant", From a60f67dab622b32c9659f4e2cb7fd9962ada7988 Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Wed, 1 Jun 2022 19:58:09 +0300 Subject: [PATCH 743/798] refactoring: comments7 (Resource) --- .../sql/BaseTbResourceServiceTest.java | 45 +++++++++---------- 1 file changed, 22 insertions(+), 23 deletions(-) diff --git a/application/src/test/java/org/thingsboard/server/service/resource/sql/BaseTbResourceServiceTest.java b/application/src/test/java/org/thingsboard/server/service/resource/sql/BaseTbResourceServiceTest.java index 59ede61621..31349ebec4 100644 --- a/application/src/test/java/org/thingsboard/server/service/resource/sql/BaseTbResourceServiceTest.java +++ b/application/src/test/java/org/thingsboard/server/service/resource/sql/BaseTbResourceServiceTest.java @@ -24,7 +24,6 @@ import org.junit.Test; import org.junit.rules.ExpectedException; import org.springframework.beans.factory.annotation.Autowired; import org.thingsboard.server.common.data.EntityInfo; -import org.thingsboard.server.common.data.OtaPackage; import org.thingsboard.server.common.data.ResourceType; import org.thingsboard.server.common.data.TbResource; import org.thingsboard.server.common.data.TbResourceInfo; @@ -173,7 +172,7 @@ public class BaseTbResourceServiceTest extends AbstractControllerTest { resource.setResourceType(ResourceType.JKS); resource.setFileName(filename); resource.setData("1"); - return resourceService.saveResource(resource); + return resourceService.saveResourceInternal(resource); } @Test @@ -185,7 +184,7 @@ public class BaseTbResourceServiceTest extends AbstractControllerTest { resource.setFileName(DEFAULT_FILE_NAME); resource.setData("Test Data"); - TbResource savedResource = resourceService.saveResource(resource); + TbResource savedResource = resourceService.saveResourceInternal(resource); Assert.assertNotNull(savedResource); Assert.assertNotNull(savedResource.getId()); @@ -197,11 +196,11 @@ public class BaseTbResourceServiceTest extends AbstractControllerTest { savedResource.setTitle("My new resource"); - resourceService.saveResource(savedResource); + resourceService.saveResourceInternal(savedResource); TbResource foundResource = resourceService.findResourceById(tenantId, savedResource.getId()); Assert.assertEquals(foundResource.getTitle(), savedResource.getTitle()); - resourceService.deleteResource(tenantId, savedResource.getId()); + resourceService.delete(savedResource, null); } @Test @@ -212,7 +211,7 @@ public class BaseTbResourceServiceTest extends AbstractControllerTest { resource.setFileName("test_model.xml"); resource.setData(Base64.getEncoder().encodeToString(LWM2M_TEST_MODEL.getBytes())); - TbResource savedResource = resourceService.saveResource(resource); + TbResource savedResource = resourceService.saveResourceInternal(resource); Assert.assertNotNull(savedResource); Assert.assertNotNull(savedResource.getId()); @@ -222,7 +221,7 @@ public class BaseTbResourceServiceTest extends AbstractControllerTest { Assert.assertEquals("0_1.0", savedResource.getResourceKey()); Assert.assertEquals(resource.getData(), savedResource.getData()); - resourceService.deleteResource(tenantId, savedResource.getId()); + resourceService.delete(savedResource, null); } @Test @@ -232,11 +231,11 @@ public class BaseTbResourceServiceTest extends AbstractControllerTest { resource.setTitle("My resource"); resource.setFileName(DEFAULT_FILE_NAME); resource.setData("Test Data"); - TbResource savedResource = resourceService.saveResource(resource); + TbResource savedResource = resourceService.saveResourceInternal(resource); Assert.assertEquals(TenantId.SYS_TENANT_ID, savedResource.getTenantId()); - resourceService.deleteResource(tenantId, savedResource.getId()); + resourceService.delete(savedResource, null); } @Test(expected = DataValidationException.class) @@ -248,7 +247,7 @@ public class BaseTbResourceServiceTest extends AbstractControllerTest { resource.setFileName(DEFAULT_FILE_NAME); resource.setData("Test Data"); - TbResource savedResource = resourceService.saveResource(resource); + TbResource savedResource = resourceService.saveResourceInternal(resource); TbResource resource2 = new TbResource(); resource.setTenantId(tenantId); @@ -258,9 +257,9 @@ public class BaseTbResourceServiceTest extends AbstractControllerTest { resource.setData("Test Data"); try { - resourceService.saveResource(resource2); + resourceService.saveResourceInternal(resource2); } finally { - resourceService.deleteResource(tenantId, savedResource.getId()); + resourceService.delete(savedResource, null); } } @@ -271,7 +270,7 @@ public class BaseTbResourceServiceTest extends AbstractControllerTest { resource.setResourceType(ResourceType.JKS); resource.setFileName(DEFAULT_FILE_NAME); resource.setData("Test Data"); - resourceService.saveResource(resource); + resourceService.saveResourceInternal(resource); } @Test(expected = DataValidationException.class) @@ -282,7 +281,7 @@ public class BaseTbResourceServiceTest extends AbstractControllerTest { resource.setTitle("My resource"); resource.setFileName(DEFAULT_FILE_NAME); resource.setData("Test Data"); - resourceService.saveResource(resource); + resourceService.saveResourceInternal(resource); } @Test @@ -292,12 +291,12 @@ public class BaseTbResourceServiceTest extends AbstractControllerTest { resource.setTitle("My resource"); resource.setFileName(DEFAULT_FILE_NAME); resource.setData("Test Data"); - TbResource savedResource = resourceService.saveResource(resource); + TbResource savedResource = resourceService.saveResourceInternal(resource); TbResource foundResource = resourceService.findResourceById(tenantId, savedResource.getId()); Assert.assertNotNull(foundResource); Assert.assertEquals(savedResource, foundResource); - resourceService.deleteResource(tenantId, savedResource.getId()); + resourceService.delete(savedResource, null); } @Test @@ -308,12 +307,12 @@ public class BaseTbResourceServiceTest extends AbstractControllerTest { resource.setTitle("My resource"); resource.setFileName(DEFAULT_FILE_NAME); resource.setData("Test Data"); - TbResource savedResource = resourceService.saveResource(resource); + TbResource savedResource = resourceService.saveResourceInternal(resource); TbResource foundResource = resourceService.getResource(tenantId, savedResource.getResourceType(), savedResource.getResourceKey()); Assert.assertNotNull(foundResource); Assert.assertEquals(savedResource, foundResource); - resourceService.deleteResource(tenantId, savedResource.getId()); + resourceService.delete(savedResource, null); } @Test @@ -323,11 +322,11 @@ public class BaseTbResourceServiceTest extends AbstractControllerTest { resource.setTitle("My resource"); resource.setFileName(DEFAULT_FILE_NAME); resource.setData("Test Data"); - TbResource savedResource = resourceService.saveResource(resource); + TbResource savedResource = resourceService.saveResourceInternal(resource); TbResource foundResource = resourceService.findResourceById(tenantId, savedResource.getId()); Assert.assertNotNull(foundResource); - resourceService.deleteResource(tenantId, savedResource.getId()); + resourceService.delete(savedResource, null); foundResource = resourceService.findResourceById(tenantId, savedResource.getId()); Assert.assertNull(foundResource); } @@ -349,7 +348,7 @@ public class BaseTbResourceServiceTest extends AbstractControllerTest { resource.setResourceType(ResourceType.JKS); resource.setFileName(i + DEFAULT_FILE_NAME); resource.setData("Test Data"); - resources.add(new TbResourceInfo(resourceService.saveResource(resource))); + resources.add(new TbResourceInfo(resourceService.saveResourceInternal(resource))); } List loadedResources = new ArrayList<>(); @@ -397,7 +396,7 @@ public class BaseTbResourceServiceTest extends AbstractControllerTest { resource.setResourceType(ResourceType.JKS); resource.setFileName(i + DEFAULT_FILE_NAME); resource.setData("Test Data"); - TbResourceInfo tbResourceInfo = new TbResourceInfo(resourceService.saveResource(resource)); + TbResourceInfo tbResourceInfo = new TbResourceInfo(resourceService.saveResourceInternal(resource)); if (i >= 50) { resources.add(tbResourceInfo); } @@ -410,7 +409,7 @@ public class BaseTbResourceServiceTest extends AbstractControllerTest { resource.setResourceType(ResourceType.JKS); resource.setFileName(i + DEFAULT_FILE_NAME); resource.setData("Test Data"); - resources.add(new TbResourceInfo(resourceService.saveResource(resource))); + resources.add(new TbResourceInfo(resourceService.saveResourceInternal(resource))); } List loadedResources = new ArrayList<>(); From 96cc5c943b37c707830027ec31548ea24ead065b Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Wed, 1 Jun 2022 20:00:21 +0300 Subject: [PATCH 744/798] refactoring: comments7_1 (Resource) --- .../controller/TbResourceController.java | 4 +- .../DefaultTbResourceNotifyService.java | 71 ------------------- .../resource/TbResourceNotifyService.java | 22 ------ .../resource/DefaultTbResourceService.java | 48 ++++++++++--- .../service/resource/TbResourceService.java | 7 +- 5 files changed, 45 insertions(+), 107 deletions(-) delete mode 100644 application/src/main/java/org/thingsboard/server/service/entitiy/resource/DefaultTbResourceNotifyService.java delete mode 100644 application/src/main/java/org/thingsboard/server/service/entitiy/resource/TbResourceNotifyService.java diff --git a/application/src/main/java/org/thingsboard/server/controller/TbResourceController.java b/application/src/main/java/org/thingsboard/server/controller/TbResourceController.java index 21d7eadb69..120449c137 100644 --- a/application/src/main/java/org/thingsboard/server/controller/TbResourceController.java +++ b/application/src/main/java/org/thingsboard/server/controller/TbResourceController.java @@ -40,7 +40,7 @@ import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.queue.util.TbCoreComponent; -import org.thingsboard.server.service.entitiy.resource.TbResourceNotifyService; +import org.thingsboard.server.service.resource.TbResourceService; import org.thingsboard.server.service.security.permission.Operation; import org.thingsboard.server.service.security.permission.Resource; @@ -71,7 +71,7 @@ import static org.thingsboard.server.controller.ControllerConstants.UUID_WIKI_LI @RequiredArgsConstructor public class TbResourceController extends BaseController { - private final TbResourceNotifyService tbResourceService; + private final TbResourceService tbResourceService; public static final String RESOURCE_ID = "resourceId"; diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/resource/DefaultTbResourceNotifyService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/resource/DefaultTbResourceNotifyService.java deleted file mode 100644 index beb1707214..0000000000 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/resource/DefaultTbResourceNotifyService.java +++ /dev/null @@ -1,71 +0,0 @@ -/** - * Copyright © 2016-2022 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.service.entitiy.resource; - - -import lombok.AllArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Service; -import org.thingsboard.server.common.data.EntityType; -import org.thingsboard.server.common.data.TbResource; -import org.thingsboard.server.common.data.audit.ActionType; -import org.thingsboard.server.common.data.exception.ThingsboardException; -import org.thingsboard.server.common.data.id.TbResourceId; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.queue.util.TbCoreComponent; -import org.thingsboard.server.service.entitiy.AbstractTbEntityService; -import org.thingsboard.server.service.security.model.SecurityUser; - -@Service -@TbCoreComponent -@AllArgsConstructor -@Slf4j -public class DefaultTbResourceNotifyService extends AbstractTbEntityService implements TbResourceNotifyService { - - @Override - public TbResource save(TbResource tbResource, SecurityUser user) throws ThingsboardException { - ActionType actionType = tbResource.getId() == null ? ActionType.ADDED : ActionType.UPDATED; - TenantId tenantId = tbResource.getTenantId(); - try { - - TbResource savedResource = checkNotNull(resourceService.saveResource(tbResource)); - tbClusterService.onResourceChange(savedResource, null); - notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, null, savedResource.getId(), - savedResource, user, actionType, false, null); - return savedResource; - } catch (Exception e) { - notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, null, emptyId(EntityType.TB_RESOURCE), - tbResource, user, actionType, false, e); - throw handleException(e); - } - } - - @Override - public void delete(TbResource tbResource, SecurityUser user) throws ThingsboardException { - TbResourceId resourceId = tbResource.getId(); - TenantId tenantId = tbResource.getTenantId(); - try { - resourceService.deleteResource(tenantId, resourceId); - tbClusterService.onResourceDeleted(tbResource, null); - notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, null, resourceId, tbResource, user, ActionType.DELETED, - false, null, resourceId.toString()); - } catch (Exception e) { - notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, null, emptyId(EntityType.TB_RESOURCE), null, user, ActionType.DELETED, - false, e, resourceId.toString()); - throw handleException(e); - } - } -} diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/resource/TbResourceNotifyService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/resource/TbResourceNotifyService.java deleted file mode 100644 index 1605d745c2..0000000000 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/resource/TbResourceNotifyService.java +++ /dev/null @@ -1,22 +0,0 @@ -/** - * Copyright © 2016-2022 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.service.entitiy.resource; - -import org.thingsboard.server.common.data.TbResource; -import org.thingsboard.server.service.entitiy.SimpleTbEntityService; - -public interface TbResourceNotifyService extends SimpleTbEntityService { -} diff --git a/application/src/main/java/org/thingsboard/server/service/resource/DefaultTbResourceService.java b/application/src/main/java/org/thingsboard/server/service/resource/DefaultTbResourceService.java index fab78afc70..c91ff91152 100644 --- a/application/src/main/java/org/thingsboard/server/service/resource/DefaultTbResourceService.java +++ b/application/src/main/java/org/thingsboard/server/service/resource/DefaultTbResourceService.java @@ -22,9 +22,11 @@ import org.eclipse.leshan.core.model.DefaultDDFFileValidator; import org.eclipse.leshan.core.model.InvalidDDFFileException; import org.eclipse.leshan.core.model.ObjectModel; import org.springframework.stereotype.Service; +import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.ResourceType; import org.thingsboard.server.common.data.TbResource; import org.thingsboard.server.common.data.TbResourceInfo; +import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.TbResourceId; @@ -36,6 +38,8 @@ import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.resource.ResourceService; +import org.thingsboard.server.service.entitiy.AbstractTbEntityService; +import org.thingsboard.server.service.security.model.SecurityUser; import java.io.ByteArrayInputStream; import java.io.IOException; @@ -53,7 +57,7 @@ import static org.thingsboard.server.dao.service.Validator.validateId; @Slf4j @Service -public class DefaultTbResourceService implements TbResourceService { +public class DefaultTbResourceService extends AbstractTbEntityService implements TbResourceService { private final ResourceService resourceService; private final DDFFileParser ddfFileParser; @@ -64,7 +68,7 @@ public class DefaultTbResourceService implements TbResourceService { } @Override - public TbResource saveResource(TbResource resource) throws ThingsboardException { + public TbResource saveResourceInternal(TbResource resource) throws ThingsboardException { log.trace("Executing saveResource [{}]", resource); if (StringUtils.isEmpty(resource.getData())) { throw new DataValidationException("Resource data should be specified!"); @@ -150,11 +154,6 @@ public class DefaultTbResourceService implements TbResourceService { .collect(Collectors.toList()); } - @Override - public void deleteResource(TenantId tenantId, TbResourceId resourceId) { - resourceService.deleteResource(tenantId, resourceId); - } - @Override public void deleteResourcesByTenantId(TenantId tenantId) { resourceService.deleteResourcesByTenantId(tenantId); @@ -215,4 +214,37 @@ public class DefaultTbResourceService implements TbResourceService { return null; } } -} + + @Override + public TbResource save(TbResource tbResource, SecurityUser user) throws ThingsboardException { + ActionType actionType = tbResource.getId() == null ? ActionType.ADDED : ActionType.UPDATED; + TenantId tenantId = tbResource.getTenantId(); + try { + + TbResource savedResource = checkNotNull(saveResourceInternal(tbResource)); + tbClusterService.onResourceChange(savedResource, null); + notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, null, savedResource.getId(), + savedResource, user, actionType, false, null); + return savedResource; + } catch (Exception e) { + notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, null, emptyId(EntityType.TB_RESOURCE), + tbResource, user, actionType, false, e); + throw handleException(e); + } + } + + @Override + public void delete(TbResource tbResource, SecurityUser user) throws ThingsboardException { + TbResourceId resourceId = tbResource.getId(); + TenantId tenantId = tbResource.getTenantId(); + try { + resourceService.deleteResource(tenantId, resourceId); + tbClusterService.onResourceDeleted(tbResource, null); + notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, null, resourceId, tbResource, user, ActionType.DELETED, + false, null, resourceId.toString()); + } catch (Exception e) { + notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, null, emptyId(EntityType.TB_RESOURCE), null, user, ActionType.DELETED, + false, e, resourceId.toString()); + throw handleException(e); + } + }} diff --git a/application/src/main/java/org/thingsboard/server/service/resource/TbResourceService.java b/application/src/main/java/org/thingsboard/server/service/resource/TbResourceService.java index bfa04d4cd6..3bcb7f0722 100644 --- a/application/src/main/java/org/thingsboard/server/service/resource/TbResourceService.java +++ b/application/src/main/java/org/thingsboard/server/service/resource/TbResourceService.java @@ -24,12 +24,13 @@ import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.lwm2m.LwM2mObject; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; +import org.thingsboard.server.service.entitiy.SimpleTbEntityService; import java.util.List; -public interface TbResourceService { +public interface TbResourceService extends SimpleTbEntityService { - TbResource saveResource(TbResource resource) throws ThingsboardException; + TbResource saveResourceInternal(TbResource resource) throws ThingsboardException; TbResource getResource(TenantId tenantId, ResourceType resourceType, String resourceKey); @@ -51,8 +52,6 @@ public interface TbResourceService { String sortOrder, PageLink pageLink); - void deleteResource(TenantId tenantId, TbResourceId resourceId); - void deleteResourcesByTenantId(TenantId tenantId); long sumDataSizeByTenantId(TenantId tenantId); From 15f4d26be9230e623392a9139a9740796aa61c2c Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Wed, 1 Jun 2022 23:35:31 +0300 Subject: [PATCH 745/798] refactoring: comments7_2 (Resource) --- .../server/service/resource/DefaultTbResourceService.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/application/src/main/java/org/thingsboard/server/service/resource/DefaultTbResourceService.java b/application/src/main/java/org/thingsboard/server/service/resource/DefaultTbResourceService.java index c91ff91152..0389c5ae43 100644 --- a/application/src/main/java/org/thingsboard/server/service/resource/DefaultTbResourceService.java +++ b/application/src/main/java/org/thingsboard/server/service/resource/DefaultTbResourceService.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.service.resource; +import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.eclipse.leshan.core.model.DDFFileParser; @@ -38,6 +39,7 @@ import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.resource.ResourceService; +import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.entitiy.AbstractTbEntityService; import org.thingsboard.server.service.security.model.SecurityUser; @@ -57,6 +59,8 @@ import static org.thingsboard.server.dao.service.Validator.validateId; @Slf4j @Service +@TbCoreComponent +@AllArgsConstructor public class DefaultTbResourceService extends AbstractTbEntityService implements TbResourceService { private final ResourceService resourceService; From 25e654685a8a72b0faca4bc6daa77b61eda8f0bf Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Thu, 2 Jun 2022 03:59:12 +0300 Subject: [PATCH 746/798] refactoring: comments7_3 (Resource) --- .../server/service/resource/DefaultTbResourceService.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/resource/DefaultTbResourceService.java b/application/src/main/java/org/thingsboard/server/service/resource/DefaultTbResourceService.java index 0389c5ae43..b6ce83a4f4 100644 --- a/application/src/main/java/org/thingsboard/server/service/resource/DefaultTbResourceService.java +++ b/application/src/main/java/org/thingsboard/server/service/resource/DefaultTbResourceService.java @@ -15,7 +15,6 @@ */ package org.thingsboard.server.service.resource; -import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.eclipse.leshan.core.model.DDFFileParser; @@ -60,7 +59,6 @@ import static org.thingsboard.server.dao.service.Validator.validateId; @Slf4j @Service @TbCoreComponent -@AllArgsConstructor public class DefaultTbResourceService extends AbstractTbEntityService implements TbResourceService { private final ResourceService resourceService; From 370919ef48811395a39175adcc0937252281556d Mon Sep 17 00:00:00 2001 From: fe-dev Date: Thu, 2 Jun 2022 09:47:54 +0300 Subject: [PATCH 747/798] UI: fix trackby and add customtranslation title --- .../tenant-profile-queues.component.html | 4 ++-- .../queue/tenant-profile-queues.component.ts | 15 ++++++++++++++- .../components/queue/queue-form.component.ts | 3 --- .../tenant-profiles-table-config.resolver.ts | 19 +------------------ 4 files changed, 17 insertions(+), 24 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/profile/queue/tenant-profile-queues.component.html b/ui-ngx/src/app/modules/home/components/profile/queue/tenant-profile-queues.component.html index b755e02d8b..784f13d3f4 100644 --- a/ui-ngx/src/app/modules/home/components/profile/queue/tenant-profile-queues.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/queue/tenant-profile-queues.component.html @@ -24,7 +24,7 @@
- {{ queuesControl.value.name }} + {{ getTitle(queuesControl.value.name) }}
diff --git a/ui-ngx/src/app/modules/home/components/profile/queue/tenant-profile-queues.component.ts b/ui-ngx/src/app/modules/home/components/profile/queue/tenant-profile-queues.component.ts index 45a7d87eca..aa5325e974 100644 --- a/ui-ngx/src/app/modules/home/components/profile/queue/tenant-profile-queues.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/queue/tenant-profile-queues.component.ts @@ -56,6 +56,7 @@ export class TenantProfileQueuesComponent implements ControlValueAccessor, Valid tenantProfileQueuesFormGroup: FormGroup; newQueue = false; + idMap = []; private requiredValue: boolean; get required(): boolean { @@ -116,7 +117,13 @@ export class TenantProfileQueuesComponent implements ControlValueAccessor, Valid } const queuesControls: Array = []; if (queues) { - queues.forEach((queue) => { + queues.forEach((queue, index) => { + if (!queue.id) { + if (!this.idMap[index]) { + this.idMap.push(guid()); + } + queue.id = this.idMap[index]; + } queuesControls.push(this.fb.control(queue, [Validators.required])); }); } @@ -140,6 +147,7 @@ export class TenantProfileQueuesComponent implements ControlValueAccessor, Valid public removeQueue(index: number) { (this.tenantProfileQueuesFormGroup.get('queues') as FormArray).removeAt(index); + this.idMap.splice(index, 1); } public addQueue() { @@ -166,6 +174,7 @@ export class TenantProfileQueuesComponent implements ControlValueAccessor, Valid description: '' } }; + this.idMap.push(queue.id); this.newQueue = true; const queuesArray = this.tenantProfileQueuesFormGroup.get('queues') as FormArray; queuesArray.push(this.fb.control(queue, [])); @@ -175,6 +184,10 @@ export class TenantProfileQueuesComponent implements ControlValueAccessor, Valid } } + getTitle(value): string { + return this.utils.customTranslation(value, value); + } + public validate(c: AbstractControl): ValidationErrors | null { return this.tenantProfileQueuesFormGroup.valid ? null : { queues: { diff --git a/ui-ngx/src/app/modules/home/components/queue/queue-form.component.ts b/ui-ngx/src/app/modules/home/components/queue/queue-form.component.ts index cb2cf06b88..bb86b1edcb 100644 --- a/ui-ngx/src/app/modules/home/components/queue/queue-form.component.ts +++ b/ui-ngx/src/app/modules/home/components/queue/queue-form.component.ts @@ -159,9 +159,6 @@ export class QueueFormComponent implements ControlValueAccessor, OnInit, OnDestr if (isDefinedAndNotNull(this.modelValue)) { this.queueFormGroup.patchValue(this.modelValue, {emitEvent: false}); this.submitStrategyTypeChanged(); - if (!this.modelValue.name) { - this.queueFormGroup.get('name').enable({emitEvent: false}); - } } if (!this.disabled && !this.queueFormGroup.valid) { this.updateModel(); diff --git a/ui-ngx/src/app/modules/home/pages/tenant-profile/tenant-profiles-table-config.resolver.ts b/ui-ngx/src/app/modules/home/pages/tenant-profile/tenant-profiles-table-config.resolver.ts index d9f7efc602..cf6686d78f 100644 --- a/ui-ngx/src/app/modules/home/pages/tenant-profile/tenant-profiles-table-config.resolver.ts +++ b/ui-ngx/src/app/modules/home/pages/tenant-profile/tenant-profiles-table-config.resolver.ts @@ -86,12 +86,7 @@ export class TenantProfilesTableConfigResolver implements Resolve this.translate.instant('tenant-profile.delete-tenant-profiles-text'); this.config.entitiesFetchFunction = pageLink => this.tenantProfileService.getTenantProfiles(pageLink); - this.config.loadEntity = id => this.tenantProfileService.getTenantProfile(id.id).pipe( - map(tenantProfile => ({ - ...tenantProfile, - profileData: {...tenantProfile.profileData, queueConfiguration: this.addId(tenantProfile.profileData.queueConfiguration)}, - })) - ); + this.config.loadEntity = id => this.tenantProfileService.getTenantProfile(id.id); this.config.saveEntity = tenantProfile => this.tenantProfileService.saveTenantProfile(tenantProfile); this.config.deleteEntity = id => this.tenantProfileService.deleteTenantProfile(id.id); this.config.onEntityAction = action => this.onTenantProfileAction(action); @@ -100,18 +95,6 @@ export class TenantProfilesTableConfigResolver implements Resolve { - value.id = guid(); - queuesWithId.push(value); - }); - return queuesWithId; - } - return null; - } - resolve(): EntityTableConfig { this.config.tableTitle = this.translate.instant('tenant-profile.tenant-profiles'); From 484a62b964a234bcf72a0d38f024b22b131b56c0 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Thu, 2 Jun 2022 11:40:39 +0300 Subject: [PATCH 748/798] Updated rulenode-core-config --- .../resources/public/static/rulenode/rulenode-core-config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rule-engine/rule-engine-components/src/main/resources/public/static/rulenode/rulenode-core-config.js b/rule-engine/rule-engine-components/src/main/resources/public/static/rulenode/rulenode-core-config.js index f7ead71ec3..c4516988b4 100644 --- a/rule-engine/rule-engine-components/src/main/resources/public/static/rulenode/rulenode-core-config.js +++ b/rule-engine/rule-engine-components/src/main/resources/public/static/rulenode/rulenode-core-config.js @@ -1 +1 @@ -System.register(["@angular/core","@shared/public-api","@ngrx/store","@angular/forms","@angular/material/form-field","@angular/material/checkbox","@angular/flex-layout/flex","@ngx-translate/core","@angular/material/input","@angular/common","@angular/platform-browser","@angular/material/select","@angular/material/core","@angular/material/expansion","@shared/components/button/toggle-password.component","@shared/components/file-input.component","@shared/components/queue/queue-autocomplete.component","@core/public-api","@shared/components/js-func.component","@angular/material/button","@angular/cdk/keycodes","@angular/material/chips","@angular/material/icon","@angular/flex-layout/extended","@shared/components/entity/entity-type-select.component","@shared/components/entity/entity-select.component","@angular/cdk/coercion","@shared/components/tb-error.component","@angular/material/tooltip","rxjs/operators","@shared/components/tb-checkbox.component","@home/components/sms/sms-provider-configuration.component","@home/components/public-api","@shared/components/relation/relation-type-autocomplete.component","@shared/components/entity/entity-subtype-list.component","@home/components/relation/relation-filters.component","rxjs","@angular/material/autocomplete","@shared/pipe/highlight.pipe","@shared/components/entity/entity-autocomplete.component","@shared/components/entity/entity-type-list.component"],(function(e){"use strict";var t,o,r,a,n,l,i,s,m,u,p,d,f,c,g,x,y,b,h,C,F,L,v,I,N,T,q,k,M,S,A,G,D,V,E,P,R,w,O,H,U,K,B,j,z,_,Q,$,W,Y,J,Z,X,ee,te,oe,re,ae,ne,le,ie,se,me,ue,pe,de,fe,ce,ge,xe,ye,be,he,Ce,Fe,Le,ve,Ie;return{setters:[function(e){t=e,o=e.Component,r=e.Pipe,a=e.ViewChild,n=e.forwardRef,l=e.Input,i=e.NgModule},function(e){s=e.RuleNodeConfigurationComponent,m=e.AttributeScope,u=e.telemetryTypeTranslations,p=e.ServiceType,d=e.AlarmSeverity,f=e.alarmSeverityTranslations,c=e.EntitySearchDirection,g=e.entitySearchDirectionTranslations,x=e.EntityType,y=e.PageComponent,b=e.MessageType,h=e.messageTypeNames,C=e,F=e.SharedModule,L=e.AggregationType,v=e.aggregationTranslations,I=e.alarmStatusTranslations,N=e.AlarmStatus},function(e){T=e},function(e){q=e,k=e.Validators,M=e.NgControl,S=e.NG_VALUE_ACCESSOR,A=e.NG_VALIDATORS,G=e.FormControl},function(e){D=e},function(e){V=e},function(e){E=e},function(e){P=e},function(e){R=e},function(e){w=e,O=e.CommonModule},function(e){H=e},function(e){U=e},function(e){K=e},function(e){B=e},function(e){j=e},function(e){z=e},function(e){_=e},function(e){Q=e,$=e.isDefinedAndNotNull,W=e.isNotEmptyStr},function(e){Y=e},function(e){J=e},function(e){Z=e.ENTER,X=e.COMMA,ee=e.SEMICOLON},function(e){te=e},function(e){oe=e},function(e){re=e},function(e){ae=e},function(e){ne=e},function(e){le=e.coerceBooleanProperty},function(e){ie=e},function(e){se=e},function(e){me=e.distinctUntilChanged,ue=e.startWith,pe=e.map,de=e.mergeMap,fe=e.share},function(e){ce=e},function(e){ge=e},function(e){xe=e.HomeComponentsModule},function(e){ye=e},function(e){be=e},function(e){he=e},function(e){Ce=e.of},function(e){Fe=e},function(e){Le=e},function(e){ve=e},function(e){Ie=e}],execute:function(){class Ne extends s{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.emptyConfigForm}onConfigurationSet(e){this.emptyConfigForm=this.fb.group({})}}e("EmptyConfigComponent",Ne),Ne.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Ne,deps:[{token:T.Store},{token:q.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),Ne.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"12.0.0",version:"12.2.15",type:Ne,selector:"tb-node-empty-config",usesInheritance:!0,ngImport:t,template:"
",isInline:!0}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Ne,decorators:[{type:o,args:[{selector:"tb-node-empty-config",template:"
",styleUrls:[]}]}],ctorParameters:function(){return[{type:T.Store},{type:q.FormBuilder}]}});class Te{constructor(e){this.sanitizer=e}transform(e){return this.sanitizer.bypassSecurityTrustHtml(e)}}e("SafeHtmlPipe",Te),Te.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Te,deps:[{token:H.DomSanitizer}],target:t.ɵɵFactoryTarget.Pipe}),Te.ɵpipe=t.ɵɵngDeclarePipe({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Te,name:"safeHtml"}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Te,decorators:[{type:r,args:[{name:"safeHtml"}]}],ctorParameters:function(){return[{type:H.DomSanitizer}]}});class qe extends s{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.assignCustomerConfigForm}onConfigurationSet(e){this.assignCustomerConfigForm=this.fb.group({customerNamePattern:[e?e.customerNamePattern:null,[k.required,k.pattern(/.*\S.*/)]],createCustomerIfNotExists:[!!e&&e.createCustomerIfNotExists,[]],customerCacheExpiration:[e?e.customerCacheExpiration:null,[k.required,k.min(0)]]})}prepareOutputConfig(e){return e.customerNamePattern=e.customerNamePattern.trim(),e}}e("AssignCustomerConfigComponent",qe),qe.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:qe,deps:[{token:T.Store},{token:q.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),qe.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"12.0.0",version:"12.2.15",type:qe,selector:"tb-action-node-assign-to-customer-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.customer-name-pattern\n \n \n {{ \'tb.rulenode.customer-name-pattern-required\' | translate }}\n \n \n \n \n {{ \'tb.rulenode.create-customer-if-not-exists\' | translate }}\n \n \n tb.rulenode.customer-cache-expiration\n \n \n {{ \'tb.rulenode.customer-cache-expiration-required\' | translate }}\n \n \n {{ \'tb.rulenode.customer-cache-expiration-range\' | translate }}\n \n tb.rulenode.customer-cache-expiration-hint\n \n
\n',components:[{type:D.MatFormField,selector:"mat-form-field",inputs:["color","floatLabel","appearance","hideRequiredMarker","hintLabel"],exportAs:["matFormField"]},{type:V.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex","aria-label","aria-labelledby","id","labelPosition","name","required","checked","disabled","indeterminate","aria-describedby","value"],outputs:["change","indeterminateChange"],exportAs:["matCheckbox"]}],directives:[{type:E.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{type:q.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{type:q.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{type:D.MatLabel,selector:"mat-label"},{type:P.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{type:R.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["id","disabled","required","type","value","readonly","placeholder","errorStateMatcher","aria-describedby"],exportAs:["matInput"]},{type:q.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{type:q.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{type:q.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{type:q.FormControlName,selector:"[formControlName]",inputs:["disabled","formControlName","ngModel"],outputs:["ngModelChange"]},{type:w.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{type:D.MatError,selector:"mat-error",inputs:["id"]},{type:D.MatHint,selector:"mat-hint",inputs:["align","id"]},{type:E.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{type:q.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{type:q.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"}],pipes:{translate:P.TranslatePipe,safeHtml:Te}}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:qe,decorators:[{type:o,args:[{selector:"tb-action-node-assign-to-customer-config",templateUrl:"./assign-customer-config.component.html",styleUrls:[]}]}],ctorParameters:function(){return[{type:T.Store},{type:q.FormBuilder}]}});class ke extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.attributeScopes=Object.keys(m),this.telemetryTypeTranslationsMap=u}configForm(){return this.attributesConfigForm}onConfigurationSet(e){this.attributesConfigForm=this.fb.group({scope:[e?e.scope:null,[k.required]],notifyDevice:[!e||e.notifyDevice,[]]})}}var Me;e("AttributesConfigComponent",ke),ke.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:ke,deps:[{token:T.Store},{token:q.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),ke.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"12.0.0",version:"12.2.15",type:ke,selector:"tb-action-node-attributes-config",usesInheritance:!0,ngImport:t,template:'
\n \n attribute.attributes-scope\n \n \n {{ telemetryTypeTranslationsMap.get(scope) | translate }}\n \n \n \n
\n \n {{ \'tb.rulenode.notify-device\' | translate }}\n \n
tb.rulenode.notify-device-hint
\n
\n
\n',components:[{type:D.MatFormField,selector:"mat-form-field",inputs:["color","floatLabel","appearance","hideRequiredMarker","hintLabel"],exportAs:["matFormField"]},{type:U.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex"],exportAs:["matSelect"]},{type:K.MatOption,selector:"mat-option",exportAs:["matOption"]},{type:V.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex","aria-label","aria-labelledby","id","labelPosition","name","required","checked","disabled","indeterminate","aria-describedby","value"],outputs:["change","indeterminateChange"],exportAs:["matCheckbox"]}],directives:[{type:E.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{type:q.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{type:q.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{type:E.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{type:D.MatLabel,selector:"mat-label"},{type:P.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{type:q.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{type:q.FormControlName,selector:"[formControlName]",inputs:["disabled","formControlName","ngModel"],outputs:["ngModelChange"]},{type:q.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{type:w.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{type:w.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]}],pipes:{translate:P.TranslatePipe}}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:ke,decorators:[{type:o,args:[{selector:"tb-action-node-attributes-config",templateUrl:"./attributes-config.component.html",styleUrls:[]}]}],ctorParameters:function(){return[{type:T.Store},{type:q.FormBuilder}]}}),function(e){e.CUSTOMER="CUSTOMER",e.TENANT="TENANT",e.RELATED="RELATED",e.ALARM_ORIGINATOR="ALARM_ORIGINATOR"}(Me||(Me={}));const Se=new Map([[Me.CUSTOMER,"tb.rulenode.originator-customer"],[Me.TENANT,"tb.rulenode.originator-tenant"],[Me.RELATED,"tb.rulenode.originator-related"],[Me.ALARM_ORIGINATOR,"tb.rulenode.originator-alarm-originator"]]);var Ae;!function(e){e.CIRCLE="CIRCLE",e.POLYGON="POLYGON"}(Ae||(Ae={}));const Ge=new Map([[Ae.CIRCLE,"tb.rulenode.perimeter-circle"],[Ae.POLYGON,"tb.rulenode.perimeter-polygon"]]);var De;!function(e){e.MILLISECONDS="MILLISECONDS",e.SECONDS="SECONDS",e.MINUTES="MINUTES",e.HOURS="HOURS",e.DAYS="DAYS"}(De||(De={}));const Ve=new Map([[De.MILLISECONDS,"tb.rulenode.time-unit-milliseconds"],[De.SECONDS,"tb.rulenode.time-unit-seconds"],[De.MINUTES,"tb.rulenode.time-unit-minutes"],[De.HOURS,"tb.rulenode.time-unit-hours"],[De.DAYS,"tb.rulenode.time-unit-days"]]);var Ee;!function(e){e.METER="METER",e.KILOMETER="KILOMETER",e.FOOT="FOOT",e.MILE="MILE",e.NAUTICAL_MILE="NAUTICAL_MILE"}(Ee||(Ee={}));const Pe=new Map([[Ee.METER,"tb.rulenode.range-unit-meter"],[Ee.KILOMETER,"tb.rulenode.range-unit-kilometer"],[Ee.FOOT,"tb.rulenode.range-unit-foot"],[Ee.MILE,"tb.rulenode.range-unit-mile"],[Ee.NAUTICAL_MILE,"tb.rulenode.range-unit-nautical-mile"]]);var Re;!function(e){e.TITLE="TITLE",e.COUNTRY="COUNTRY",e.STATE="STATE",e.CITY="CITY",e.ZIP="ZIP",e.ADDRESS="ADDRESS",e.ADDRESS2="ADDRESS2",e.PHONE="PHONE",e.EMAIL="EMAIL",e.ADDITIONAL_INFO="ADDITIONAL_INFO"}(Re||(Re={}));const we=new Map([[Re.TITLE,"tb.rulenode.entity-details-title"],[Re.COUNTRY,"tb.rulenode.entity-details-country"],[Re.STATE,"tb.rulenode.entity-details-state"],[Re.CITY,"tb.rulenode.entity-details-city"],[Re.ZIP,"tb.rulenode.entity-details-zip"],[Re.ADDRESS,"tb.rulenode.entity-details-address"],[Re.ADDRESS2,"tb.rulenode.entity-details-address2"],[Re.PHONE,"tb.rulenode.entity-details-phone"],[Re.EMAIL,"tb.rulenode.entity-details-email"],[Re.ADDITIONAL_INFO,"tb.rulenode.entity-details-additional_info"]]);var Oe,He,Ue;!function(e){e.FIRST="FIRST",e.LAST="LAST",e.ALL="ALL"}(Oe||(Oe={})),function(e){e.ASC="ASC",e.DESC="DESC"}(He||(He={})),function(e){e.STANDARD="STANDARD",e.FIFO="FIFO"}(Ue||(Ue={}));const Ke=new Map([[Ue.STANDARD,"tb.rulenode.sqs-queue-standard"],[Ue.FIFO,"tb.rulenode.sqs-queue-fifo"]]),Be=["anonymous","basic","cert.PEM"],je=new Map([["anonymous","tb.rulenode.credentials-anonymous"],["basic","tb.rulenode.credentials-basic"],["cert.PEM","tb.rulenode.credentials-pem"]]),ze=["sas","cert.PEM"],_e=new Map([["sas","tb.rulenode.credentials-sas"],["cert.PEM","tb.rulenode.credentials-pem"]]);var Qe;!function(e){e.GET="GET",e.POST="POST",e.PUT="PUT",e.DELETE="DELETE"}(Qe||(Qe={}));const $e=["US-ASCII","ISO-8859-1","UTF-8","UTF-16BE","UTF-16LE","UTF-16"],We=new Map([["US-ASCII","tb.rulenode.charset-us-ascii"],["ISO-8859-1","tb.rulenode.charset-iso-8859-1"],["UTF-8","tb.rulenode.charset-utf-8"],["UTF-16BE","tb.rulenode.charset-utf-16be"],["UTF-16LE","tb.rulenode.charset-utf-16le"],["UTF-16","tb.rulenode.charset-utf-16"]]);class Ye extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.allAzureIotHubCredentialsTypes=ze,this.azureIotHubCredentialsTypeTranslationsMap=_e}configForm(){return this.azureIotHubConfigForm}onConfigurationSet(e){this.azureIotHubConfigForm=this.fb.group({topicPattern:[e?e.topicPattern:null,[k.required]],host:[e?e.host:null,[k.required]],port:[e?e.port:null,[k.required,k.min(1),k.max(65535)]],connectTimeoutSec:[e?e.connectTimeoutSec:null,[k.required,k.min(1),k.max(200)]],clientId:[e?e.clientId:null,[k.required]],cleanSession:[!!e&&e.cleanSession,[]],ssl:[!!e&&e.ssl,[]],credentials:this.fb.group({type:[e&&e.credentials?e.credentials.type:null,[k.required]],sasKey:[e&&e.credentials?e.credentials.sasKey:null,[]],caCert:[e&&e.credentials?e.credentials.caCert:null,[]],caCertFileName:[e&&e.credentials?e.credentials.caCertFileName:null,[]],privateKey:[e&&e.credentials?e.credentials.privateKey:null,[]],privateKeyFileName:[e&&e.credentials?e.credentials.privateKeyFileName:null,[]],cert:[e&&e.credentials?e.credentials.cert:null,[]],certFileName:[e&&e.credentials?e.credentials.certFileName:null,[]],password:[e&&e.credentials?e.credentials.password:null,[]]})})}prepareOutputConfig(e){const t=e.credentials.type;return"sas"===t&&(e.credentials={type:t,sasKey:e.credentials.sasKey,caCert:e.credentials.caCert,caCertFileName:e.credentials.caCertFileName}),e}validatorTriggers(){return["credentials.type"]}updateValidators(e){const t=this.azureIotHubConfigForm.get("credentials"),o=t.get("type").value;switch(e&&t.reset({type:o},{emitEvent:!1}),t.get("sasKey").setValidators([]),t.get("privateKey").setValidators([]),t.get("privateKeyFileName").setValidators([]),t.get("cert").setValidators([]),t.get("certFileName").setValidators([]),o){case"sas":t.get("sasKey").setValidators([k.required]);break;case"cert.PEM":t.get("privateKey").setValidators([k.required]),t.get("privateKeyFileName").setValidators([k.required]),t.get("cert").setValidators([k.required]),t.get("certFileName").setValidators([k.required])}t.get("sasKey").updateValueAndValidity({emitEvent:e}),t.get("privateKey").updateValueAndValidity({emitEvent:e}),t.get("privateKeyFileName").updateValueAndValidity({emitEvent:e}),t.get("cert").updateValueAndValidity({emitEvent:e}),t.get("certFileName").updateValueAndValidity({emitEvent:e})}}e("AzureIotHubConfigComponent",Ye),Ye.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Ye,deps:[{token:T.Store},{token:q.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),Ye.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"12.0.0",version:"12.2.15",type:Ye,selector:"tb-action-node-azure-iot-hub-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.topic\n \n \n {{ \'tb.rulenode.topic-required\' | translate }}\n \n \n \n \n tb.rulenode.hostname\n \n \n {{ \'tb.rulenode.hostname-required\' | translate }}\n \n \n \n tb.rulenode.device-id\n \n \n {{ \'tb.rulenode.device-id-required\' | translate }}\n \n \n \n \n \n tb.rulenode.credentials\n \n {{ azureIotHubCredentialsTypeTranslationsMap.get(azureIotHubConfigForm.get(\'credentials.type\').value) | translate }}\n \n \n
\n \n tb.rulenode.credentials-type\n \n \n {{ azureIotHubCredentialsTypeTranslationsMap.get(credentialsType) | translate }}\n \n \n \n {{ \'tb.rulenode.credentials-type-required\' | translate }}\n \n \n
\n \n \n \n \n tb.rulenode.sas-key\n \n \n \n {{ \'tb.rulenode.sas-key-required\' | translate }}\n \n \n \n \n \n \n \n \n \n \n \n \n \n tb.rulenode.private-key-password\n \n \n \n \n
\n
\n
\n
\n
\n',styles:[":host .tb-mqtt-credentials-panel-group{margin:0 6px}:host .tb-hint.client-id{margin-top:-1.25em;max-width:-moz-fit-content;max-width:fit-content}:host mat-checkbox{padding-bottom:16px}\n"],components:[{type:D.MatFormField,selector:"mat-form-field",inputs:["color","floatLabel","appearance","hideRequiredMarker","hintLabel"],exportAs:["matFormField"]},{type:B.MatExpansionPanel,selector:"mat-expansion-panel",inputs:["disabled","expanded","hideToggle","togglePosition"],outputs:["opened","closed","expandedChange","afterExpand","afterCollapse"],exportAs:["matExpansionPanel"]},{type:B.MatExpansionPanelHeader,selector:"mat-expansion-panel-header",inputs:["tabIndex","expandedHeight","collapsedHeight"]},{type:U.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex"],exportAs:["matSelect"]},{type:K.MatOption,selector:"mat-option",exportAs:["matOption"]},{type:j.TogglePasswordComponent,selector:"tb-toggle-password"},{type:z.FileInputComponent,selector:"tb-file-input",inputs:["label","accept","noFileText","inputId","allowedExtensions","dropLabel","contentConvertFunction","required","requiredAsError","disabled","existingFileName","readAsBinary","workFromFileObj","multipleFile"],outputs:["fileNameChanged"]}],directives:[{type:E.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{type:q.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{type:q.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{type:D.MatLabel,selector:"mat-label"},{type:P.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{type:R.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["id","disabled","required","type","value","readonly","placeholder","errorStateMatcher","aria-describedby"],exportAs:["matInput"]},{type:q.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{type:q.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{type:q.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{type:q.FormControlName,selector:"[formControlName]",inputs:["disabled","formControlName","ngModel"],outputs:["ngModelChange"]},{type:w.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{type:D.MatError,selector:"mat-error",inputs:["id"]},{type:D.MatHint,selector:"mat-hint",inputs:["align","id"]},{type:B.MatAccordion,selector:"mat-accordion",inputs:["multi","displayMode","togglePosition","hideToggle"],exportAs:["matAccordion"]},{type:B.MatExpansionPanelTitle,selector:"mat-panel-title"},{type:B.MatExpansionPanelDescription,selector:"mat-panel-description"},{type:q.FormGroupName,selector:"[formGroupName]",inputs:["formGroupName"]},{type:w.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{type:w.NgSwitch,selector:"[ngSwitch]",inputs:["ngSwitch"]},{type:w.NgSwitchCase,selector:"[ngSwitchCase]",inputs:["ngSwitchCase"]},{type:D.MatSuffix,selector:"[matSuffix]"}],pipes:{translate:P.TranslatePipe,safeHtml:Te}}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Ye,decorators:[{type:o,args:[{selector:"tb-action-node-azure-iot-hub-config",templateUrl:"./azure-iot-hub-config.component.html",styleUrls:["./mqtt-config.component.scss"]}]}],ctorParameters:function(){return[{type:T.Store},{type:q.FormBuilder}]}});class Je extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.serviceType=p.TB_RULE_ENGINE}configForm(){return this.checkPointConfigForm}onConfigurationSet(e){this.checkPointConfigForm=this.fb.group({queueId:[e?e.queueId:null,[k.required]]})}}e("CheckPointConfigComponent",Je),Je.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Je,deps:[{token:T.Store},{token:q.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),Je.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"12.0.0",version:"12.2.15",type:Je,selector:"tb-action-node-check-point-config",usesInheritance:!0,ngImport:t,template:'
\n \n \n
\n',components:[{type:_.QueueAutocompleteComponent,selector:"tb-queue-autocomplete",inputs:["labelText","requiredText","required","queueType","disabled"]}],directives:[{type:E.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{type:q.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{type:q.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{type:q.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{type:q.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{type:q.FormControlName,selector:"[formControlName]",inputs:["disabled","formControlName","ngModel"],outputs:["ngModelChange"]}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Je,decorators:[{type:o,args:[{selector:"tb-action-node-check-point-config",templateUrl:"./check-point-config.component.html",styleUrls:[]}]}],ctorParameters:function(){return[{type:T.Store},{type:q.FormBuilder}]}});class Ze extends s{constructor(e,t,o,r){super(e),this.store=e,this.fb=t,this.nodeScriptTestService=o,this.translate=r}configForm(){return this.clearAlarmConfigForm}onConfigurationSet(e){this.clearAlarmConfigForm=this.fb.group({alarmDetailsBuildJs:[e?e.alarmDetailsBuildJs:null,[k.required]],alarmType:[e?e.alarmType:null,[k.required]]})}testScript(){const e=this.clearAlarmConfigForm.get("alarmDetailsBuildJs").value;this.nodeScriptTestService.testNodeScript(e,"json",this.translate.instant("tb.rulenode.details"),"Details",["msg","metadata","msgType"],this.ruleNodeId,"rulenode/clear_alarm_node_script_fn").subscribe((e=>{e&&this.clearAlarmConfigForm.get("alarmDetailsBuildJs").setValue(e)}))}onValidate(){this.jsFuncComponent.validateOnSubmit()}}e("ClearAlarmConfigComponent",Ze),Ze.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Ze,deps:[{token:T.Store},{token:q.FormBuilder},{token:Q.NodeScriptTestService},{token:P.TranslateService}],target:t.ɵɵFactoryTarget.Component}),Ze.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"12.0.0",version:"12.2.15",type:Ze,selector:"tb-action-node-clear-alarm-config",viewQueries:[{propertyName:"jsFuncComponent",first:!0,predicate:["jsFuncComponent"],descendants:!0,static:!0}],usesInheritance:!0,ngImport:t,template:'
\n \n \n \n
\n \n
\n \n tb.rulenode.alarm-type\n \n \n {{ \'tb.rulenode.alarm-type-required\' | translate }}\n \n \n \n
\n',components:[{type:Y.JsFuncComponent,selector:"tb-js-func",inputs:["functionName","functionArgs","validationArgs","resultType","disabled","fillHeight","editorCompleter","globalVariables","disableUndefinedCheck","helpId","noValidate","required"]},{type:J.MatButton,selector:"button[mat-button], button[mat-raised-button], button[mat-icon-button], button[mat-fab], button[mat-mini-fab], button[mat-stroked-button], button[mat-flat-button]",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{type:D.MatFormField,selector:"mat-form-field",inputs:["color","floatLabel","appearance","hideRequiredMarker","hintLabel"],exportAs:["matFormField"]}],directives:[{type:E.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{type:q.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{type:q.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{type:P.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{type:q.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{type:q.FormControlName,selector:"[formControlName]",inputs:["disabled","formControlName","ngModel"],outputs:["ngModelChange"]},{type:D.MatLabel,selector:"mat-label"},{type:R.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["id","disabled","required","type","value","readonly","placeholder","errorStateMatcher","aria-describedby"],exportAs:["matInput"]},{type:q.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{type:q.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{type:w.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{type:D.MatError,selector:"mat-error",inputs:["id"]},{type:D.MatHint,selector:"mat-hint",inputs:["align","id"]}],pipes:{translate:P.TranslatePipe,safeHtml:Te}}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Ze,decorators:[{type:o,args:[{selector:"tb-action-node-clear-alarm-config",templateUrl:"./clear-alarm-config.component.html",styleUrls:[]}]}],ctorParameters:function(){return[{type:T.Store},{type:q.FormBuilder},{type:Q.NodeScriptTestService},{type:P.TranslateService}]},propDecorators:{jsFuncComponent:[{type:a,args:["jsFuncComponent",{static:!0}]}]}});class Xe extends s{constructor(e,t,o,r){super(e),this.store=e,this.fb=t,this.nodeScriptTestService=o,this.translate=r,this.alarmSeverities=Object.keys(d),this.alarmSeverityTranslationMap=f,this.separatorKeysCodes=[Z,X,ee]}configForm(){return this.createAlarmConfigForm}onConfigurationSet(e){this.createAlarmConfigForm=this.fb.group({alarmDetailsBuildJs:[e?e.alarmDetailsBuildJs:null,[k.required]],useMessageAlarmData:[!!e&&e.useMessageAlarmData,[]],overwriteAlarmDetails:[!!e&&e.overwriteAlarmDetails,[]],alarmType:[e?e.alarmType:null,[]],severity:[e?e.severity:null,[]],propagate:[!!e&&e.propagate,[]],relationTypes:[e?e.relationTypes:null,[]],propagateToOwner:[!!e&&e.propagateToOwner,[]],propagateToTenant:[!!e&&e.propagateToTenant,[]],dynamicSeverity:!1}),this.createAlarmConfigForm.get("dynamicSeverity").valueChanges.subscribe((e=>{e?this.createAlarmConfigForm.get("severity").patchValue("",{emitEvent:!1}):this.createAlarmConfigForm.get("severity").patchValue(this.alarmSeverities[0],{emitEvent:!1})}))}validatorTriggers(){return["useMessageAlarmData"]}updateValidators(e){this.createAlarmConfigForm.get("useMessageAlarmData").value?(this.createAlarmConfigForm.get("alarmType").setValidators([]),this.createAlarmConfigForm.get("severity").setValidators([])):(this.createAlarmConfigForm.get("alarmType").setValidators([k.required]),this.createAlarmConfigForm.get("severity").setValidators([k.required])),this.createAlarmConfigForm.get("alarmType").updateValueAndValidity({emitEvent:e}),this.createAlarmConfigForm.get("severity").updateValueAndValidity({emitEvent:e})}testScript(){const e=this.createAlarmConfigForm.get("alarmDetailsBuildJs").value;this.nodeScriptTestService.testNodeScript(e,"json",this.translate.instant("tb.rulenode.details"),"Details",["msg","metadata","msgType"],this.ruleNodeId,"rulenode/create_alarm_node_script_fn").subscribe((e=>{e&&this.createAlarmConfigForm.get("alarmDetailsBuildJs").setValue(e)}))}removeKey(e,t){const o=this.createAlarmConfigForm.get(t).value,r=o.indexOf(e);r>=0&&(o.splice(r,1),this.createAlarmConfigForm.get(t).setValue(o,{emitEvent:!0}))}addKey(e,t){const o=e.input;let r=e.value;if((r||"").trim()){r=r.trim();let e=this.createAlarmConfigForm.get(t).value;e&&-1!==e.indexOf(r)||(e||(e=[]),e.push(r),this.createAlarmConfigForm.get(t).setValue(e,{emitEvent:!0}))}o&&(o.value="")}onValidate(){const e=this.createAlarmConfigForm.get("useMessageAlarmData").value,t=this.createAlarmConfigForm.get("overwriteAlarmDetails").value;e&&!t||this.jsFuncComponent.validateOnSubmit()}}e("CreateAlarmConfigComponent",Xe),Xe.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Xe,deps:[{token:T.Store},{token:q.FormBuilder},{token:Q.NodeScriptTestService},{token:P.TranslateService}],target:t.ɵɵFactoryTarget.Component}),Xe.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"12.0.0",version:"12.2.15",type:Xe,selector:"tb-action-node-create-alarm-config",viewQueries:[{propertyName:"jsFuncComponent",first:!0,predicate:["jsFuncComponent"],descendants:!0,static:!0}],usesInheritance:!0,ngImport:t,template:'
\n \n {{ \'tb.rulenode.use-message-alarm-data\' | translate }}\n \n \n {{ \'tb.rulenode.overwrite-alarm-details\' | translate }}\n \n
\n \n \n \n
\n \n
\n
\n
\n \n tb.rulenode.alarm-type\n \n \n {{ \'tb.rulenode.alarm-type-required\' | translate }}\n \n \n \n \n {{ \'tb.rulenode.use-alarm-severity-pattern\' | translate }}\n \n \n tb.rulenode.alarm-severity\n \n \n {{ alarmSeverityTranslationMap.get(severity) | translate }}\n \n \n \n {{ \'tb.rulenode.alarm-severity-required\' | translate }}\n \n \n \n tb.rulenode.alarm-severity-pattern\n \n \n {{ \'tb.rulenode.alarm-severity-required\' | translate }}\n \n \n \n \n {{ \'tb.rulenode.propagate\' | translate }}\n \n
\n \n tb.rulenode.relation-types-list\n \n \n {{key}}\n close\n \n \n \n tb.rulenode.relation-types-list-hint\n \n
\n \n {{ \'tb.rulenode.propagate-to-owner\' | translate }}\n \n \n {{ \'tb.rulenode.propagate-to-tenant\' | translate }}\n \n
\n
\n',components:[{type:V.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex","aria-label","aria-labelledby","id","labelPosition","name","required","checked","disabled","indeterminate","aria-describedby","value"],outputs:["change","indeterminateChange"],exportAs:["matCheckbox"]},{type:Y.JsFuncComponent,selector:"tb-js-func",inputs:["functionName","functionArgs","validationArgs","resultType","disabled","fillHeight","editorCompleter","globalVariables","disableUndefinedCheck","helpId","noValidate","required"]},{type:J.MatButton,selector:"button[mat-button], button[mat-raised-button], button[mat-icon-button], button[mat-fab], button[mat-mini-fab], button[mat-stroked-button], button[mat-flat-button]",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{type:D.MatFormField,selector:"mat-form-field",inputs:["color","floatLabel","appearance","hideRequiredMarker","hintLabel"],exportAs:["matFormField"]},{type:U.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex"],exportAs:["matSelect"]},{type:K.MatOption,selector:"mat-option",exportAs:["matOption"]},{type:te.MatChipList,selector:"mat-chip-list",inputs:["aria-orientation","multiple","compareWith","value","required","placeholder","disabled","selectable","tabIndex","errorStateMatcher"],outputs:["change","valueChange"],exportAs:["matChipList"]},{type:oe.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]}],directives:[{type:E.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{type:q.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{type:q.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{type:q.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{type:q.FormControlName,selector:"[formControlName]",inputs:["disabled","formControlName","ngModel"],outputs:["ngModelChange"]},{type:re.DefaultShowHideDirective,selector:" [fxShow], [fxShow.print], [fxShow.xs], [fxShow.sm], [fxShow.md], [fxShow.lg], [fxShow.xl], [fxShow.lt-sm], [fxShow.lt-md], [fxShow.lt-lg], [fxShow.lt-xl], [fxShow.gt-xs], [fxShow.gt-sm], [fxShow.gt-md], [fxShow.gt-lg], [fxHide], [fxHide.print], [fxHide.xs], [fxHide.sm], [fxHide.md], [fxHide.lg], [fxHide.xl], [fxHide.lt-sm], [fxHide.lt-md], [fxHide.lt-lg], [fxHide.lt-xl], [fxHide.gt-xs], [fxHide.gt-sm], [fxHide.gt-md], [fxHide.gt-lg]",inputs:["fxShow","fxShow.print","fxShow.xs","fxShow.sm","fxShow.md","fxShow.lg","fxShow.xl","fxShow.lt-sm","fxShow.lt-md","fxShow.lt-lg","fxShow.lt-xl","fxShow.gt-xs","fxShow.gt-sm","fxShow.gt-md","fxShow.gt-lg","fxHide","fxHide.print","fxHide.xs","fxHide.sm","fxHide.md","fxHide.lg","fxHide.xl","fxHide.lt-sm","fxHide.lt-md","fxHide.lt-lg","fxHide.lt-xl","fxHide.gt-xs","fxHide.gt-sm","fxHide.gt-md","fxHide.gt-lg"]},{type:P.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{type:E.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{type:D.MatLabel,selector:"mat-label"},{type:R.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["id","disabled","required","type","value","readonly","placeholder","errorStateMatcher","aria-describedby"],exportAs:["matInput"]},{type:q.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{type:q.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{type:w.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{type:D.MatError,selector:"mat-error",inputs:["id"]},{type:D.MatHint,selector:"mat-hint",inputs:["align","id"]},{type:w.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{type:te.MatChip,selector:"mat-basic-chip, [mat-basic-chip], mat-chip, [mat-chip]",inputs:["color","disableRipple","tabIndex","selected","value","selectable","disabled","removable"],outputs:["selectionChange","destroyed","removed"],exportAs:["matChip"]},{type:te.MatChipRemove,selector:"[matChipRemove]"},{type:te.MatChipInput,selector:"input[matChipInputFor]",inputs:["matChipInputSeparatorKeyCodes","placeholder","id","matChipInputFor","matChipInputAddOnBlur","disabled"],outputs:["matChipInputTokenEnd"],exportAs:["matChipInput","matChipInputFor"]}],pipes:{translate:P.TranslatePipe,safeHtml:Te}}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Xe,decorators:[{type:o,args:[{selector:"tb-action-node-create-alarm-config",templateUrl:"./create-alarm-config.component.html",styleUrls:[]}]}],ctorParameters:function(){return[{type:T.Store},{type:q.FormBuilder},{type:Q.NodeScriptTestService},{type:P.TranslateService}]},propDecorators:{jsFuncComponent:[{type:a,args:["jsFuncComponent",{static:!0}]}]}});class et extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.directionTypes=Object.keys(c),this.directionTypeTranslations=g,this.entityType=x}configForm(){return this.createRelationConfigForm}onConfigurationSet(e){this.createRelationConfigForm=this.fb.group({direction:[e?e.direction:null,[k.required]],entityType:[e?e.entityType:null,[k.required]],entityNamePattern:[e?e.entityNamePattern:null,[]],entityTypePattern:[e?e.entityTypePattern:null,[]],relationType:[e?e.relationType:null,[k.required]],createEntityIfNotExists:[!!e&&e.createEntityIfNotExists,[]],removeCurrentRelations:[!!e&&e.removeCurrentRelations,[]],changeOriginatorToRelatedEntity:[!!e&&e.changeOriginatorToRelatedEntity,[]],entityCacheExpiration:[e?e.entityCacheExpiration:null,[k.required,k.min(0)]]})}validatorTriggers(){return["entityType"]}updateValidators(e){const t=this.createRelationConfigForm.get("entityType").value;t?this.createRelationConfigForm.get("entityNamePattern").setValidators([k.required,k.pattern(/.*\S.*/)]):this.createRelationConfigForm.get("entityNamePattern").setValidators([]),!t||t!==x.DEVICE&&t!==x.ASSET?this.createRelationConfigForm.get("entityTypePattern").setValidators([]):this.createRelationConfigForm.get("entityTypePattern").setValidators([k.required,k.pattern(/.*\S.*/)]),this.createRelationConfigForm.get("entityNamePattern").updateValueAndValidity({emitEvent:e}),this.createRelationConfigForm.get("entityTypePattern").updateValueAndValidity({emitEvent:e})}prepareOutputConfig(e){return e.entityNamePattern=e.entityNamePattern?e.entityNamePattern.trim():null,e.entityTypePattern=e.entityTypePattern?e.entityTypePattern.trim():null,e}}e("CreateRelationConfigComponent",et),et.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:et,deps:[{token:T.Store},{token:q.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),et.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"12.0.0",version:"12.2.15",type:et,selector:"tb-action-node-create-relation-config",usesInheritance:!0,ngImport:t,template:'
\n \n relation.direction\n \n \n {{ directionTypeTranslations.get(type) | translate }}\n \n \n \n
\n \n \n \n tb.rulenode.entity-name-pattern\n \n \n {{ \'tb.rulenode.entity-name-pattern-required\' | translate }}\n \n \n \n \n tb.rulenode.entity-type-pattern\n \n \n {{ \'tb.rulenode.entity-type-pattern-required\' | translate }}\n \n \n \n
\n \n tb.rulenode.relation-type-pattern\n \n \n {{ \'tb.rulenode.relation-type-pattern-required\' | translate }}\n \n \n \n
\n \n {{ \'tb.rulenode.create-entity-if-not-exists\' | translate }}\n \n
tb.rulenode.create-entity-if-not-exists-hint
\n
\n \n {{ \'tb.rulenode.remove-current-relations\' | translate }}\n \n
tb.rulenode.remove-current-relations-hint
\n \n {{ \'tb.rulenode.change-originator-to-related-entity\' | translate }}\n \n
tb.rulenode.change-originator-to-related-entity-hint
\n \n tb.rulenode.entity-cache-expiration\n \n \n {{ \'tb.rulenode.entity-cache-expiration-required\' | translate }}\n \n \n {{ \'tb.rulenode.entity-cache-expiration-range\' | translate }}\n \n tb.rulenode.entity-cache-expiration-hint\n \n
\n',components:[{type:D.MatFormField,selector:"mat-form-field",inputs:["color","floatLabel","appearance","hideRequiredMarker","hintLabel"],exportAs:["matFormField"]},{type:U.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex"],exportAs:["matSelect"]},{type:K.MatOption,selector:"mat-option",exportAs:["matOption"]},{type:ae.EntityTypeSelectComponent,selector:"tb-entity-type-select",inputs:["allowedEntityTypes","useAliasEntityTypes","showLabel","required","disabled"]},{type:V.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex","aria-label","aria-labelledby","id","labelPosition","name","required","checked","disabled","indeterminate","aria-describedby","value"],outputs:["change","indeterminateChange"],exportAs:["matCheckbox"]}],directives:[{type:E.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{type:q.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{type:q.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{type:D.MatLabel,selector:"mat-label"},{type:P.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{type:q.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{type:q.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{type:q.FormControlName,selector:"[formControlName]",inputs:["disabled","formControlName","ngModel"],outputs:["ngModelChange"]},{type:w.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{type:E.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{type:w.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{type:E.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{type:R.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["id","disabled","required","type","value","readonly","placeholder","errorStateMatcher","aria-describedby"],exportAs:["matInput"]},{type:q.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{type:D.MatError,selector:"mat-error",inputs:["id"]},{type:D.MatHint,selector:"mat-hint",inputs:["align","id"]},{type:q.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{type:q.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"}],pipes:{translate:P.TranslatePipe,safeHtml:Te}}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:et,decorators:[{type:o,args:[{selector:"tb-action-node-create-relation-config",templateUrl:"./create-relation-config.component.html",styleUrls:[]}]}],ctorParameters:function(){return[{type:T.Store},{type:q.FormBuilder}]}});class tt extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.directionTypes=Object.keys(c),this.directionTypeTranslations=g,this.entityType=x}configForm(){return this.deleteRelationConfigForm}onConfigurationSet(e){this.deleteRelationConfigForm=this.fb.group({deleteForSingleEntity:[!!e&&e.deleteForSingleEntity,[]],direction:[e?e.direction:null,[k.required]],entityType:[e?e.entityType:null,[]],entityNamePattern:[e?e.entityNamePattern:null,[]],relationType:[e?e.relationType:null,[k.required]],entityCacheExpiration:[e?e.entityCacheExpiration:null,[k.required,k.min(0)]]})}validatorTriggers(){return["deleteForSingleEntity","entityType"]}updateValidators(e){const t=this.deleteRelationConfigForm.get("deleteForSingleEntity").value,o=this.deleteRelationConfigForm.get("entityType").value;t?this.deleteRelationConfigForm.get("entityType").setValidators([k.required]):this.deleteRelationConfigForm.get("entityType").setValidators([]),t&&o?this.deleteRelationConfigForm.get("entityNamePattern").setValidators([k.required,k.pattern(/.*\S.*/)]):this.deleteRelationConfigForm.get("entityNamePattern").setValidators([]),this.deleteRelationConfigForm.get("entityType").updateValueAndValidity({emitEvent:!1}),this.deleteRelationConfigForm.get("entityNamePattern").updateValueAndValidity({emitEvent:e})}prepareOutputConfig(e){return e.entityNamePattern=e.entityNamePattern?e.entityNamePattern.trim():null,e}}e("DeleteRelationConfigComponent",tt),tt.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:tt,deps:[{token:T.Store},{token:q.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),tt.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"12.0.0",version:"12.2.15",type:tt,selector:"tb-action-node-delete-relation-config",usesInheritance:!0,ngImport:t,template:'
\n \n {{ \'tb.rulenode.delete-relation-to-specific-entity\' | translate }}\n \n
tb.rulenode.delete-relation-hint
\n \n relation.direction\n \n \n {{ directionTypeTranslations.get(type) | translate }}\n \n \n \n
\n \n \n \n tb.rulenode.entity-name-pattern\n \n \n {{ \'tb.rulenode.entity-name-pattern-required\' | translate }}\n \n \n \n
\n \n tb.rulenode.relation-type-pattern\n \n \n {{ \'tb.rulenode.relation-type-pattern-required\' | translate }}\n \n \n \n \n tb.rulenode.entity-cache-expiration\n \n \n {{ \'tb.rulenode.entity-cache-expiration-required\' | translate }}\n \n \n {{ \'tb.rulenode.entity-cache-expiration-range\' | translate }}\n \n tb.rulenode.entity-cache-expiration-hint\n \n
\n',components:[{type:V.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex","aria-label","aria-labelledby","id","labelPosition","name","required","checked","disabled","indeterminate","aria-describedby","value"],outputs:["change","indeterminateChange"],exportAs:["matCheckbox"]},{type:D.MatFormField,selector:"mat-form-field",inputs:["color","floatLabel","appearance","hideRequiredMarker","hintLabel"],exportAs:["matFormField"]},{type:U.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex"],exportAs:["matSelect"]},{type:K.MatOption,selector:"mat-option",exportAs:["matOption"]},{type:ae.EntityTypeSelectComponent,selector:"tb-entity-type-select",inputs:["allowedEntityTypes","useAliasEntityTypes","showLabel","required","disabled"]}],directives:[{type:E.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{type:q.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{type:q.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{type:q.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{type:q.FormControlName,selector:"[formControlName]",inputs:["disabled","formControlName","ngModel"],outputs:["ngModelChange"]},{type:P.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{type:D.MatLabel,selector:"mat-label"},{type:q.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{type:w.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{type:w.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{type:E.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{type:E.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{type:R.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["id","disabled","required","type","value","readonly","placeholder","errorStateMatcher","aria-describedby"],exportAs:["matInput"]},{type:q.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{type:D.MatError,selector:"mat-error",inputs:["id"]},{type:D.MatHint,selector:"mat-hint",inputs:["align","id"]},{type:q.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{type:q.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"}],pipes:{translate:P.TranslatePipe,safeHtml:Te}}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:tt,decorators:[{type:o,args:[{selector:"tb-action-node-delete-relation-config",templateUrl:"./delete-relation-config.component.html",styleUrls:[]}]}],ctorParameters:function(){return[{type:T.Store},{type:q.FormBuilder}]}});class ot extends s{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.deviceProfile}onConfigurationSet(e){this.deviceProfile=this.fb.group({persistAlarmRulesState:[!!e&&e.persistAlarmRulesState,k.required],fetchAlarmRulesStateOnStart:[!!e&&e.fetchAlarmRulesStateOnStart,k.required]})}}e("DeviceProfileConfigComponent",ot),ot.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:ot,deps:[{token:T.Store},{token:q.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),ot.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"12.0.0",version:"12.2.15",type:ot,selector:"tb-device-profile-config",usesInheritance:!0,ngImport:t,template:'
\n \n {{ \'tb.rulenode.persist-alarm-rules\' | translate }}\n \n \n {{ \'tb.rulenode.fetch-alarm-rules\' | translate }}\n \n
\n',components:[{type:V.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex","aria-label","aria-labelledby","id","labelPosition","name","required","checked","disabled","indeterminate","aria-describedby","value"],outputs:["change","indeterminateChange"],exportAs:["matCheckbox"]}],directives:[{type:E.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{type:q.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{type:q.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{type:E.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{type:q.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{type:q.FormControlName,selector:"[formControlName]",inputs:["disabled","formControlName","ngModel"],outputs:["ngModelChange"]}],pipes:{translate:P.TranslatePipe}}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:ot,decorators:[{type:o,args:[{selector:"tb-device-profile-config",templateUrl:"./device-profile-config.component.html",styleUrls:[]}]}],ctorParameters:function(){return[{type:T.Store},{type:q.FormBuilder}]}});class rt extends s{constructor(e,t,o,r){super(e),this.store=e,this.fb=t,this.nodeScriptTestService=o,this.translate=r}configForm(){return this.generatorConfigForm}onConfigurationSet(e){this.generatorConfigForm=this.fb.group({msgCount:[e?e.msgCount:null,[k.required,k.min(0)]],periodInSeconds:[e?e.periodInSeconds:null,[k.required,k.min(1)]],originator:[e?e.originator:null,[]],jsScript:[e?e.jsScript:null,[k.required]]})}prepareInputConfig(e){return e&&(e.originatorId&&e.originatorType?e.originator={id:e.originatorId,entityType:e.originatorType}:e.originator=null,delete e.originatorId,delete e.originatorType),e}prepareOutputConfig(e){return e.originator?(e.originatorId=e.originator.id,e.originatorType=e.originator.entityType):(e.originatorId=null,e.originatorType=null),delete e.originator,e}testScript(){const e=this.generatorConfigForm.get("jsScript").value;this.nodeScriptTestService.testNodeScript(e,"generate",this.translate.instant("tb.rulenode.generator"),"Generate",["prevMsg","prevMetadata","prevMsgType"],this.ruleNodeId,"rulenode/generator_node_script_fn").subscribe((e=>{e&&this.generatorConfigForm.get("jsScript").setValue(e)}))}onValidate(){this.jsFuncComponent.validateOnSubmit()}}e("GeneratorConfigComponent",rt),rt.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:rt,deps:[{token:T.Store},{token:q.FormBuilder},{token:Q.NodeScriptTestService},{token:P.TranslateService}],target:t.ɵɵFactoryTarget.Component}),rt.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"12.0.0",version:"12.2.15",type:rt,selector:"tb-action-node-generator-config",viewQueries:[{propertyName:"jsFuncComponent",first:!0,predicate:["jsFuncComponent"],descendants:!0,static:!0}],usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.message-count\n \n \n {{ \'tb.rulenode.message-count-required\' | translate }}\n \n \n {{ \'tb.rulenode.min-message-count-message\' | translate }}\n \n \n \n tb.rulenode.period-seconds\n \n \n {{ \'tb.rulenode.period-seconds-required\' | translate }}\n \n \n {{ \'tb.rulenode.min-period-seconds-message\' | translate }}\n \n \n
\n \n \n \n
\n \n \n \n
\n \n
\n
\n',components:[{type:D.MatFormField,selector:"mat-form-field",inputs:["color","floatLabel","appearance","hideRequiredMarker","hintLabel"],exportAs:["matFormField"]},{type:ne.EntitySelectComponent,selector:"tb-entity-select",inputs:["allowedEntityTypes","useAliasEntityTypes","required","disabled"]},{type:Y.JsFuncComponent,selector:"tb-js-func",inputs:["functionName","functionArgs","validationArgs","resultType","disabled","fillHeight","editorCompleter","globalVariables","disableUndefinedCheck","helpId","noValidate","required"]},{type:J.MatButton,selector:"button[mat-button], button[mat-raised-button], button[mat-icon-button], button[mat-fab], button[mat-mini-fab], button[mat-stroked-button], button[mat-flat-button]",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]}],directives:[{type:E.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{type:q.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{type:q.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{type:D.MatLabel,selector:"mat-label"},{type:P.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{type:q.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{type:q.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{type:R.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["id","disabled","required","type","value","readonly","placeholder","errorStateMatcher","aria-describedby"],exportAs:["matInput"]},{type:q.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{type:q.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{type:q.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{type:q.FormControlName,selector:"[formControlName]",inputs:["disabled","formControlName","ngModel"],outputs:["ngModelChange"]},{type:w.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{type:D.MatError,selector:"mat-error",inputs:["id"]}],pipes:{translate:P.TranslatePipe}}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:rt,decorators:[{type:o,args:[{selector:"tb-action-node-generator-config",templateUrl:"./generator-config.component.html",styleUrls:[]}]}],ctorParameters:function(){return[{type:T.Store},{type:q.FormBuilder},{type:Q.NodeScriptTestService},{type:P.TranslateService}]},propDecorators:{jsFuncComponent:[{type:a,args:["jsFuncComponent",{static:!0}]}]}});class at extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.perimeterType=Ae,this.perimeterTypes=Object.keys(Ae),this.perimeterTypeTranslationMap=Ge,this.rangeUnits=Object.keys(Ee),this.rangeUnitTranslationMap=Pe,this.timeUnits=Object.keys(De),this.timeUnitsTranslationMap=Ve}configForm(){return this.geoActionConfigForm}onConfigurationSet(e){this.geoActionConfigForm=this.fb.group({latitudeKeyName:[e?e.latitudeKeyName:null,[k.required]],longitudeKeyName:[e?e.longitudeKeyName:null,[k.required]],perimeterType:[e?e.perimeterType:null,[k.required]],fetchPerimeterInfoFromMessageMetadata:[!!e&&e.fetchPerimeterInfoFromMessageMetadata,[]],perimeterKeyName:[e?e.perimeterKeyName:null,[]],centerLatitude:[e?e.centerLatitude:null,[]],centerLongitude:[e?e.centerLatitude:null,[]],range:[e?e.range:null,[]],rangeUnit:[e?e.rangeUnit:null,[]],polygonsDefinition:[e?e.polygonsDefinition:null,[]],minInsideDuration:[e?e.minInsideDuration:null,[k.required,k.min(1),k.max(2147483647)]],minInsideDurationTimeUnit:[e?e.minInsideDurationTimeUnit:null,[k.required]],minOutsideDuration:[e?e.minOutsideDuration:null,[k.required,k.min(1),k.max(2147483647)]],minOutsideDurationTimeUnit:[e?e.minOutsideDurationTimeUnit:null,[k.required]]})}validatorTriggers(){return["fetchPerimeterInfoFromMessageMetadata","perimeterType"]}updateValidators(e){const t=this.geoActionConfigForm.get("fetchPerimeterInfoFromMessageMetadata").value,o=this.geoActionConfigForm.get("perimeterType").value;t?this.geoActionConfigForm.get("perimeterKeyName").setValidators([k.required]):this.geoActionConfigForm.get("perimeterKeyName").setValidators([]),t||o!==Ae.CIRCLE?(this.geoActionConfigForm.get("centerLatitude").setValidators([]),this.geoActionConfigForm.get("centerLongitude").setValidators([]),this.geoActionConfigForm.get("range").setValidators([]),this.geoActionConfigForm.get("rangeUnit").setValidators([])):(this.geoActionConfigForm.get("centerLatitude").setValidators([k.required,k.min(-90),k.max(90)]),this.geoActionConfigForm.get("centerLongitude").setValidators([k.required,k.min(-180),k.max(180)]),this.geoActionConfigForm.get("range").setValidators([k.required,k.min(0)]),this.geoActionConfigForm.get("rangeUnit").setValidators([k.required])),t||o!==Ae.POLYGON?this.geoActionConfigForm.get("polygonsDefinition").setValidators([]):this.geoActionConfigForm.get("polygonsDefinition").setValidators([k.required]),this.geoActionConfigForm.get("perimeterKeyName").updateValueAndValidity({emitEvent:e}),this.geoActionConfigForm.get("centerLatitude").updateValueAndValidity({emitEvent:e}),this.geoActionConfigForm.get("centerLongitude").updateValueAndValidity({emitEvent:e}),this.geoActionConfigForm.get("range").updateValueAndValidity({emitEvent:e}),this.geoActionConfigForm.get("rangeUnit").updateValueAndValidity({emitEvent:e}),this.geoActionConfigForm.get("polygonsDefinition").updateValueAndValidity({emitEvent:e})}}e("GpsGeoActionConfigComponent",at),at.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:at,deps:[{token:T.Store},{token:q.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),at.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"12.0.0",version:"12.2.15",type:at,selector:"tb-action-node-gps-geofencing-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.latitude-key-name\n \n \n {{ \'tb.rulenode.latitude-key-name-required\' | translate }}\n \n \n \n tb.rulenode.longitude-key-name\n \n \n {{ \'tb.rulenode.longitude-key-name-required\' | translate }}\n \n \n \n tb.rulenode.perimeter-type\n \n \n {{ perimeterTypeTranslationMap.get(type) | translate }}\n \n \n \n \n {{ \'tb.rulenode.fetch-perimeter-info-from-message-metadata\' | translate }}\n \n \n tb.rulenode.perimeter-key-name\n \n \n {{ \'tb.rulenode.perimeter-key-name-required\' | translate }}\n \n \n
\n
\n \n tb.rulenode.circle-center-latitude\n \n \n {{ \'tb.rulenode.circle-center-latitude-required\' | translate }}\n \n \n \n tb.rulenode.circle-center-longitude\n \n \n {{ \'tb.rulenode.circle-center-longitude-required\' | translate }}\n \n \n
\n
\n \n tb.rulenode.range\n \n \n {{ \'tb.rulenode.range-required\' | translate }}\n \n \n \n tb.rulenode.range-units\n \n \n {{ rangeUnitTranslationMap.get(type) | translate }}\n \n \n \n
\n
\n
\n \n tb.rulenode.polygon-definition\n \n \n {{ \'tb.rulenode.polygon-definition-required\' | translate }}\n \n \n
\n
\n \n tb.rulenode.min-inside-duration\n \n \n {{ \'tb.rulenode.min-inside-duration-value-required\' | translate }}\n \n \n {{ \'tb.rulenode.time-value-range\' | translate }}\n \n \n {{ \'tb.rulenode.time-value-range\' | translate }}\n \n \n \n tb.rulenode.min-inside-duration-time-unit\n \n \n {{ timeUnitsTranslationMap.get(timeUnit) | translate }}\n \n \n \n
\n
\n \n tb.rulenode.min-outside-duration\n \n \n {{ \'tb.rulenode.min-outside-duration-value-required\' | translate }}\n \n \n {{ \'tb.rulenode.time-value-range\' | translate }}\n \n \n {{ \'tb.rulenode.time-value-range\' | translate }}\n \n \n \n tb.rulenode.min-outside-duration-time-unit\n \n \n {{ timeUnitsTranslationMap.get(timeUnit) | translate }}\n \n \n \n
\n
\n',components:[{type:D.MatFormField,selector:"mat-form-field",inputs:["color","floatLabel","appearance","hideRequiredMarker","hintLabel"],exportAs:["matFormField"]},{type:U.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex"],exportAs:["matSelect"]},{type:K.MatOption,selector:"mat-option",exportAs:["matOption"]},{type:V.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex","aria-label","aria-labelledby","id","labelPosition","name","required","checked","disabled","indeterminate","aria-describedby","value"],outputs:["change","indeterminateChange"],exportAs:["matCheckbox"]}],directives:[{type:E.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{type:q.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{type:q.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{type:D.MatLabel,selector:"mat-label"},{type:P.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{type:R.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["id","disabled","required","type","value","readonly","placeholder","errorStateMatcher","aria-describedby"],exportAs:["matInput"]},{type:q.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{type:q.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{type:q.FormControlName,selector:"[formControlName]",inputs:["disabled","formControlName","ngModel"],outputs:["ngModelChange"]},{type:q.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{type:w.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{type:D.MatError,selector:"mat-error",inputs:["id"]},{type:E.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{type:w.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{type:E.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{type:q.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{type:q.MaxValidator,selector:"input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]",inputs:["max"]},{type:q.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"}],pipes:{translate:P.TranslatePipe}}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:at,decorators:[{type:o,args:[{selector:"tb-action-node-gps-geofencing-config",templateUrl:"./gps-geo-action-config.component.html",styleUrls:[]}]}],ctorParameters:function(){return[{type:T.Store},{type:q.FormBuilder}]}});class nt extends y{constructor(e,t,o,r){super(e),this.store=e,this.translate=t,this.injector=o,this.fb=r,this.propagateChange=null,this.valueChangeSubscription=null}get required(){return this.requiredValue}set required(e){this.requiredValue=le(e)}ngOnInit(){this.ngControl=this.injector.get(M),null!=this.ngControl&&(this.ngControl.valueAccessor=this),this.kvListFormGroup=this.fb.group({}),this.kvListFormGroup.addControl("keyVals",this.fb.array([]))}keyValsFormArray(){return this.kvListFormGroup.get("keyVals")}registerOnChange(e){this.propagateChange=e}registerOnTouched(e){}setDisabledState(e){this.disabled=e,this.disabled?this.kvListFormGroup.disable({emitEvent:!1}):this.kvListFormGroup.enable({emitEvent:!1})}writeValue(e){this.valueChangeSubscription&&this.valueChangeSubscription.unsubscribe();const t=[];if(e)for(const o of Object.keys(e))Object.prototype.hasOwnProperty.call(e,o)&&t.push(this.fb.group({key:[o,[k.required]],value:[e[o],[k.required]]}));this.kvListFormGroup.setControl("keyVals",this.fb.array(t)),this.valueChangeSubscription=this.kvListFormGroup.valueChanges.subscribe((()=>{this.updateModel()}))}removeKeyVal(e){this.kvListFormGroup.get("keyVals").removeAt(e)}addKeyVal(){this.kvListFormGroup.get("keyVals").push(this.fb.group({key:["",[k.required]],value:["",[k.required]]}))}validate(e){return!this.kvListFormGroup.get("keyVals").value.length&&this.required?{kvMapRequired:!0}:this.kvListFormGroup.valid?null:{kvFieldsRequired:!0}}updateModel(){const e=this.kvListFormGroup.get("keyVals").value;if(this.required&&!e.length||!this.kvListFormGroup.valid)this.propagateChange(null);else{const t={};e.forEach((e=>{t[e.key]=e.value})),this.propagateChange(t)}}}e("KvMapConfigComponent",nt),nt.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:nt,deps:[{token:T.Store},{token:P.TranslateService},{token:t.Injector},{token:q.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),nt.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"12.0.0",version:"12.2.15",type:nt,selector:"tb-kv-map-config",inputs:{disabled:"disabled",requiredText:"requiredText",keyText:"keyText",keyRequiredText:"keyRequiredText",valText:"valText",valRequiredText:"valRequiredText",hintText:"hintText",required:"required"},providers:[{provide:S,useExisting:n((()=>nt)),multi:!0},{provide:A,useExisting:n((()=>nt)),multi:!0}],usesInheritance:!0,ngImport:t,template:'
\n
\n {{ keyText | translate }}\n {{ valText | translate }}\n \n
\n
\n
\n \n \n \n \n {{ keyRequiredText | translate }}\n \n \n \n \n \n \n {{ valRequiredText | translate }}\n \n \n \n
\n
\n
\n \n
\n \n
\n
\n',styles:[":host .tb-kv-map-config{margin-bottom:16px}:host .tb-kv-map-config .header{padding-left:5px;padding-right:5px;padding-bottom:5px}:host .tb-kv-map-config .header .cell{padding-left:5px;padding-right:5px;color:#0000008a;font-size:12px;font-weight:700;white-space:nowrap}:host .tb-kv-map-config .body{padding-left:5px;padding-right:5px;padding-bottom:20px;max-height:300px;overflow:auto}:host .tb-kv-map-config .body .cell{padding-left:5px;padding-right:5px}:host ::ng-deep .tb-kv-map-config .body mat-form-field.cell{margin:0}:host ::ng-deep .tb-kv-map-config .body mat-form-field.cell .mat-form-field-infix{border-top:0}:host ::ng-deep .tb-kv-map-config .body button.mat-button{margin:0;align-self:baseline}\n"],components:[{type:D.MatFormField,selector:"mat-form-field",inputs:["color","floatLabel","appearance","hideRequiredMarker","hintLabel"],exportAs:["matFormField"]},{type:J.MatButton,selector:"button[mat-button], button[mat-raised-button], button[mat-icon-button], button[mat-fab], button[mat-mini-fab], button[mat-stroked-button], button[mat-flat-button]",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{type:oe.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{type:ie.TbErrorComponent,selector:"tb-error",inputs:["error"]}],directives:[{type:E.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{type:q.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{type:q.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{type:E.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{type:E.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{type:re.DefaultShowHideDirective,selector:" [fxShow], [fxShow.print], [fxShow.xs], [fxShow.sm], [fxShow.md], [fxShow.lg], [fxShow.xl], [fxShow.lt-sm], [fxShow.lt-md], [fxShow.lt-lg], [fxShow.lt-xl], [fxShow.gt-xs], [fxShow.gt-sm], [fxShow.gt-md], [fxShow.gt-lg], [fxHide], [fxHide.print], [fxHide.xs], [fxHide.sm], [fxHide.md], [fxHide.lg], [fxHide.xl], [fxHide.lt-sm], [fxHide.lt-md], [fxHide.lt-lg], [fxHide.lt-xl], [fxHide.gt-xs], [fxHide.gt-sm], [fxHide.gt-md], [fxHide.gt-lg]",inputs:["fxShow","fxShow.print","fxShow.xs","fxShow.sm","fxShow.md","fxShow.lg","fxShow.xl","fxShow.lt-sm","fxShow.lt-md","fxShow.lt-lg","fxShow.lt-xl","fxShow.gt-xs","fxShow.gt-sm","fxShow.gt-md","fxShow.gt-lg","fxHide","fxHide.print","fxHide.xs","fxHide.sm","fxHide.md","fxHide.lg","fxHide.xl","fxHide.lt-sm","fxHide.lt-md","fxHide.lt-lg","fxHide.lt-xl","fxHide.gt-xs","fxHide.gt-sm","fxHide.gt-md","fxHide.gt-lg"]},{type:w.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{type:E.DefaultLayoutAlignDirective,selector:" [fxLayoutAlign], [fxLayoutAlign.xs], [fxLayoutAlign.sm], [fxLayoutAlign.md], [fxLayoutAlign.lg], [fxLayoutAlign.xl], [fxLayoutAlign.lt-sm], [fxLayoutAlign.lt-md], [fxLayoutAlign.lt-lg], [fxLayoutAlign.lt-xl], [fxLayoutAlign.gt-xs], [fxLayoutAlign.gt-sm], [fxLayoutAlign.gt-md], [fxLayoutAlign.gt-lg]",inputs:["fxLayoutAlign","fxLayoutAlign.xs","fxLayoutAlign.sm","fxLayoutAlign.md","fxLayoutAlign.lg","fxLayoutAlign.xl","fxLayoutAlign.lt-sm","fxLayoutAlign.lt-md","fxLayoutAlign.lt-lg","fxLayoutAlign.lt-xl","fxLayoutAlign.gt-xs","fxLayoutAlign.gt-sm","fxLayoutAlign.gt-md","fxLayoutAlign.gt-lg"]},{type:q.FormArrayName,selector:"[formArrayName]",inputs:["formArrayName"]},{type:D.MatLabel,selector:"mat-label"},{type:R.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["id","disabled","required","type","value","readonly","placeholder","errorStateMatcher","aria-describedby"],exportAs:["matInput"]},{type:q.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{type:q.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{type:q.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{type:q.FormControlDirective,selector:"[formControl]",inputs:["disabled","formControl","ngModel"],outputs:["ngModelChange"],exportAs:["ngForm"]},{type:w.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{type:D.MatError,selector:"mat-error",inputs:["id"]},{type:se.MatTooltip,selector:"[matTooltip]",exportAs:["matTooltip"]}],pipes:{translate:P.TranslatePipe,async:w.AsyncPipe,safeHtml:Te}}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:nt,decorators:[{type:o,args:[{selector:"tb-kv-map-config",templateUrl:"./kv-map-config.component.html",styleUrls:["./kv-map-config.component.scss"],providers:[{provide:S,useExisting:n((()=>nt)),multi:!0},{provide:A,useExisting:n((()=>nt)),multi:!0}]}]}],ctorParameters:function(){return[{type:T.Store},{type:P.TranslateService},{type:t.Injector},{type:q.FormBuilder}]},propDecorators:{disabled:[{type:l}],requiredText:[{type:l}],keyText:[{type:l}],keyRequiredText:[{type:l}],valText:[{type:l}],valRequiredText:[{type:l}],hintText:[{type:l}],required:[{type:l}]}});class lt extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.ackValues=["all","-1","0","1"],this.ToByteStandartCharsetTypesValues=$e,this.ToByteStandartCharsetTypeTranslationMap=We}configForm(){return this.kafkaConfigForm}onConfigurationSet(e){this.kafkaConfigForm=this.fb.group({topicPattern:[e?e.topicPattern:null,[k.required]],bootstrapServers:[e?e.bootstrapServers:null,[k.required]],retries:[e?e.retries:null,[k.min(0)]],batchSize:[e?e.batchSize:null,[k.min(0)]],linger:[e?e.linger:null,[k.min(0)]],bufferMemory:[e?e.bufferMemory:null,[k.min(0)]],acks:[e?e.acks:null,[k.required]],keySerializer:[e?e.keySerializer:null,[k.required]],valueSerializer:[e?e.valueSerializer:null,[k.required]],otherProperties:[e?e.otherProperties:null,[]],addMetadataKeyValuesAsKafkaHeaders:[!!e&&e.addMetadataKeyValuesAsKafkaHeaders,[]],kafkaHeadersCharset:[e?e.kafkaHeadersCharset:null,[]]})}validatorTriggers(){return["addMetadataKeyValuesAsKafkaHeaders"]}updateValidators(e){this.kafkaConfigForm.get("addMetadataKeyValuesAsKafkaHeaders").value?this.kafkaConfigForm.get("kafkaHeadersCharset").setValidators([k.required]):this.kafkaConfigForm.get("kafkaHeadersCharset").setValidators([]),this.kafkaConfigForm.get("kafkaHeadersCharset").updateValueAndValidity({emitEvent:e})}}e("KafkaConfigComponent",lt),lt.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:lt,deps:[{token:T.Store},{token:q.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),lt.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"12.0.0",version:"12.2.15",type:lt,selector:"tb-action-node-kafka-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.topic-pattern\n \n \n {{ \'tb.rulenode.topic-pattern-required\' | translate }}\n \n \n \n \n tb.rulenode.bootstrap-servers\n \n \n {{ \'tb.rulenode.bootstrap-servers-required\' | translate }}\n \n \n \n tb.rulenode.retries\n \n \n {{ \'tb.rulenode.min-retries-message\' | translate }}\n \n \n \n tb.rulenode.batch-size-bytes\n \n \n {{ \'tb.rulenode.min-batch-size-bytes-message\' | translate }}\n \n \n \n tb.rulenode.linger-ms\n \n \n {{ \'tb.rulenode.min-linger-ms-message\' | translate }}\n \n \n \n tb.rulenode.buffer-memory-bytes\n \n \n {{ \'tb.rulenode.min-buffer-memory-bytes-message\' | translate }}\n \n \n \n tb.rulenode.acks\n \n \n {{ ackValue }}\n \n \n \n \n tb.rulenode.key-serializer\n \n \n {{ \'tb.rulenode.key-serializer-required\' | translate }}\n \n \n \n tb.rulenode.value-serializer\n \n \n {{ \'tb.rulenode.value-serializer-required\' | translate }}\n \n \n \n \n \n \n {{ \'tb.rulenode.add-metadata-key-values-as-kafka-headers\' | translate }}\n \n
tb.rulenode.add-metadata-key-values-as-kafka-headers-hint
\n \n tb.rulenode.charset-encoding\n \n \n {{ ToByteStandartCharsetTypeTranslationMap.get(charset) | translate }}\n \n \n \n
\n',components:[{type:D.MatFormField,selector:"mat-form-field",inputs:["color","floatLabel","appearance","hideRequiredMarker","hintLabel"],exportAs:["matFormField"]},{type:U.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex"],exportAs:["matSelect"]},{type:K.MatOption,selector:"mat-option",exportAs:["matOption"]},{type:nt,selector:"tb-kv-map-config",inputs:["disabled","requiredText","keyText","keyRequiredText","valText","valRequiredText","hintText","required"]},{type:V.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex","aria-label","aria-labelledby","id","labelPosition","name","required","checked","disabled","indeterminate","aria-describedby","value"],outputs:["change","indeterminateChange"],exportAs:["matCheckbox"]}],directives:[{type:E.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{type:q.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{type:q.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{type:D.MatLabel,selector:"mat-label"},{type:P.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{type:R.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["id","disabled","required","type","value","readonly","placeholder","errorStateMatcher","aria-describedby"],exportAs:["matInput"]},{type:q.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{type:q.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{type:q.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{type:q.FormControlName,selector:"[formControlName]",inputs:["disabled","formControlName","ngModel"],outputs:["ngModelChange"]},{type:w.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{type:D.MatError,selector:"mat-error",inputs:["id"]},{type:D.MatHint,selector:"mat-hint",inputs:["align","id"]},{type:q.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{type:q.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{type:w.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{type:E.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]}],pipes:{translate:P.TranslatePipe,safeHtml:Te}}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:lt,decorators:[{type:o,args:[{selector:"tb-action-node-kafka-config",templateUrl:"./kafka-config.component.html",styleUrls:[]}]}],ctorParameters:function(){return[{type:T.Store},{type:q.FormBuilder}]}});class it extends s{constructor(e,t,o,r){super(e),this.store=e,this.fb=t,this.nodeScriptTestService=o,this.translate=r}configForm(){return this.logConfigForm}onConfigurationSet(e){this.logConfigForm=this.fb.group({jsScript:[e?e.jsScript:null,[k.required]]})}testScript(){const e=this.logConfigForm.get("jsScript").value;this.nodeScriptTestService.testNodeScript(e,"string",this.translate.instant("tb.rulenode.to-string"),"ToString",["msg","metadata","msgType"],this.ruleNodeId,"rulenode/log_node_script_fn").subscribe((e=>{e&&this.logConfigForm.get("jsScript").setValue(e)}))}onValidate(){this.jsFuncComponent.validateOnSubmit()}}e("LogConfigComponent",it),it.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:it,deps:[{token:T.Store},{token:q.FormBuilder},{token:Q.NodeScriptTestService},{token:P.TranslateService}],target:t.ɵɵFactoryTarget.Component}),it.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"12.0.0",version:"12.2.15",type:it,selector:"tb-action-node-log-config",viewQueries:[{propertyName:"jsFuncComponent",first:!0,predicate:["jsFuncComponent"],descendants:!0,static:!0}],usesInheritance:!0,ngImport:t,template:'
\n \n \n \n
\n \n
\n
\n',components:[{type:Y.JsFuncComponent,selector:"tb-js-func",inputs:["functionName","functionArgs","validationArgs","resultType","disabled","fillHeight","editorCompleter","globalVariables","disableUndefinedCheck","helpId","noValidate","required"]},{type:J.MatButton,selector:"button[mat-button], button[mat-raised-button], button[mat-icon-button], button[mat-fab], button[mat-mini-fab], button[mat-stroked-button], button[mat-flat-button]",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]}],directives:[{type:E.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{type:q.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{type:q.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{type:P.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{type:q.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{type:q.FormControlName,selector:"[formControlName]",inputs:["disabled","formControlName","ngModel"],outputs:["ngModelChange"]}],pipes:{translate:P.TranslatePipe}}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:it,decorators:[{type:o,args:[{selector:"tb-action-node-log-config",templateUrl:"./log-config.component.html",styleUrls:[]}]}],ctorParameters:function(){return[{type:T.Store},{type:q.FormBuilder},{type:Q.NodeScriptTestService},{type:P.TranslateService}]},propDecorators:{jsFuncComponent:[{type:a,args:["jsFuncComponent",{static:!0}]}]}});class st extends y{constructor(e,t){super(e),this.store=e,this.fb=t,this.subscriptions=[],this.disableCertPemCredentials=!1,this.passwordFieldRquired=!0,this.allCredentialsTypes=Be,this.credentialsTypeTranslationsMap=je,this.propagateChange=null}get required(){return this.requiredValue}set required(e){this.requiredValue=le(e)}ngOnInit(){this.credentialsConfigFormGroup=this.fb.group({type:[null,[k.required]],username:[null,[]],password:[null,[]],caCert:[null,[]],caCertFileName:[null,[]],privateKey:[null,[]],privateKeyFileName:[null,[]],cert:[null,[]],certFileName:[null,[]]}),this.subscriptions.push(this.credentialsConfigFormGroup.valueChanges.pipe(me()).subscribe((()=>{this.updateView()}))),this.subscriptions.push(this.credentialsConfigFormGroup.get("type").valueChanges.subscribe((()=>{this.credentialsTypeChanged()})))}ngOnChanges(e){for(const t of Object.keys(e)){const o=e[t];if(!o.firstChange&&o.currentValue!==o.previousValue&&o.currentValue&&"disableCertPemCredentials"===t){"cert.PEM"===this.credentialsConfigFormGroup.get("type").value&&setTimeout((()=>{this.credentialsConfigFormGroup.get("type").patchValue("anonymous",{emitEvent:!0})}))}}}ngOnDestroy(){this.subscriptions.forEach((e=>e.unsubscribe()))}writeValue(e){$(e)&&(this.credentialsConfigFormGroup.reset(e,{emitEvent:!1}),this.updateValidators(!1))}setDisabledState(e){e?this.credentialsConfigFormGroup.disable():(this.credentialsConfigFormGroup.enable(),this.updateValidators())}updateView(){let e=this.credentialsConfigFormGroup.value;const t=e.type;switch(t){case"anonymous":e={type:t};break;case"basic":e={type:t,username:e.username,password:e.password};break;case"cert.PEM":delete e.username}this.propagateChange(e)}registerOnChange(e){this.propagateChange=e}registerOnTouched(e){}validate(e){return this.credentialsConfigFormGroup.valid?null:{credentialsConfig:{valid:!1}}}credentialsTypeChanged(){this.credentialsConfigFormGroup.patchValue({username:null,password:null,caCert:null,caCertFileName:null,privateKey:null,privateKeyFileName:null,cert:null,certFileName:null}),this.updateValidators()}updateValidators(e=!1){const t=this.credentialsConfigFormGroup.get("type").value;switch(e&&this.credentialsConfigFormGroup.reset({type:t},{emitEvent:!1}),this.credentialsConfigFormGroup.setValidators([]),this.credentialsConfigFormGroup.get("username").setValidators([]),this.credentialsConfigFormGroup.get("password").setValidators([]),t){case"anonymous":break;case"basic":this.credentialsConfigFormGroup.get("username").setValidators([k.required]),this.credentialsConfigFormGroup.get("password").setValidators(this.passwordFieldRquired?[k.required]:[]);break;case"cert.PEM":this.credentialsConfigFormGroup.setValidators([this.requiredFilesSelected(k.required,[["caCert","caCertFileName"],["privateKey","privateKeyFileName","cert","certFileName"]])])}this.credentialsConfigFormGroup.get("username").updateValueAndValidity({emitEvent:e}),this.credentialsConfigFormGroup.get("password").updateValueAndValidity({emitEvent:e}),this.credentialsConfigFormGroup.updateValueAndValidity({emitEvent:e})}requiredFilesSelected(e,t=null){return o=>{t||(t=[Object.keys(o.controls)]);return(null==o?void 0:o.controls)&&t.some((t=>t.every((t=>!e(o.controls[t])))))?null:{notAllRequiredFilesSelected:!0}}}}e("CredentialsConfigComponent",st),st.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:st,deps:[{token:T.Store},{token:q.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),st.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"12.0.0",version:"12.2.15",type:st,selector:"tb-credentials-config",inputs:{required:"required",disableCertPemCredentials:"disableCertPemCredentials",passwordFieldRquired:"passwordFieldRquired"},providers:[{provide:S,useExisting:n((()=>st)),multi:!0},{provide:A,useExisting:n((()=>st)),multi:!0}],usesInheritance:!0,usesOnChanges:!0,ngImport:t,template:'
\n \n \n tb.rulenode.credentials\n \n {{ credentialsTypeTranslationsMap.get(credentialsConfigFormGroup.get(\'type\').value) | translate }}\n \n \n \n \n tb.rulenode.credentials-type\n \n \n {{ credentialsTypeTranslationsMap.get(credentialsType) | translate }}\n \n \n \n {{ \'tb.rulenode.credentials-type-required\' | translate }}\n \n \n
\n \n \n \n \n tb.rulenode.username\n \n \n {{ \'tb.rulenode.username-required\' | translate }}\n \n \n \n tb.rulenode.password\n \n \n \n {{ \'tb.rulenode.password-required\' | translate }}\n \n \n \n \n
{{ \'tb.rulenode.credentials-pem-hint\' | translate }}
\n \n \n \n \n \n \n \n tb.rulenode.private-key-password\n \n \n \n
\n
\n
\n
\n
\n',components:[{type:B.MatExpansionPanel,selector:"mat-expansion-panel",inputs:["disabled","expanded","hideToggle","togglePosition"],outputs:["opened","closed","expandedChange","afterExpand","afterCollapse"],exportAs:["matExpansionPanel"]},{type:B.MatExpansionPanelHeader,selector:"mat-expansion-panel-header",inputs:["tabIndex","expandedHeight","collapsedHeight"]},{type:D.MatFormField,selector:"mat-form-field",inputs:["color","floatLabel","appearance","hideRequiredMarker","hintLabel"],exportAs:["matFormField"]},{type:U.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex"],exportAs:["matSelect"]},{type:K.MatOption,selector:"mat-option",exportAs:["matOption"]},{type:j.TogglePasswordComponent,selector:"tb-toggle-password"},{type:z.FileInputComponent,selector:"tb-file-input",inputs:["label","accept","noFileText","inputId","allowedExtensions","dropLabel","contentConvertFunction","required","requiredAsError","disabled","existingFileName","readAsBinary","workFromFileObj","multipleFile"],outputs:["fileNameChanged"]}],directives:[{type:E.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{type:q.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{type:q.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{type:B.MatExpansionPanelTitle,selector:"mat-panel-title"},{type:P.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{type:B.MatExpansionPanelDescription,selector:"mat-panel-description"},{type:B.MatExpansionPanelContent,selector:"ng-template[matExpansionPanelContent]"},{type:D.MatLabel,selector:"mat-label"},{type:q.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{type:q.FormControlName,selector:"[formControlName]",inputs:["disabled","formControlName","ngModel"],outputs:["ngModelChange"]},{type:q.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{type:w.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{type:w.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{type:D.MatError,selector:"mat-error",inputs:["id"]},{type:w.NgSwitch,selector:"[ngSwitch]",inputs:["ngSwitch"]},{type:w.NgSwitchCase,selector:"[ngSwitchCase]",inputs:["ngSwitchCase"]},{type:R.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["id","disabled","required","type","value","readonly","placeholder","errorStateMatcher","aria-describedby"],exportAs:["matInput"]},{type:q.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{type:D.MatSuffix,selector:"[matSuffix]"}],pipes:{translate:P.TranslatePipe}}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:st,decorators:[{type:o,args:[{selector:"tb-credentials-config",templateUrl:"./credentials-config.component.html",styleUrls:[],providers:[{provide:S,useExisting:n((()=>st)),multi:!0},{provide:A,useExisting:n((()=>st)),multi:!0}]}]}],ctorParameters:function(){return[{type:T.Store},{type:q.FormBuilder}]},propDecorators:{required:[{type:l}],disableCertPemCredentials:[{type:l}],passwordFieldRquired:[{type:l}]}});class mt extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.subscriptions=[]}configForm(){return this.mqttConfigForm}onConfigurationSet(e){this.mqttConfigForm=this.fb.group({topicPattern:[e?e.topicPattern:null,[k.required]],host:[e?e.host:null,[k.required]],port:[e?e.port:null,[k.required,k.min(1),k.max(65535)]],connectTimeoutSec:[e?e.connectTimeoutSec:null,[k.required,k.min(1),k.max(200)]],clientId:[e?e.clientId:null,[]],appendClientIdSuffix:[{value:!!e&&e.appendClientIdSuffix,disabled:!(e&&W(e.clientId))},[]],cleanSession:[!!e&&e.cleanSession,[]],ssl:[!!e&&e.ssl,[]],credentials:[e?e.credentials:null,[]]}),this.subscriptions.push(this.mqttConfigForm.get("clientId").valueChanges.subscribe((e=>{W(e)?this.mqttConfigForm.get("appendClientIdSuffix").enable({emitEvent:!1}):this.mqttConfigForm.get("appendClientIdSuffix").disable({emitEvent:!1})})))}ngOnDestroy(){this.subscriptions.forEach((e=>e.unsubscribe()))}}e("MqttConfigComponent",mt),mt.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:mt,deps:[{token:T.Store},{token:q.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),mt.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"12.0.0",version:"12.2.15",type:mt,selector:"tb-action-node-mqtt-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.topic-pattern\n \n \n {{ \'tb.rulenode.topic-pattern-required\' | translate }}\n \n \n \n
\n \n tb.rulenode.host\n \n \n {{ \'tb.rulenode.host-required\' | translate }}\n \n \n \n tb.rulenode.port\n \n \n {{ \'tb.rulenode.port-required\' | translate }}\n \n \n {{ \'tb.rulenode.port-range\' | translate }}\n \n \n {{ \'tb.rulenode.port-range\' | translate }}\n \n \n \n tb.rulenode.connect-timeout\n \n \n {{ \'tb.rulenode.connect-timeout-required\' | translate }}\n \n \n {{ \'tb.rulenode.connect-timeout-range\' | translate }}\n \n \n {{ \'tb.rulenode.connect-timeout-range\' | translate }}\n \n \n
\n \n tb.rulenode.client-id\n \n \n
tb.rulenode.client-id-hint
\n \n {{ \'tb.rulenode.append-client-id-suffix\' | translate }}\n \n
{{ "tb.rulenode.client-id-suffix-hint" | translate }}
\n \n {{ \'tb.rulenode.clean-session\' | translate }}\n \n \n {{ \'tb.rulenode.enable-ssl\' | translate }}\n \n \n
\n',styles:[":host .tb-mqtt-credentials-panel-group{margin:0 6px}:host .tb-hint.client-id{margin-top:-1.25em;max-width:-moz-fit-content;max-width:fit-content}:host mat-checkbox{padding-bottom:16px}\n"],components:[{type:D.MatFormField,selector:"mat-form-field",inputs:["color","floatLabel","appearance","hideRequiredMarker","hintLabel"],exportAs:["matFormField"]},{type:V.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex","aria-label","aria-labelledby","id","labelPosition","name","required","checked","disabled","indeterminate","aria-describedby","value"],outputs:["change","indeterminateChange"],exportAs:["matCheckbox"]},{type:st,selector:"tb-credentials-config",inputs:["required","disableCertPemCredentials","passwordFieldRquired"]}],directives:[{type:E.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{type:q.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{type:q.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{type:D.MatLabel,selector:"mat-label"},{type:P.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{type:R.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["id","disabled","required","type","value","readonly","placeholder","errorStateMatcher","aria-describedby"],exportAs:["matInput"]},{type:q.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{type:q.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{type:q.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{type:q.FormControlName,selector:"[formControlName]",inputs:["disabled","formControlName","ngModel"],outputs:["ngModelChange"]},{type:w.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{type:D.MatError,selector:"mat-error",inputs:["id"]},{type:D.MatHint,selector:"mat-hint",inputs:["align","id"]},{type:E.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{type:E.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{type:q.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{type:q.MaxValidator,selector:"input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]",inputs:["max"]},{type:q.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"}],pipes:{translate:P.TranslatePipe,safeHtml:Te}}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:mt,decorators:[{type:o,args:[{selector:"tb-action-node-mqtt-config",templateUrl:"./mqtt-config.component.html",styleUrls:["./mqtt-config.component.scss"]}]}],ctorParameters:function(){return[{type:T.Store},{type:q.FormBuilder}]}});class ut extends s{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.msgCountConfigForm}onConfigurationSet(e){this.msgCountConfigForm=this.fb.group({interval:[e?e.interval:null,[k.required,k.min(1)]],telemetryPrefix:[e?e.telemetryPrefix:null,[k.required]]})}}e("MsgCountConfigComponent",ut),ut.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:ut,deps:[{token:T.Store},{token:q.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),ut.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"12.0.0",version:"12.2.15",type:ut,selector:"tb-action-node-msg-count-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.interval-seconds\n \n \n {{ \'tb.rulenode.interval-seconds-required\' | translate }}\n \n \n {{ \'tb.rulenode.min-interval-seconds-message\' | translate }}\n \n \n \n tb.rulenode.output-timeseries-key-prefix\n \n \n {{ \'tb.rulenode.output-timeseries-key-prefix-required\' | translate }}\n \n \n
\n',components:[{type:D.MatFormField,selector:"mat-form-field",inputs:["color","floatLabel","appearance","hideRequiredMarker","hintLabel"],exportAs:["matFormField"]}],directives:[{type:E.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{type:q.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{type:q.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{type:D.MatLabel,selector:"mat-label"},{type:P.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{type:q.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{type:q.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{type:R.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["id","disabled","required","type","value","readonly","placeholder","errorStateMatcher","aria-describedby"],exportAs:["matInput"]},{type:q.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{type:q.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{type:q.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{type:q.FormControlName,selector:"[formControlName]",inputs:["disabled","formControlName","ngModel"],outputs:["ngModelChange"]},{type:w.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{type:D.MatError,selector:"mat-error",inputs:["id"]}],pipes:{translate:P.TranslatePipe}}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:ut,decorators:[{type:o,args:[{selector:"tb-action-node-msg-count-config",templateUrl:"./msg-count-config.component.html",styleUrls:[]}]}],ctorParameters:function(){return[{type:T.Store},{type:q.FormBuilder}]}});class pt extends s{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.msgDelayConfigForm}onConfigurationSet(e){this.msgDelayConfigForm=this.fb.group({useMetadataPeriodInSecondsPatterns:[!!e&&e.useMetadataPeriodInSecondsPatterns,[]],periodInSeconds:[e?e.periodInSeconds:null,[]],periodInSecondsPattern:[e?e.periodInSecondsPattern:null,[]],maxPendingMsgs:[e?e.maxPendingMsgs:null,[k.required,k.min(1),k.max(1e5)]]})}validatorTriggers(){return["useMetadataPeriodInSecondsPatterns"]}updateValidators(e){this.msgDelayConfigForm.get("useMetadataPeriodInSecondsPatterns").value?(this.msgDelayConfigForm.get("periodInSecondsPattern").setValidators([k.required]),this.msgDelayConfigForm.get("periodInSeconds").setValidators([])):(this.msgDelayConfigForm.get("periodInSecondsPattern").setValidators([]),this.msgDelayConfigForm.get("periodInSeconds").setValidators([k.required,k.min(0)])),this.msgDelayConfigForm.get("periodInSecondsPattern").updateValueAndValidity({emitEvent:e}),this.msgDelayConfigForm.get("periodInSeconds").updateValueAndValidity({emitEvent:e})}}e("MsgDelayConfigComponent",pt),pt.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:pt,deps:[{token:T.Store},{token:q.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),pt.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"12.0.0",version:"12.2.15",type:pt,selector:"tb-action-node-msg-delay-config",usesInheritance:!0,ngImport:t,template:'
\n \n {{ \'tb.rulenode.use-metadata-period-in-seconds-patterns\' | translate }}\n \n
tb.rulenode.use-metadata-period-in-seconds-patterns-hint
\n \n tb.rulenode.period-seconds\n \n \n {{ \'tb.rulenode.period-seconds-required\' | translate }}\n \n \n {{ \'tb.rulenode.min-period-0-seconds-message\' | translate }}\n \n \n \n \n tb.rulenode.period-in-seconds-pattern\n \n \n {{ \'tb.rulenode.period-in-seconds-pattern-required\' | translate }}\n \n \n \n \n \n tb.rulenode.max-pending-messages\n \n \n {{ \'tb.rulenode.max-pending-messages-required\' | translate }}\n \n \n {{ \'tb.rulenode.max-pending-messages-range\' | translate }}\n \n \n {{ \'tb.rulenode.max-pending-messages-range\' | translate }}\n \n \n
\n',components:[{type:V.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex","aria-label","aria-labelledby","id","labelPosition","name","required","checked","disabled","indeterminate","aria-describedby","value"],outputs:["change","indeterminateChange"],exportAs:["matCheckbox"]},{type:D.MatFormField,selector:"mat-form-field",inputs:["color","floatLabel","appearance","hideRequiredMarker","hintLabel"],exportAs:["matFormField"]}],directives:[{type:E.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{type:q.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{type:q.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{type:q.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{type:q.FormControlName,selector:"[formControlName]",inputs:["disabled","formControlName","ngModel"],outputs:["ngModelChange"]},{type:P.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{type:w.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{type:D.MatLabel,selector:"mat-label"},{type:q.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{type:q.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{type:R.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["id","disabled","required","type","value","readonly","placeholder","errorStateMatcher","aria-describedby"],exportAs:["matInput"]},{type:q.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{type:q.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{type:D.MatError,selector:"mat-error",inputs:["id"]},{type:D.MatHint,selector:"mat-hint",inputs:["align","id"]},{type:q.MaxValidator,selector:"input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]",inputs:["max"]}],pipes:{translate:P.TranslatePipe,safeHtml:Te}}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:pt,decorators:[{type:o,args:[{selector:"tb-action-node-msg-delay-config",templateUrl:"./msg-delay-config.component.html",styleUrls:[]}]}],ctorParameters:function(){return[{type:T.Store},{type:q.FormBuilder}]}});class dt extends s{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.pubSubConfigForm}onConfigurationSet(e){this.pubSubConfigForm=this.fb.group({projectId:[e?e.projectId:null,[k.required]],topicName:[e?e.topicName:null,[k.required]],serviceAccountKey:[e?e.serviceAccountKey:null,[k.required]],serviceAccountKeyFileName:[e?e.serviceAccountKeyFileName:null,[k.required]],messageAttributes:[e?e.messageAttributes:null,[]]})}}e("PubSubConfigComponent",dt),dt.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:dt,deps:[{token:T.Store},{token:q.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),dt.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"12.0.0",version:"12.2.15",type:dt,selector:"tb-action-node-pub-sub-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.gcp-project-id\n \n \n {{ \'tb.rulenode.gcp-project-id-required\' | translate }}\n \n \n \n tb.rulenode.pubsub-topic-name\n \n \n {{ \'tb.rulenode.pubsub-topic-name-required\' | translate }}\n \n \n \n \n \n
\n \n \n
\n',components:[{type:D.MatFormField,selector:"mat-form-field",inputs:["color","floatLabel","appearance","hideRequiredMarker","hintLabel"],exportAs:["matFormField"]},{type:z.FileInputComponent,selector:"tb-file-input",inputs:["label","accept","noFileText","inputId","allowedExtensions","dropLabel","contentConvertFunction","required","requiredAsError","disabled","existingFileName","readAsBinary","workFromFileObj","multipleFile"],outputs:["fileNameChanged"]},{type:nt,selector:"tb-kv-map-config",inputs:["disabled","requiredText","keyText","keyRequiredText","valText","valRequiredText","hintText","required"]}],directives:[{type:E.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{type:q.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{type:q.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{type:D.MatLabel,selector:"mat-label"},{type:P.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{type:R.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["id","disabled","required","type","value","readonly","placeholder","errorStateMatcher","aria-describedby"],exportAs:["matInput"]},{type:q.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{type:q.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{type:q.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{type:q.FormControlName,selector:"[formControlName]",inputs:["disabled","formControlName","ngModel"],outputs:["ngModelChange"]},{type:w.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{type:D.MatError,selector:"mat-error",inputs:["id"]}],pipes:{translate:P.TranslatePipe,safeHtml:Te}}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:dt,decorators:[{type:o,args:[{selector:"tb-action-node-pub-sub-config",templateUrl:"./pubsub-config.component.html",styleUrls:[]}]}],ctorParameters:function(){return[{type:T.Store},{type:q.FormBuilder}]}});class ft extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.attributeScopes=Object.keys(m),this.telemetryTypeTranslationsMap=u}configForm(){return this.pushToCloudConfigForm}onConfigurationSet(e){this.pushToCloudConfigForm=this.fb.group({scope:[e?e.scope:null,[k.required]]})}}e("PushToCloudConfigComponent",ft),ft.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:ft,deps:[{token:T.Store},{token:q.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),ft.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"12.0.0",version:"12.2.15",type:ft,selector:"tb-action-node-push-to-cloud-config",usesInheritance:!0,ngImport:t,template:'
\n \n attribute.attributes-scope\n \n \n {{ telemetryTypeTranslationsMap.get(scope) | translate }}\n \n \n \n
\n',components:[{type:D.MatFormField,selector:"mat-form-field",inputs:["color","floatLabel","appearance","hideRequiredMarker","hintLabel"],exportAs:["matFormField"]},{type:U.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex"],exportAs:["matSelect"]},{type:K.MatOption,selector:"mat-option",exportAs:["matOption"]}],directives:[{type:E.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{type:q.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{type:q.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{type:E.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{type:D.MatLabel,selector:"mat-label"},{type:P.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{type:q.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{type:q.FormControlName,selector:"[formControlName]",inputs:["disabled","formControlName","ngModel"],outputs:["ngModelChange"]},{type:q.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{type:w.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]}],pipes:{translate:P.TranslatePipe}}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:ft,decorators:[{type:o,args:[{selector:"tb-action-node-push-to-cloud-config",templateUrl:"./push-to-cloud-config.component.html",styleUrls:[]}]}],ctorParameters:function(){return[{type:T.Store},{type:q.FormBuilder}]}});class ct extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.attributeScopes=Object.keys(m),this.telemetryTypeTranslationsMap=u}configForm(){return this.pushToEdgeConfigForm}onConfigurationSet(e){this.pushToEdgeConfigForm=this.fb.group({scope:[e?e.scope:null,[k.required]]})}}e("PushToEdgeConfigComponent",ct),ct.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:ct,deps:[{token:T.Store},{token:q.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),ct.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"12.0.0",version:"12.2.15",type:ct,selector:"tb-action-node-push-to-edge-config",usesInheritance:!0,ngImport:t,template:'
\n \n attribute.attributes-scope\n \n \n {{ telemetryTypeTranslationsMap.get(scope) | translate }}\n \n \n \n
\n',components:[{type:D.MatFormField,selector:"mat-form-field",inputs:["color","floatLabel","appearance","hideRequiredMarker","hintLabel"],exportAs:["matFormField"]},{type:U.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex"],exportAs:["matSelect"]},{type:K.MatOption,selector:"mat-option",exportAs:["matOption"]}],directives:[{type:E.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{type:q.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{type:q.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{type:E.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{type:D.MatLabel,selector:"mat-label"},{type:P.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{type:q.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{type:q.FormControlName,selector:"[formControlName]",inputs:["disabled","formControlName","ngModel"],outputs:["ngModelChange"]},{type:q.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{type:w.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]}],pipes:{translate:P.TranslatePipe}}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:ct,decorators:[{type:o,args:[{selector:"tb-action-node-push-to-edge-config",templateUrl:"./push-to-edge-config.component.html",styleUrls:[]}]}],ctorParameters:function(){return[{type:T.Store},{type:q.FormBuilder}]}});class gt extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.messageProperties=[null,"BASIC","TEXT_PLAIN","MINIMAL_BASIC","MINIMAL_PERSISTENT_BASIC","PERSISTENT_BASIC","PERSISTENT_TEXT_PLAIN"]}configForm(){return this.rabbitMqConfigForm}onConfigurationSet(e){this.rabbitMqConfigForm=this.fb.group({exchangeNamePattern:[e?e.exchangeNamePattern:null,[]],routingKeyPattern:[e?e.routingKeyPattern:null,[]],messageProperties:[e?e.messageProperties:null,[]],host:[e?e.host:null,[k.required]],port:[e?e.port:null,[k.required,k.min(1),k.max(65535)]],virtualHost:[e?e.virtualHost:null,[]],username:[e?e.username:null,[]],password:[e?e.password:null,[]],automaticRecoveryEnabled:[!!e&&e.automaticRecoveryEnabled,[]],connectionTimeout:[e?e.connectionTimeout:null,[k.min(0)]],handshakeTimeout:[e?e.handshakeTimeout:null,[k.min(0)]],clientProperties:[e?e.clientProperties:null,[]]})}}e("RabbitMqConfigComponent",gt),gt.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:gt,deps:[{token:T.Store},{token:q.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),gt.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"12.0.0",version:"12.2.15",type:gt,selector:"tb-action-node-rabbit-mq-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.exchange-name-pattern\n \n \n \n tb.rulenode.routing-key-pattern\n \n \n \n tb.rulenode.message-properties\n \n \n {{ property }}\n \n \n \n
\n \n tb.rulenode.host\n \n \n {{ \'tb.rulenode.host-required\' | translate }}\n \n \n \n tb.rulenode.port\n \n \n {{ \'tb.rulenode.port-required\' | translate }}\n \n \n {{ \'tb.rulenode.port-range\' | translate }}\n \n \n {{ \'tb.rulenode.port-range\' | translate }}\n \n \n
\n \n tb.rulenode.virtual-host\n \n \n \n tb.rulenode.username\n \n \n \n tb.rulenode.password\n \n \n \n \n {{ \'tb.rulenode.automatic-recovery\' | translate }}\n \n \n tb.rulenode.connection-timeout-ms\n \n \n {{ \'tb.rulenode.min-connection-timeout-ms-message\' | translate }}\n \n \n \n tb.rulenode.handshake-timeout-ms\n \n \n {{ \'tb.rulenode.min-handshake-timeout-ms-message\' | translate }}\n \n \n \n \n \n
\n',components:[{type:D.MatFormField,selector:"mat-form-field",inputs:["color","floatLabel","appearance","hideRequiredMarker","hintLabel"],exportAs:["matFormField"]},{type:U.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex"],exportAs:["matSelect"]},{type:K.MatOption,selector:"mat-option",exportAs:["matOption"]},{type:j.TogglePasswordComponent,selector:"tb-toggle-password"},{type:V.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex","aria-label","aria-labelledby","id","labelPosition","name","required","checked","disabled","indeterminate","aria-describedby","value"],outputs:["change","indeterminateChange"],exportAs:["matCheckbox"]},{type:nt,selector:"tb-kv-map-config",inputs:["disabled","requiredText","keyText","keyRequiredText","valText","valRequiredText","hintText","required"]}],directives:[{type:E.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{type:q.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{type:q.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{type:D.MatLabel,selector:"mat-label"},{type:P.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{type:R.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["id","disabled","required","type","value","readonly","placeholder","errorStateMatcher","aria-describedby"],exportAs:["matInput"]},{type:q.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{type:q.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{type:q.FormControlName,selector:"[formControlName]",inputs:["disabled","formControlName","ngModel"],outputs:["ngModelChange"]},{type:w.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{type:E.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{type:E.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{type:q.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{type:w.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{type:D.MatError,selector:"mat-error",inputs:["id"]},{type:q.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{type:q.MaxValidator,selector:"input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]",inputs:["max"]},{type:q.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{type:D.MatSuffix,selector:"[matSuffix]"}],pipes:{translate:P.TranslatePipe}}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:gt,decorators:[{type:o,args:[{selector:"tb-action-node-rabbit-mq-config",templateUrl:"./rabbit-mq-config.component.html",styleUrls:[]}]}],ctorParameters:function(){return[{type:T.Store},{type:q.FormBuilder}]}});class xt extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.proxySchemes=["http","https"],this.httpRequestTypes=Object.keys(Qe)}configForm(){return this.restApiCallConfigForm}onConfigurationSet(e){this.restApiCallConfigForm=this.fb.group({restEndpointUrlPattern:[e?e.restEndpointUrlPattern:null,[k.required]],requestMethod:[e?e.requestMethod:null,[k.required]],useSimpleClientHttpFactory:[!!e&&e.useSimpleClientHttpFactory,[]],ignoreRequestBody:[!!e&&e.ignoreRequestBody,[]],enableProxy:[!!e&&e.enableProxy,[]],useSystemProxyProperties:[!!e&&e.enableProxy,[]],proxyScheme:[e?e.proxyHost:null,[]],proxyHost:[e?e.proxyHost:null,[]],proxyPort:[e?e.proxyPort:null,[]],proxyUser:[e?e.proxyUser:null,[]],proxyPassword:[e?e.proxyPassword:null,[]],readTimeoutMs:[e?e.readTimeoutMs:null,[]],maxParallelRequestsCount:[e?e.maxParallelRequestsCount:null,[k.min(0)]],headers:[e?e.headers:null,[]],useRedisQueueForMsgPersistence:[!!e&&e.useRedisQueueForMsgPersistence,[]],trimQueue:[!!e&&e.trimQueue,[]],maxQueueSize:[e?e.maxQueueSize:null,[]],credentials:[e?e.credentials:null,[]]})}validatorTriggers(){return["useSimpleClientHttpFactory","useRedisQueueForMsgPersistence","enableProxy","useSystemProxyProperties"]}updateValidators(e){const t=this.restApiCallConfigForm.get("useSimpleClientHttpFactory").value,o=this.restApiCallConfigForm.get("useRedisQueueForMsgPersistence").value,r=this.restApiCallConfigForm.get("enableProxy").value,a=this.restApiCallConfigForm.get("useSystemProxyProperties").value;r&&!a?(this.restApiCallConfigForm.get("proxyHost").setValidators(r?[k.required]:[]),this.restApiCallConfigForm.get("proxyPort").setValidators(r?[k.required,k.min(1),k.max(65535)]:[])):(this.restApiCallConfigForm.get("proxyHost").setValidators([]),this.restApiCallConfigForm.get("proxyPort").setValidators([]),t?this.restApiCallConfigForm.get("readTimeoutMs").setValidators([]):this.restApiCallConfigForm.get("readTimeoutMs").setValidators([k.min(0)])),o?this.restApiCallConfigForm.get("maxQueueSize").setValidators([k.min(0)]):this.restApiCallConfigForm.get("maxQueueSize").setValidators([]),this.restApiCallConfigForm.get("readTimeoutMs").updateValueAndValidity({emitEvent:e}),this.restApiCallConfigForm.get("maxQueueSize").updateValueAndValidity({emitEvent:e}),this.restApiCallConfigForm.get("proxyHost").updateValueAndValidity({emitEvent:e}),this.restApiCallConfigForm.get("proxyPort").updateValueAndValidity({emitEvent:e}),this.restApiCallConfigForm.get("credentials").updateValueAndValidity({emitEvent:e})}}e("RestApiCallConfigComponent",xt),xt.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:xt,deps:[{token:T.Store},{token:q.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),xt.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"12.0.0",version:"12.2.15",type:xt,selector:"tb-action-node-rest-api-call-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.endpoint-url-pattern\n \n \n {{ \'tb.rulenode.endpoint-url-pattern-required\' | translate }}\n \n \n \n \n tb.rulenode.request-method\n \n \n {{ requestType }}\n \n \n \n \n {{ \'tb.rulenode.enable-proxy\' | translate }}\n \n \n {{ \'tb.rulenode.use-simple-client-http-factory\' | translate }}\n \n \n {{ \'tb.rulenode.ignore-request-body\' | translate }}\n \n
\n \n {{ \'tb.rulenode.use-system-proxy-properties\' | translate }}\n \n
\n
\n \n tb.rulenode.proxy-scheme\n \n \n {{ proxyScheme }}\n \n \n \n \n tb.rulenode.proxy-host\n \n \n {{ \'tb.rulenode.proxy-host-required\' | translate }}\n \n \n \n tb.rulenode.proxy-port\n \n \n {{ \'tb.rulenode.proxy-port-required\' | translate }}\n \n \n {{ \'tb.rulenode.proxy-port-range\' | translate }}\n \n \n
\n \n tb.rulenode.proxy-user\n \n \n \n tb.rulenode.proxy-password\n \n \n
\n
\n \n tb.rulenode.read-timeout\n \n tb.rulenode.read-timeout-hint\n \n \n tb.rulenode.max-parallel-requests-count\n \n tb.rulenode.max-parallel-requests-count-hint\n \n \n
\n \n \n \n {{ \'tb.rulenode.use-redis-queue\' | translate }}\n \n
\n \n {{ \'tb.rulenode.trim-redis-queue\' | translate }}\n \n \n tb.rulenode.redis-queue-max-size\n \n \n
\n \n
\n',components:[{type:D.MatFormField,selector:"mat-form-field",inputs:["color","floatLabel","appearance","hideRequiredMarker","hintLabel"],exportAs:["matFormField"]},{type:U.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex"],exportAs:["matSelect"]},{type:K.MatOption,selector:"mat-option",exportAs:["matOption"]},{type:V.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex","aria-label","aria-labelledby","id","labelPosition","name","required","checked","disabled","indeterminate","aria-describedby","value"],outputs:["change","indeterminateChange"],exportAs:["matCheckbox"]},{type:nt,selector:"tb-kv-map-config",inputs:["disabled","requiredText","keyText","keyRequiredText","valText","valRequiredText","hintText","required"]},{type:st,selector:"tb-credentials-config",inputs:["required","disableCertPemCredentials","passwordFieldRquired"]}],directives:[{type:E.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{type:q.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{type:q.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{type:D.MatLabel,selector:"mat-label"},{type:P.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{type:R.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["id","disabled","required","type","value","readonly","placeholder","errorStateMatcher","aria-describedby"],exportAs:["matInput"]},{type:q.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{type:q.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{type:q.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{type:q.FormControlName,selector:"[formControlName]",inputs:["disabled","formControlName","ngModel"],outputs:["ngModelChange"]},{type:w.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{type:D.MatError,selector:"mat-error",inputs:["id"]},{type:D.MatHint,selector:"mat-hint",inputs:["align","id"]},{type:w.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{type:E.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{type:E.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{type:q.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{type:q.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]}],pipes:{translate:P.TranslatePipe,safeHtml:Te}}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:xt,decorators:[{type:o,args:[{selector:"tb-action-node-rest-api-call-config",templateUrl:"./rest-api-call-config.component.html",styleUrls:[]}]}],ctorParameters:function(){return[{type:T.Store},{type:q.FormBuilder}]}});class yt extends s{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.rpcReplyConfigForm}onConfigurationSet(e){this.rpcReplyConfigForm=this.fb.group({requestIdMetaDataAttribute:[e?e.requestIdMetaDataAttribute:null,[]]})}}e("RpcReplyConfigComponent",yt),yt.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:yt,deps:[{token:T.Store},{token:q.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),yt.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"12.0.0",version:"12.2.15",type:yt,selector:"tb-action-node-rpc-reply-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.request-id-metadata-attribute\n \n \n
\n',components:[{type:D.MatFormField,selector:"mat-form-field",inputs:["color","floatLabel","appearance","hideRequiredMarker","hintLabel"],exportAs:["matFormField"]}],directives:[{type:E.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{type:q.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{type:q.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{type:D.MatLabel,selector:"mat-label"},{type:P.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{type:R.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["id","disabled","required","type","value","readonly","placeholder","errorStateMatcher","aria-describedby"],exportAs:["matInput"]},{type:q.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{type:q.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{type:q.FormControlName,selector:"[formControlName]",inputs:["disabled","formControlName","ngModel"],outputs:["ngModelChange"]}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:yt,decorators:[{type:o,args:[{selector:"tb-action-node-rpc-reply-config",templateUrl:"./rpc-reply-config.component.html",styleUrls:[]}]}],ctorParameters:function(){return[{type:T.Store},{type:q.FormBuilder}]}});class bt extends s{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.rpcRequestConfigForm}onConfigurationSet(e){this.rpcRequestConfigForm=this.fb.group({timeoutInSeconds:[e?e.timeoutInSeconds:null,[k.required,k.min(0)]]})}}e("RpcRequestConfigComponent",bt),bt.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:bt,deps:[{token:T.Store},{token:q.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),bt.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"12.0.0",version:"12.2.15",type:bt,selector:"tb-action-node-rpc-request-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.timeout-sec\n \n \n {{ \'tb.rulenode.timeout-required\' | translate }}\n \n \n {{ \'tb.rulenode.min-timeout-message\' | translate }}\n \n \n
\n',components:[{type:D.MatFormField,selector:"mat-form-field",inputs:["color","floatLabel","appearance","hideRequiredMarker","hintLabel"],exportAs:["matFormField"]}],directives:[{type:E.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{type:q.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{type:q.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{type:E.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{type:D.MatLabel,selector:"mat-label"},{type:P.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{type:q.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{type:q.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{type:R.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["id","disabled","required","type","value","readonly","placeholder","errorStateMatcher","aria-describedby"],exportAs:["matInput"]},{type:q.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{type:q.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{type:q.FormControlName,selector:"[formControlName]",inputs:["disabled","formControlName","ngModel"],outputs:["ngModelChange"]},{type:q.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{type:w.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{type:D.MatError,selector:"mat-error",inputs:["id"]}],pipes:{translate:P.TranslatePipe}}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:bt,decorators:[{type:o,args:[{selector:"tb-action-node-rpc-request-config",templateUrl:"./rpc-request-config.component.html",styleUrls:[]}]}],ctorParameters:function(){return[{type:T.Store},{type:q.FormBuilder}]}});class ht extends s{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.saveToCustomTableConfigForm}onConfigurationSet(e){this.saveToCustomTableConfigForm=this.fb.group({tableName:[e?e.tableName:null,[k.required,k.pattern(/.*\S.*/)]],fieldsMapping:[e?e.fieldsMapping:null,[k.required]]})}prepareOutputConfig(e){return e.tableName=e.tableName.trim(),e}}e("SaveToCustomTableConfigComponent",ht),ht.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:ht,deps:[{token:T.Store},{token:q.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),ht.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"12.0.0",version:"12.2.15",type:ht,selector:"tb-action-node-custom-table-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.custom-table-name\n \n \n {{ \'tb.rulenode.custom-table-name-required\' | translate }}\n \n tb.rulenode.custom-table-hint\n \n \n \n \n
\n',components:[{type:D.MatFormField,selector:"mat-form-field",inputs:["color","floatLabel","appearance","hideRequiredMarker","hintLabel"],exportAs:["matFormField"]},{type:nt,selector:"tb-kv-map-config",inputs:["disabled","requiredText","keyText","keyRequiredText","valText","valRequiredText","hintText","required"]}],directives:[{type:E.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{type:q.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{type:q.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{type:D.MatLabel,selector:"mat-label"},{type:P.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{type:R.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["id","disabled","required","type","value","readonly","placeholder","errorStateMatcher","aria-describedby"],exportAs:["matInput"]},{type:q.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{type:q.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{type:q.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{type:q.FormControlName,selector:"[formControlName]",inputs:["disabled","formControlName","ngModel"],outputs:["ngModelChange"]},{type:w.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{type:D.MatError,selector:"mat-error",inputs:["id"]},{type:D.MatHint,selector:"mat-hint",inputs:["align","id"]}],pipes:{translate:P.TranslatePipe}}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:ht,decorators:[{type:o,args:[{selector:"tb-action-node-custom-table-config",templateUrl:"./save-to-custom-table-config.component.html",styleUrls:[]}]}],ctorParameters:function(){return[{type:T.Store},{type:q.FormBuilder}]}});class Ct extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.smtpProtocols=["smtp","smtps"],this.tlsVersions=["TLSv1","TLSv1.1","TLSv1.2","TLSv1.3"]}configForm(){return this.sendEmailConfigForm}onConfigurationSet(e){this.sendEmailConfigForm=this.fb.group({useSystemSmtpSettings:[!!e&&e.useSystemSmtpSettings,[]],smtpProtocol:[e?e.smtpProtocol:null,[]],smtpHost:[e?e.smtpHost:null,[]],smtpPort:[e?e.smtpPort:null,[]],timeout:[e?e.timeout:null,[]],enableTls:[!!e&&e.enableTls,[]],tlsVersion:[e?e.tlsVersion:null,[]],enableProxy:[!!e&&e.enableProxy,[]],proxyHost:[e?e.proxyHost:null,[]],proxyPort:[e?e.proxyPort:null,[]],proxyUser:[e?e.proxyUser:null,[]],proxyPassword:[e?e.proxyPassword:null,[]],username:[e?e.username:null,[]],password:[e?e.password:null,[]]})}validatorTriggers(){return["useSystemSmtpSettings","enableProxy"]}updateValidators(e){const t=this.sendEmailConfigForm.get("useSystemSmtpSettings").value,o=this.sendEmailConfigForm.get("enableProxy").value;t?(this.sendEmailConfigForm.get("smtpProtocol").setValidators([]),this.sendEmailConfigForm.get("smtpHost").setValidators([]),this.sendEmailConfigForm.get("smtpPort").setValidators([]),this.sendEmailConfigForm.get("timeout").setValidators([]),this.sendEmailConfigForm.get("proxyHost").setValidators([]),this.sendEmailConfigForm.get("proxyPort").setValidators([])):(this.sendEmailConfigForm.get("smtpProtocol").setValidators([k.required]),this.sendEmailConfigForm.get("smtpHost").setValidators([k.required]),this.sendEmailConfigForm.get("smtpPort").setValidators([k.required,k.min(1),k.max(65535)]),this.sendEmailConfigForm.get("timeout").setValidators([k.required,k.min(0)]),this.sendEmailConfigForm.get("proxyHost").setValidators(o?[k.required]:[]),this.sendEmailConfigForm.get("proxyPort").setValidators(o?[k.required,k.min(1),k.max(65535)]:[])),this.sendEmailConfigForm.get("smtpProtocol").updateValueAndValidity({emitEvent:e}),this.sendEmailConfigForm.get("smtpHost").updateValueAndValidity({emitEvent:e}),this.sendEmailConfigForm.get("smtpPort").updateValueAndValidity({emitEvent:e}),this.sendEmailConfigForm.get("timeout").updateValueAndValidity({emitEvent:e}),this.sendEmailConfigForm.get("proxyHost").updateValueAndValidity({emitEvent:e}),this.sendEmailConfigForm.get("proxyPort").updateValueAndValidity({emitEvent:e})}}e("SendEmailConfigComponent",Ct),Ct.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Ct,deps:[{token:T.Store},{token:q.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),Ct.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"12.0.0",version:"12.2.15",type:Ct,selector:"tb-action-node-send-email-config",usesInheritance:!0,ngImport:t,template:'
\n \n {{ \'tb.rulenode.use-system-smtp-settings\' | translate }}\n \n
\n \n tb.rulenode.smtp-protocol\n \n \n {{ smtpProtocol.toUpperCase() }}\n \n \n \n
\n \n tb.rulenode.smtp-host\n \n \n {{ \'tb.rulenode.smtp-host-required\' | translate }}\n \n \n \n tb.rulenode.smtp-port\n \n \n {{ \'tb.rulenode.smtp-port-required\' | translate }}\n \n \n {{ \'tb.rulenode.smtp-port-range\' | translate }}\n \n \n {{ \'tb.rulenode.smtp-port-range\' | translate }}\n \n \n
\n \n tb.rulenode.timeout-msec\n \n \n {{ \'tb.rulenode.timeout-required\' | translate }}\n \n \n {{ \'tb.rulenode.min-timeout-msec-message\' | translate }}\n \n \n \n {{ \'tb.rulenode.enable-tls\' | translate }}\n \n \n tb.rulenode.tls-version\n \n \n {{ tlsVersion }}\n \n \n \n \n {{ \'tb.rulenode.enable-proxy\' | translate }}\n \n
\n
\n \n tb.rulenode.proxy-host\n \n \n {{ \'tb.rulenode.proxy-host-required\' | translate }}\n \n \n \n tb.rulenode.proxy-port\n \n \n {{ \'tb.rulenode.proxy-port-required\' | translate }}\n \n \n {{ \'tb.rulenode.proxy-port-range\' | translate }}\n \n \n
\n \n tb.rulenode.proxy-user\n \n \n \n tb.rulenode.proxy-password\n \n \n
\n \n tb.rulenode.username\n \n \n \n tb.rulenode.password\n \n \n \n
\n
\n',components:[{type:V.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex","aria-label","aria-labelledby","id","labelPosition","name","required","checked","disabled","indeterminate","aria-describedby","value"],outputs:["change","indeterminateChange"],exportAs:["matCheckbox"]},{type:D.MatFormField,selector:"mat-form-field",inputs:["color","floatLabel","appearance","hideRequiredMarker","hintLabel"],exportAs:["matFormField"]},{type:U.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex"],exportAs:["matSelect"]},{type:K.MatOption,selector:"mat-option",exportAs:["matOption"]},{type:ce.TbCheckboxComponent,selector:"tb-checkbox",inputs:["disabled","trueValue","falseValue"],outputs:["valueChange"]},{type:j.TogglePasswordComponent,selector:"tb-toggle-password"}],directives:[{type:E.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{type:q.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{type:q.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{type:q.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{type:q.FormControlName,selector:"[formControlName]",inputs:["disabled","formControlName","ngModel"],outputs:["ngModelChange"]},{type:w.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{type:D.MatLabel,selector:"mat-label"},{type:P.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{type:w.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{type:E.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{type:E.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{type:R.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["id","disabled","required","type","value","readonly","placeholder","errorStateMatcher","aria-describedby"],exportAs:["matInput"]},{type:q.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{type:q.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{type:D.MatError,selector:"mat-error",inputs:["id"]},{type:q.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{type:q.MaxValidator,selector:"input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]",inputs:["max"]},{type:q.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{type:D.MatSuffix,selector:"[matSuffix]"}],pipes:{translate:P.TranslatePipe}}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Ct,decorators:[{type:o,args:[{selector:"tb-action-node-send-email-config",templateUrl:"./send-email-config.component.html",styleUrls:[]}]}],ctorParameters:function(){return[{type:T.Store},{type:q.FormBuilder}]}});class Ft extends s{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.sendSmsConfigForm}onConfigurationSet(e){this.sendSmsConfigForm=this.fb.group({numbersToTemplate:[e?e.numbersToTemplate:null,[k.required]],smsMessageTemplate:[e?e.smsMessageTemplate:null,[k.required]],useSystemSmsSettings:[!!e&&e.useSystemSmsSettings,[]],smsProviderConfiguration:[e?e.smsProviderConfiguration:null,[]]})}validatorTriggers(){return["useSystemSmsSettings"]}updateValidators(e){this.sendSmsConfigForm.get("useSystemSmsSettings").value?this.sendSmsConfigForm.get("smsProviderConfiguration").setValidators([]):this.sendSmsConfigForm.get("smsProviderConfiguration").setValidators([k.required]),this.sendSmsConfigForm.get("smsProviderConfiguration").updateValueAndValidity({emitEvent:e})}}e("SendSmsConfigComponent",Ft),Ft.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Ft,deps:[{token:T.Store},{token:q.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),Ft.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"12.0.0",version:"12.2.15",type:Ft,selector:"tb-action-node-send-sms-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.numbers-to-template\n \n \n {{ \'tb.rulenode.numbers-to-template-required\' | translate }}\n \n \n \n \n tb.rulenode.sms-message-template\n \n \n {{ \'tb.rulenode.sms-message-template-required\' | translate }}\n \n \n \n \n {{ \'tb.rulenode.use-system-sms-settings\' | translate }}\n \n \n \n
\n',components:[{type:D.MatFormField,selector:"mat-form-field",inputs:["color","floatLabel","appearance","hideRequiredMarker","hintLabel"],exportAs:["matFormField"]},{type:V.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex","aria-label","aria-labelledby","id","labelPosition","name","required","checked","disabled","indeterminate","aria-describedby","value"],outputs:["change","indeterminateChange"],exportAs:["matCheckbox"]},{type:ge.SmsProviderConfigurationComponent,selector:"tb-sms-provider-configuration",inputs:["required","disabled"]}],directives:[{type:E.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{type:q.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{type:q.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{type:D.MatLabel,selector:"mat-label"},{type:P.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{type:R.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["id","disabled","required","type","value","readonly","placeholder","errorStateMatcher","aria-describedby"],exportAs:["matInput"]},{type:q.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{type:q.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{type:q.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{type:q.FormControlName,selector:"[formControlName]",inputs:["disabled","formControlName","ngModel"],outputs:["ngModelChange"]},{type:w.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{type:D.MatError,selector:"mat-error",inputs:["id"]},{type:D.MatHint,selector:"mat-hint",inputs:["align","id"]}],pipes:{translate:P.TranslatePipe,safeHtml:Te}}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Ft,decorators:[{type:o,args:[{selector:"tb-action-node-send-sms-config",templateUrl:"./send-sms-config.component.html",styleUrls:[]}]}],ctorParameters:function(){return[{type:T.Store},{type:q.FormBuilder}]}});class Lt extends s{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.snsConfigForm}onConfigurationSet(e){this.snsConfigForm=this.fb.group({topicArnPattern:[e?e.topicArnPattern:null,[k.required]],accessKeyId:[e?e.accessKeyId:null,[k.required]],secretAccessKey:[e?e.secretAccessKey:null,[k.required]],region:[e?e.region:null,[k.required]]})}}e("SnsConfigComponent",Lt),Lt.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Lt,deps:[{token:T.Store},{token:q.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),Lt.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"12.0.0",version:"12.2.15",type:Lt,selector:"tb-action-node-sns-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.topic-arn-pattern\n \n \n {{ \'tb.rulenode.topic-arn-pattern-required\' | translate }}\n \n \n \n \n tb.rulenode.aws-access-key-id\n \n \n {{ \'tb.rulenode.aws-access-key-id-required\' | translate }}\n \n \n \n tb.rulenode.aws-secret-access-key\n \n \n {{ \'tb.rulenode.aws-secret-access-key-required\' | translate }}\n \n \n \n tb.rulenode.aws-region\n \n \n {{ \'tb.rulenode.aws-region-required\' | translate }}\n \n \n
\n',components:[{type:D.MatFormField,selector:"mat-form-field",inputs:["color","floatLabel","appearance","hideRequiredMarker","hintLabel"],exportAs:["matFormField"]}],directives:[{type:E.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{type:q.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{type:q.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{type:D.MatLabel,selector:"mat-label"},{type:P.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{type:R.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["id","disabled","required","type","value","readonly","placeholder","errorStateMatcher","aria-describedby"],exportAs:["matInput"]},{type:q.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{type:q.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{type:q.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{type:q.FormControlName,selector:"[formControlName]",inputs:["disabled","formControlName","ngModel"],outputs:["ngModelChange"]},{type:w.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{type:D.MatError,selector:"mat-error",inputs:["id"]},{type:D.MatHint,selector:"mat-hint",inputs:["align","id"]}],pipes:{translate:P.TranslatePipe,safeHtml:Te}}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Lt,decorators:[{type:o,args:[{selector:"tb-action-node-sns-config",templateUrl:"./sns-config.component.html",styleUrls:[]}]}],ctorParameters:function(){return[{type:T.Store},{type:q.FormBuilder}]}});class vt extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.sqsQueueType=Ue,this.sqsQueueTypes=Object.keys(Ue),this.sqsQueueTypeTranslationsMap=Ke}configForm(){return this.sqsConfigForm}onConfigurationSet(e){this.sqsConfigForm=this.fb.group({queueType:[e?e.queueType:null,[k.required]],queueUrlPattern:[e?e.queueUrlPattern:null,[k.required]],delaySeconds:[e?e.delaySeconds:null,[k.min(0),k.max(900)]],messageAttributes:[e?e.messageAttributes:null,[]],accessKeyId:[e?e.accessKeyId:null,[k.required]],secretAccessKey:[e?e.secretAccessKey:null,[k.required]],region:[e?e.region:null,[k.required]]})}}e("SqsConfigComponent",vt),vt.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:vt,deps:[{token:T.Store},{token:q.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),vt.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"12.0.0",version:"12.2.15",type:vt,selector:"tb-action-node-sqs-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.queue-type\n \n \n {{ sqsQueueTypeTranslationsMap.get(type) | translate }}\n \n \n \n \n tb.rulenode.queue-url-pattern\n \n \n {{ \'tb.rulenode.queue-url-pattern-required\' | translate }}\n \n \n \n \n tb.rulenode.delay-seconds\n \n \n {{ \'tb.rulenode.min-delay-seconds-message\' | translate }}\n \n \n {{ \'tb.rulenode.max-delay-seconds-message\' | translate }}\n \n \n \n
\n \n \n \n tb.rulenode.aws-access-key-id\n \n \n {{ \'tb.rulenode.aws-access-key-id-required\' | translate }}\n \n \n \n tb.rulenode.aws-secret-access-key\n \n \n {{ \'tb.rulenode.aws-secret-access-key-required\' | translate }}\n \n \n \n tb.rulenode.aws-region\n \n \n {{ \'tb.rulenode.aws-region-required\' | translate }}\n \n \n
\n',components:[{type:D.MatFormField,selector:"mat-form-field",inputs:["color","floatLabel","appearance","hideRequiredMarker","hintLabel"],exportAs:["matFormField"]},{type:U.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex"],exportAs:["matSelect"]},{type:K.MatOption,selector:"mat-option",exportAs:["matOption"]},{type:nt,selector:"tb-kv-map-config",inputs:["disabled","requiredText","keyText","keyRequiredText","valText","valRequiredText","hintText","required"]}],directives:[{type:E.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{type:q.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{type:q.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{type:D.MatLabel,selector:"mat-label"},{type:P.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{type:q.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{type:q.FormControlName,selector:"[formControlName]",inputs:["disabled","formControlName","ngModel"],outputs:["ngModelChange"]},{type:q.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{type:w.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{type:R.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["id","disabled","required","type","value","readonly","placeholder","errorStateMatcher","aria-describedby"],exportAs:["matInput"]},{type:q.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{type:w.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{type:D.MatError,selector:"mat-error",inputs:["id"]},{type:D.MatHint,selector:"mat-hint",inputs:["align","id"]},{type:q.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{type:q.MaxValidator,selector:"input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]",inputs:["max"]},{type:q.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"}],pipes:{translate:P.TranslatePipe,safeHtml:Te}}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:vt,decorators:[{type:o,args:[{selector:"tb-action-node-sqs-config",templateUrl:"./sqs-config.component.html",styleUrls:[]}]}],ctorParameters:function(){return[{type:T.Store},{type:q.FormBuilder}]}});class It extends s{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.timeseriesConfigForm}onConfigurationSet(e){this.timeseriesConfigForm=this.fb.group({defaultTTL:[e?e.defaultTTL:null,[k.required,k.min(0)]],skipLatestPersistence:[!!e&&e.skipLatestPersistence,[]],useServerTs:[!!e&&e.useServerTs,[]]})}}e("TimeseriesConfigComponent",It),It.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:It,deps:[{token:T.Store},{token:q.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),It.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"12.0.0",version:"12.2.15",type:It,selector:"tb-action-node-timeseries-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.default-ttl\n \n \n {{ \'tb.rulenode.default-ttl-required\' | translate }}\n \n \n {{ \'tb.rulenode.min-default-ttl-message\' | translate }}\n \n \n \n {{ \'tb.rulenode.skip-latest-persistence\' | translate }}\n \n \n {{ \'tb.rulenode.use-server-ts\' | translate }}\n \n
tb.rulenode.use-server-ts-hint
\n
\n',components:[{type:D.MatFormField,selector:"mat-form-field",inputs:["color","floatLabel","appearance","hideRequiredMarker","hintLabel"],exportAs:["matFormField"]},{type:V.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex","aria-label","aria-labelledby","id","labelPosition","name","required","checked","disabled","indeterminate","aria-describedby","value"],outputs:["change","indeterminateChange"],exportAs:["matCheckbox"]}],directives:[{type:E.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{type:q.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{type:q.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{type:E.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{type:D.MatLabel,selector:"mat-label"},{type:P.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{type:q.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{type:q.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{type:R.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["id","disabled","required","type","value","readonly","placeholder","errorStateMatcher","aria-describedby"],exportAs:["matInput"]},{type:q.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{type:q.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{type:q.FormControlName,selector:"[formControlName]",inputs:["disabled","formControlName","ngModel"],outputs:["ngModelChange"]},{type:q.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{type:w.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{type:D.MatError,selector:"mat-error",inputs:["id"]}],pipes:{translate:P.TranslatePipe}}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:It,decorators:[{type:o,args:[{selector:"tb-action-node-timeseries-config",templateUrl:"./timeseries-config.component.html",styleUrls:[]}]}],ctorParameters:function(){return[{type:T.Store},{type:q.FormBuilder}]}});class Nt extends s{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.unassignCustomerConfigForm}onConfigurationSet(e){this.unassignCustomerConfigForm=this.fb.group({customerNamePattern:[e?e.customerNamePattern:null,[k.required,k.pattern(/.*\S.*/)]],customerCacheExpiration:[e?e.customerCacheExpiration:null,[k.required,k.min(0)]]})}prepareOutputConfig(e){return e.customerNamePattern=e.customerNamePattern.trim(),e}}e("UnassignCustomerConfigComponent",Nt),Nt.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Nt,deps:[{token:T.Store},{token:q.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),Nt.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"12.0.0",version:"12.2.15",type:Nt,selector:"tb-action-node-un-assign-to-customer-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.customer-name-pattern\n \n \n {{ \'tb.rulenode.customer-name-pattern-required\' | translate }}\n \n \n \n \n tb.rulenode.customer-cache-expiration\n \n \n {{ \'tb.rulenode.customer-cache-expiration-required\' | translate }}\n \n \n {{ \'tb.rulenode.customer-cache-expiration-range\' | translate }}\n \n tb.rulenode.customer-cache-expiration-hint\n \n
\n',components:[{type:D.MatFormField,selector:"mat-form-field",inputs:["color","floatLabel","appearance","hideRequiredMarker","hintLabel"],exportAs:["matFormField"]}],directives:[{type:E.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{type:q.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{type:q.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{type:D.MatLabel,selector:"mat-label"},{type:P.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{type:R.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["id","disabled","required","type","value","readonly","placeholder","errorStateMatcher","aria-describedby"],exportAs:["matInput"]},{type:q.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{type:q.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{type:q.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{type:q.FormControlName,selector:"[formControlName]",inputs:["disabled","formControlName","ngModel"],outputs:["ngModelChange"]},{type:w.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{type:D.MatError,selector:"mat-error",inputs:["id"]},{type:D.MatHint,selector:"mat-hint",inputs:["align","id"]},{type:q.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{type:q.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"}],pipes:{translate:P.TranslatePipe,safeHtml:Te}}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Nt,decorators:[{type:o,args:[{selector:"tb-action-node-un-assign-to-customer-config",templateUrl:"./unassign-customer-config.component.html",styleUrls:[]}]}],ctorParameters:function(){return[{type:T.Store},{type:q.FormBuilder}]}});class Tt extends y{constructor(e,t){super(e),this.store=e,this.fb=t,this.directionTypes=Object.keys(c),this.directionTypeTranslations=g,this.entityType=x,this.propagateChange=null}get required(){return this.requiredValue}set required(e){this.requiredValue=le(e)}ngOnInit(){this.deviceRelationsQueryFormGroup=this.fb.group({fetchLastLevelOnly:[!1,[]],direction:[null,[k.required]],maxLevel:[null,[]],relationType:[null],deviceTypes:[null,[k.required]]}),this.deviceRelationsQueryFormGroup.valueChanges.subscribe((e=>{this.deviceRelationsQueryFormGroup.valid?this.propagateChange(e):this.propagateChange(null)}))}registerOnChange(e){this.propagateChange=e}registerOnTouched(e){}setDisabledState(e){this.disabled=e,this.disabled?this.deviceRelationsQueryFormGroup.disable({emitEvent:!1}):this.deviceRelationsQueryFormGroup.enable({emitEvent:!1})}writeValue(e){this.deviceRelationsQueryFormGroup.reset(e,{emitEvent:!1})}}e("DeviceRelationsQueryConfigComponent",Tt),Tt.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Tt,deps:[{token:T.Store},{token:q.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),Tt.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"12.0.0",version:"12.2.15",type:Tt,selector:"tb-device-relations-query-config",inputs:{disabled:"disabled",required:"required"},providers:[{provide:S,useExisting:n((()=>Tt)),multi:!0}],usesInheritance:!0,ngImport:t,template:'
\n \n {{ \'alias.last-level-relation\' | translate }}\n \n
\n \n relation.direction\n \n \n {{ directionTypeTranslations.get(type) | translate }}\n \n \n \n \n tb.rulenode.max-relation-level\n \n \n
\n
relation.relation-type
\n \n \n
device.device-types
\n \n \n
\n',components:[{type:V.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex","aria-label","aria-labelledby","id","labelPosition","name","required","checked","disabled","indeterminate","aria-describedby","value"],outputs:["change","indeterminateChange"],exportAs:["matCheckbox"]},{type:D.MatFormField,selector:"mat-form-field",inputs:["color","floatLabel","appearance","hideRequiredMarker","hintLabel"],exportAs:["matFormField"]},{type:U.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex"],exportAs:["matSelect"]},{type:K.MatOption,selector:"mat-option",exportAs:["matOption"]},{type:ye.RelationTypeAutocompleteComponent,selector:"tb-relation-type-autocomplete",inputs:["required","disabled"]},{type:be.EntitySubTypeListComponent,selector:"tb-entity-subtype-list",inputs:["required","disabled","entityType"]}],directives:[{type:E.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{type:q.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{type:q.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{type:q.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{type:q.FormControlName,selector:"[formControlName]",inputs:["disabled","formControlName","ngModel"],outputs:["ngModelChange"]},{type:E.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{type:D.MatLabel,selector:"mat-label"},{type:P.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{type:q.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{type:w.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{type:E.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{type:R.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["id","disabled","required","type","value","readonly","placeholder","errorStateMatcher","aria-describedby"],exportAs:["matInput"]},{type:q.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{type:q.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{type:q.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"}],pipes:{translate:P.TranslatePipe}}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Tt,decorators:[{type:o,args:[{selector:"tb-device-relations-query-config",templateUrl:"./device-relations-query-config.component.html",styleUrls:[],providers:[{provide:S,useExisting:n((()=>Tt)),multi:!0}]}]}],ctorParameters:function(){return[{type:T.Store},{type:q.FormBuilder}]},propDecorators:{disabled:[{type:l}],required:[{type:l}]}});class qt extends y{constructor(e,t){super(e),this.store=e,this.fb=t,this.directionTypes=Object.keys(c),this.directionTypeTranslations=g,this.propagateChange=null}get required(){return this.requiredValue}set required(e){this.requiredValue=le(e)}ngOnInit(){this.relationsQueryFormGroup=this.fb.group({fetchLastLevelOnly:[!1,[]],direction:[null,[k.required]],maxLevel:[null,[]],filters:[null]}),this.relationsQueryFormGroup.valueChanges.subscribe((e=>{this.relationsQueryFormGroup.valid?this.propagateChange(e):this.propagateChange(null)}))}registerOnChange(e){this.propagateChange=e}registerOnTouched(e){}setDisabledState(e){this.disabled=e,this.disabled?this.relationsQueryFormGroup.disable({emitEvent:!1}):this.relationsQueryFormGroup.enable({emitEvent:!1})}writeValue(e){this.relationsQueryFormGroup.reset(e||{},{emitEvent:!1})}}e("RelationsQueryConfigComponent",qt),qt.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:qt,deps:[{token:T.Store},{token:q.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),qt.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"12.0.0",version:"12.2.15",type:qt,selector:"tb-relations-query-config",inputs:{disabled:"disabled",required:"required"},providers:[{provide:S,useExisting:n((()=>qt)),multi:!0}],usesInheritance:!0,ngImport:t,template:'
\n \n {{ \'alias.last-level-relation\' | translate }}\n \n
\n \n relation.direction\n \n \n {{ directionTypeTranslations.get(type) | translate }}\n \n \n \n \n tb.rulenode.max-relation-level\n \n \n
\n
relation.relation-filters
\n \n
\n',components:[{type:V.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex","aria-label","aria-labelledby","id","labelPosition","name","required","checked","disabled","indeterminate","aria-describedby","value"],outputs:["change","indeterminateChange"],exportAs:["matCheckbox"]},{type:D.MatFormField,selector:"mat-form-field",inputs:["color","floatLabel","appearance","hideRequiredMarker","hintLabel"],exportAs:["matFormField"]},{type:U.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex"],exportAs:["matSelect"]},{type:K.MatOption,selector:"mat-option",exportAs:["matOption"]},{type:he.RelationFiltersComponent,selector:"tb-relation-filters",inputs:["disabled","allowedEntityTypes"]}],directives:[{type:E.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{type:q.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{type:q.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{type:q.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{type:q.FormControlName,selector:"[formControlName]",inputs:["disabled","formControlName","ngModel"],outputs:["ngModelChange"]},{type:E.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{type:D.MatLabel,selector:"mat-label"},{type:P.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{type:q.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{type:w.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{type:E.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{type:R.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["id","disabled","required","type","value","readonly","placeholder","errorStateMatcher","aria-describedby"],exportAs:["matInput"]},{type:q.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{type:q.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{type:q.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"}],pipes:{translate:P.TranslatePipe}}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:qt,decorators:[{type:o,args:[{selector:"tb-relations-query-config",templateUrl:"./relations-query-config.component.html",styleUrls:[],providers:[{provide:S,useExisting:n((()=>qt)),multi:!0}]}]}],ctorParameters:function(){return[{type:T.Store},{type:q.FormBuilder}]},propDecorators:{disabled:[{type:l}],required:[{type:l}]}});class kt extends y{constructor(e,t,o,r){super(e),this.store=e,this.translate=t,this.truncate=o,this.fb=r,this.placeholder="tb.rulenode.message-type",this.separatorKeysCodes=[Z,X,ee],this.messageTypes=[],this.messageTypesList=[],this.searchText="",this.propagateChange=e=>{},this.messageTypeConfigForm=this.fb.group({messageType:[null]});for(const e of Object.keys(b))this.messageTypesList.push({name:h.get(b[e]),value:e})}get required(){return this.requiredValue}set required(e){this.requiredValue=le(e)}registerOnChange(e){this.propagateChange=e}registerOnTouched(e){}ngOnInit(){this.filteredMessageTypes=this.messageTypeConfigForm.get("messageType").valueChanges.pipe(ue(""),pe((e=>e||"")),de((e=>this.fetchMessageTypes(e))),fe())}ngAfterViewInit(){}setDisabledState(e){this.disabled=e,this.disabled?this.messageTypeConfigForm.disable({emitEvent:!1}):this.messageTypeConfigForm.enable({emitEvent:!1})}writeValue(e){this.searchText="",this.messageTypes.length=0,e&&e.forEach((e=>{const t=this.messageTypesList.find((t=>t.value===e));t?this.messageTypes.push({name:t.name,value:t.value}):this.messageTypes.push({name:e,value:e})}))}displayMessageTypeFn(e){return e?e.name:void 0}textIsNotEmpty(e){return!!(e&&null!=e&&e.length>0)}createMessageType(e,t){e.preventDefault(),this.transformMessageType(t)}add(e){this.transformMessageType(e.value)}fetchMessageTypes(e){if(this.searchText=e,this.searchText&&this.searchText.length){const e=this.searchText.toUpperCase();return Ce(this.messageTypesList.filter((t=>t.name.toUpperCase().includes(e))))}return Ce(this.messageTypesList)}transformMessageType(e){if((e||"").trim()){let t=null;const o=e.trim(),r=this.messageTypesList.find((e=>e.name===o));t=r?{name:r.name,value:r.value}:{name:o,value:o},t&&this.addMessageType(t)}this.clear("")}remove(e){const t=this.messageTypes.indexOf(e);t>=0&&(this.messageTypes.splice(t,1),this.updateModel())}selected(e){this.addMessageType(e.option.value),this.clear("")}addMessageType(e){-1===this.messageTypes.findIndex((t=>t.value===e.value))&&(this.messageTypes.push(e),this.updateModel())}onFocus(){this.messageTypeConfigForm.get("messageType").updateValueAndValidity({onlySelf:!0,emitEvent:!0})}clear(e=""){this.messageTypeInput.nativeElement.value=e,this.messageTypeConfigForm.get("messageType").patchValue(null,{emitEvent:!0}),setTimeout((()=>{this.messageTypeInput.nativeElement.blur(),this.messageTypeInput.nativeElement.focus()}),0)}updateModel(){const e=this.messageTypes.map((e=>e.value));this.required?(this.chipList.errorState=!e.length,this.propagateChange(e.length>0?e:null)):(this.chipList.errorState=!1,this.propagateChange(e))}}e("MessageTypesConfigComponent",kt),kt.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:kt,deps:[{token:T.Store},{token:P.TranslateService},{token:C.TruncatePipe},{token:q.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),kt.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"12.0.0",version:"12.2.15",type:kt,selector:"tb-message-types-config",inputs:{required:"required",label:"label",placeholder:"placeholder",disabled:"disabled"},providers:[{provide:S,useExisting:n((()=>kt)),multi:!0}],viewQueries:[{propertyName:"chipList",first:!0,predicate:["chipList"],descendants:!0},{propertyName:"matAutocomplete",first:!0,predicate:["messageTypeAutocomplete"],descendants:!0},{propertyName:"messageTypeInput",first:!0,predicate:["messageTypeInput"],descendants:!0}],usesInheritance:!0,ngImport:t,template:'\n {{ label }}\n \n \n {{messageType.name}}\n close\n \n \n \n \n \n \n \n \n
\n
\n tb.rulenode.no-message-types-found\n
\n \n \n {{ translate.get(\'tb.rulenode.no-message-type-matching\',\n {messageType: truncate.transform(searchText, true, 6, '...')}) | async }}\n \n \n \n tb.rulenode.create-new-message-type\n \n
\n
\n
\n \n {{ \'tb.rulenode.message-types-required\' | translate }}\n \n
\n',components:[{type:D.MatFormField,selector:"mat-form-field",inputs:["color","floatLabel","appearance","hideRequiredMarker","hintLabel"],exportAs:["matFormField"]},{type:te.MatChipList,selector:"mat-chip-list",inputs:["aria-orientation","multiple","compareWith","value","required","placeholder","disabled","selectable","tabIndex","errorStateMatcher"],outputs:["change","valueChange"],exportAs:["matChipList"]},{type:oe.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{type:Fe.MatAutocomplete,selector:"mat-autocomplete",inputs:["disableRipple"],exportAs:["matAutocomplete"]},{type:K.MatOption,selector:"mat-option",exportAs:["matOption"]}],directives:[{type:q.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{type:q.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{type:w.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{type:D.MatLabel,selector:"mat-label"},{type:P.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{type:w.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{type:te.MatChip,selector:"mat-basic-chip, [mat-basic-chip], mat-chip, [mat-chip]",inputs:["color","disableRipple","tabIndex","selected","value","selectable","disabled","removable"],outputs:["selectionChange","destroyed","removed"],exportAs:["matChip"]},{type:te.MatChipRemove,selector:"[matChipRemove]"},{type:R.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["id","disabled","required","type","value","readonly","placeholder","errorStateMatcher","aria-describedby"],exportAs:["matInput"]},{type:q.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{type:Fe.MatAutocompleteTrigger,selector:"input[matAutocomplete], textarea[matAutocomplete]",exportAs:["matAutocompleteTrigger"]},{type:te.MatChipInput,selector:"input[matChipInputFor]",inputs:["matChipInputSeparatorKeyCodes","placeholder","id","matChipInputFor","matChipInputAddOnBlur","disabled"],outputs:["matChipInputTokenEnd"],exportAs:["matChipInput","matChipInputFor"]},{type:q.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{type:q.FormControlName,selector:"[formControlName]",inputs:["disabled","formControlName","ngModel"],outputs:["ngModelChange"]},{type:Fe.MatAutocompleteOrigin,selector:"[matAutocompleteOrigin]",exportAs:["matAutocompleteOrigin"]},{type:D.MatError,selector:"mat-error",inputs:["id"]}],pipes:{translate:P.TranslatePipe,async:w.AsyncPipe,highlight:Le.HighlightPipe}}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:kt,decorators:[{type:o,args:[{selector:"tb-message-types-config",templateUrl:"./message-types-config.component.html",styleUrls:[],providers:[{provide:S,useExisting:n((()=>kt)),multi:!0}]}]}],ctorParameters:function(){return[{type:T.Store},{type:P.TranslateService},{type:C.TruncatePipe},{type:q.FormBuilder}]},propDecorators:{required:[{type:l}],label:[{type:l}],placeholder:[{type:l}],disabled:[{type:l}],chipList:[{type:a,args:["chipList",{static:!1}]}],matAutocomplete:[{type:a,args:["messageTypeAutocomplete",{static:!1}]}],messageTypeInput:[{type:a,args:["messageTypeInput",{static:!1}]}]}});class Mt{}e("RulenodeCoreConfigCommonModule",Mt),Mt.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Mt,deps:[],target:t.ɵɵFactoryTarget.NgModule}),Mt.ɵmod=t.ɵɵngDeclareNgModule({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Mt,declarations:[nt,Tt,qt,kt,st,Te],imports:[O,F,xe],exports:[nt,Tt,qt,kt,st,Te]}),Mt.ɵinj=t.ɵɵngDeclareInjector({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Mt,imports:[[O,F,xe]]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Mt,decorators:[{type:i,args:[{declarations:[nt,Tt,qt,kt,st,Te],imports:[O,F,xe],exports:[nt,Tt,qt,kt,st,Te]}]}]});class St{}e("RuleNodeCoreConfigActionModule",St),St.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:St,deps:[],target:t.ɵɵFactoryTarget.NgModule}),St.ɵmod=t.ɵɵngDeclareNgModule({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:St,declarations:[ke,It,bt,it,qe,Ze,Xe,et,pt,tt,rt,at,ut,yt,ht,Nt,Lt,vt,dt,lt,mt,gt,xt,Ct,Je,Ye,ot,Ft,ct,ft],imports:[O,F,xe,Mt],exports:[ke,It,bt,it,qe,Ze,Xe,et,pt,tt,rt,at,ut,yt,ht,Nt,Lt,vt,dt,lt,mt,gt,xt,Ct,Je,Ye,ot,Ft,ct,ft]}),St.ɵinj=t.ɵɵngDeclareInjector({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:St,imports:[[O,F,xe,Mt]]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:St,decorators:[{type:i,args:[{declarations:[ke,It,bt,it,qe,Ze,Xe,et,pt,tt,rt,at,ut,yt,ht,Nt,Lt,vt,dt,lt,mt,gt,xt,Ct,Je,Ye,ot,Ft,ct,ft],imports:[O,F,xe,Mt],exports:[ke,It,bt,it,qe,Ze,Xe,et,pt,tt,rt,at,ut,yt,ht,Nt,Lt,vt,dt,lt,mt,gt,xt,Ct,Je,Ye,ot,Ft,ct,ft]}]}]});class At extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.separatorKeysCodes=[Z,X,ee]}configForm(){return this.calculateDeltaConfigForm}onConfigurationSet(e){this.calculateDeltaConfigForm=this.fb.group({inputValueKey:[e?e.inputValueKey:null,[k.required]],outputValueKey:[e?e.outputValueKey:null,[k.required]],useCache:[e?e.useCache:null,[]],addPeriodBetweenMsgs:[!!e&&e.addPeriodBetweenMsgs,[]],periodValueKey:[e?e.periodValueKey:null,[]],round:[e?e.round:null,[k.min(0),k.max(15)]],tellFailureIfDeltaIsNegative:[e?e.tellFailureIfDeltaIsNegative:null,[]]})}updateValidators(e){this.calculateDeltaConfigForm.get("addPeriodBetweenMsgs").value?this.calculateDeltaConfigForm.get("periodValueKey").setValidators([k.required]):this.calculateDeltaConfigForm.get("periodValueKey").setValidators([]),this.calculateDeltaConfigForm.get("periodValueKey").updateValueAndValidity({emitEvent:e})}validatorTriggers(){return["addPeriodBetweenMsgs"]}}e("CalculateDeltaConfigComponent",At),At.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:At,deps:[{token:T.Store},{token:q.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),At.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"12.0.0",version:"12.2.15",type:At,selector:"tb-enrichment-node-calculate-delta-config",usesInheritance:!0,ngImport:t,template:'
\n
\n \n tb.rulenode.input-value-key\n \n \n {{ \'tb.rulenode.input-value-key-required\' | translate }}\n \n \n \n tb.rulenode.output-value-key\n \n \n {{ \'tb.rulenode.output-value-key-required\' | translate }}\n \n \n \n tb.rulenode.round\n \n \n {{ \'tb.rulenode.round-range\' | translate }}\n \n \n {{ \'tb.rulenode.round-range\' | translate }}\n \n \n
\n \n {{ \'tb.rulenode.use-cache\' | translate }}\n \n \n {{ \'tb.rulenode.tell-failure-if-delta-is-negative\' | translate }}\n \n \n {{ \'tb.rulenode.add-period-between-msgs\' | translate }}\n \n \n tb.rulenode.period-value-key\n \n \n {{ \'tb.rulenode.period-value-key-required\' | translate }}\n \n \n
\n',components:[{type:D.MatFormField,selector:"mat-form-field",inputs:["color","floatLabel","appearance","hideRequiredMarker","hintLabel"],exportAs:["matFormField"]},{type:V.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex","aria-label","aria-labelledby","id","labelPosition","name","required","checked","disabled","indeterminate","aria-describedby","value"],outputs:["change","indeterminateChange"],exportAs:["matCheckbox"]}],directives:[{type:E.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{type:q.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{type:q.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{type:E.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{type:E.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{type:D.MatLabel,selector:"mat-label"},{type:P.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{type:R.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["id","disabled","required","type","value","readonly","placeholder","errorStateMatcher","aria-describedby"],exportAs:["matInput"]},{type:q.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{type:q.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{type:q.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{type:q.FormControlName,selector:"[formControlName]",inputs:["disabled","formControlName","ngModel"],outputs:["ngModelChange"]},{type:w.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{type:D.MatError,selector:"mat-error",inputs:["id"]},{type:q.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{type:q.MaxValidator,selector:"input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]",inputs:["max"]},{type:q.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"}],pipes:{translate:P.TranslatePipe}}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:At,decorators:[{type:o,args:[{selector:"tb-enrichment-node-calculate-delta-config",templateUrl:"./calculate-delta-config.component.html",styleUrls:[]}]}],ctorParameters:function(){return[{type:T.Store},{type:q.FormBuilder}]}});class Gt extends s{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.customerAttributesConfigForm}onConfigurationSet(e){this.customerAttributesConfigForm=this.fb.group({telemetry:[!!e&&e.telemetry,[]],attrMapping:[e?e.attrMapping:null,[k.required]]})}}e("CustomerAttributesConfigComponent",Gt),Gt.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Gt,deps:[{token:T.Store},{token:q.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),Gt.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"12.0.0",version:"12.2.15",type:Gt,selector:"tb-enrichment-node-customer-attributes-config",usesInheritance:!0,ngImport:t,template:'
\n \n \n {{ \'tb.rulenode.latest-telemetry\' | translate }}\n \n \n \n
\n',components:[{type:V.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex","aria-label","aria-labelledby","id","labelPosition","name","required","checked","disabled","indeterminate","aria-describedby","value"],outputs:["change","indeterminateChange"],exportAs:["matCheckbox"]},{type:nt,selector:"tb-kv-map-config",inputs:["disabled","requiredText","keyText","keyRequiredText","valText","valRequiredText","hintText","required"]}],directives:[{type:E.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{type:q.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{type:q.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{type:P.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{type:E.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{type:q.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{type:q.FormControlName,selector:"[formControlName]",inputs:["disabled","formControlName","ngModel"],outputs:["ngModelChange"]},{type:q.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]}],pipes:{translate:P.TranslatePipe}}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Gt,decorators:[{type:o,args:[{selector:"tb-enrichment-node-customer-attributes-config",templateUrl:"./customer-attributes-config.component.html",styleUrls:[]}]}],ctorParameters:function(){return[{type:T.Store},{type:q.FormBuilder}]}});class Dt extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.separatorKeysCodes=[Z,X,ee]}configForm(){return this.deviceAttributesConfigForm}onConfigurationSet(e){this.deviceAttributesConfigForm=this.fb.group({deviceRelationsQuery:[e?e.deviceRelationsQuery:null,[k.required]],tellFailureIfAbsent:[!!e&&e.tellFailureIfAbsent,[]],clientAttributeNames:[e?e.clientAttributeNames:null,[]],sharedAttributeNames:[e?e.sharedAttributeNames:null,[]],serverAttributeNames:[e?e.serverAttributeNames:null,[]],latestTsKeyNames:[e?e.latestTsKeyNames:null,[]],getLatestValueWithTs:[!!e&&e.getLatestValueWithTs,[]]})}removeKey(e,t){const o=this.deviceAttributesConfigForm.get(t).value,r=o.indexOf(e);r>=0&&(o.splice(r,1),this.deviceAttributesConfigForm.get(t).setValue(o,{emitEvent:!0}))}addKey(e,t){const o=e.input;let r=e.value;if((r||"").trim()){r=r.trim();let e=this.deviceAttributesConfigForm.get(t).value;e&&-1!==e.indexOf(r)||(e||(e=[]),e.push(r),this.deviceAttributesConfigForm.get(t).setValue(e,{emitEvent:!0}))}o&&(o.value="")}}e("DeviceAttributesConfigComponent",Dt),Dt.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Dt,deps:[{token:T.Store},{token:q.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),Dt.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"12.0.0",version:"12.2.15",type:Dt,selector:"tb-enrichment-node-device-attributes-config",usesInheritance:!0,ngImport:t,template:'
\n \n \n \n \n {{ \'tb.rulenode.tell-failure-if-absent\' | translate }}\n \n
tb.rulenode.tell-failure-if-absent-hint
\n \n \n \n \n \n {{key}}\n close\n \n \n \n \n \n \n \n \n \n {{key}}\n close\n \n \n \n \n \n \n \n \n \n {{key}}\n close\n \n \n \n \n \n \n \n \n \n {{key}}\n close\n \n \n \n \n \n {{ \'tb.rulenode.get-latest-value-with-ts\' | translate }}\n \n
\n
\n',styles:[":host label.tb-title{margin-bottom:-10px}\n"],components:[{type:Tt,selector:"tb-device-relations-query-config",inputs:["disabled","required"]},{type:V.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex","aria-label","aria-labelledby","id","labelPosition","name","required","checked","disabled","indeterminate","aria-describedby","value"],outputs:["change","indeterminateChange"],exportAs:["matCheckbox"]},{type:D.MatFormField,selector:"mat-form-field",inputs:["color","floatLabel","appearance","hideRequiredMarker","hintLabel"],exportAs:["matFormField"]},{type:te.MatChipList,selector:"mat-chip-list",inputs:["aria-orientation","multiple","compareWith","value","required","placeholder","disabled","selectable","tabIndex","errorStateMatcher"],outputs:["change","valueChange"],exportAs:["matChipList"]},{type:oe.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]}],directives:[{type:E.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{type:q.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{type:q.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{type:P.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{type:q.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{type:q.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{type:q.FormControlName,selector:"[formControlName]",inputs:["disabled","formControlName","ngModel"],outputs:["ngModelChange"]},{type:E.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{type:D.MatLabel,selector:"mat-label"},{type:w.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{type:te.MatChip,selector:"mat-basic-chip, [mat-basic-chip], mat-chip, [mat-chip]",inputs:["color","disableRipple","tabIndex","selected","value","selectable","disabled","removable"],outputs:["selectionChange","destroyed","removed"],exportAs:["matChip"]},{type:te.MatChipRemove,selector:"[matChipRemove]"},{type:R.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["id","disabled","required","type","value","readonly","placeholder","errorStateMatcher","aria-describedby"],exportAs:["matInput"]},{type:te.MatChipInput,selector:"input[matChipInputFor]",inputs:["matChipInputSeparatorKeyCodes","placeholder","id","matChipInputFor","matChipInputAddOnBlur","disabled"],outputs:["matChipInputTokenEnd"],exportAs:["matChipInput","matChipInputFor"]}],pipes:{translate:P.TranslatePipe,safeHtml:Te}}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Dt,decorators:[{type:o,args:[{selector:"tb-enrichment-node-device-attributes-config",templateUrl:"./device-attributes-config.component.html",styleUrls:["./device-attributes-config.component.scss"]}]}],ctorParameters:function(){return[{type:T.Store},{type:q.FormBuilder}]}});class Vt extends s{constructor(e,t,o){super(e),this.store=e,this.translate=t,this.fb=o,this.entityDetailsTranslationsMap=we,this.entityDetailsList=[],this.searchText="",this.displayDetailsFn=this.displayDetails.bind(this);for(const e of Object.keys(Re))this.entityDetailsList.push(Re[e]);this.detailsFormControl=new G(""),this.filteredEntityDetails=this.detailsFormControl.valueChanges.pipe(ue(""),pe((e=>e||"")),de((e=>this.fetchEntityDetails(e))),fe())}ngOnInit(){super.ngOnInit()}configForm(){return this.entityDetailsConfigForm}prepareInputConfig(e){return this.searchText="",this.detailsFormControl.patchValue("",{emitEvent:!0}),e}onConfigurationSet(e){this.entityDetailsConfigForm=this.fb.group({detailsList:[e?e.detailsList:null,[k.required]],addToMetadata:[!!e&&e.addToMetadata,[]]})}displayDetails(e){return e?this.translate.instant(we.get(e)):void 0}fetchEntityDetails(e){if(this.searchText=e,this.searchText&&this.searchText.length){const e=this.searchText.toUpperCase();return Ce(this.entityDetailsList.filter((t=>this.translate.instant(we.get(Re[t])).toUpperCase().includes(e))))}return Ce(this.entityDetailsList)}detailsFieldSelected(e){this.addDetailsField(e.option.value),this.clear("")}removeDetailsField(e){const t=this.entityDetailsConfigForm.get("detailsList").value;if(t){const o=t.indexOf(e);o>=0&&(t.splice(o,1),this.entityDetailsConfigForm.get("detailsList").setValue(t))}}addDetailsField(e){let t=this.entityDetailsConfigForm.get("detailsList").value;t||(t=[]);-1===t.indexOf(e)&&(t.push(e),this.entityDetailsConfigForm.get("detailsList").setValue(t))}onEntityDetailsInputFocus(){this.detailsFormControl.updateValueAndValidity({onlySelf:!0,emitEvent:!0})}clear(e=""){this.detailsInput.nativeElement.value=e,this.detailsFormControl.patchValue(null,{emitEvent:!0}),setTimeout((()=>{this.detailsInput.nativeElement.blur(),this.detailsInput.nativeElement.focus()}),0)}}e("EntityDetailsConfigComponent",Vt),Vt.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Vt,deps:[{token:T.Store},{token:P.TranslateService},{token:q.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),Vt.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"12.0.0",version:"12.2.15",type:Vt,selector:"tb-enrichment-node-entity-details-config",viewQueries:[{propertyName:"detailsInput",first:!0,predicate:["detailsInput"],descendants:!0}],usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.entity-details\n \n \n \n {{entityDetailsTranslationsMap.get(details) | translate}}\n \n close\n \n \n \n \n \n \n \n \n
\n
\n tb.rulenode.no-entity-details-matching\n
\n
\n
\n
\n
\n \n \n {{ \'tb.rulenode.add-to-metadata\' | translate }}\n \n
tb.rulenode.add-to-metadata-hint
\n
\n',styles:[":host ::ng-deep mat-form-field.entity-fields-list .mat-form-field-wrapper{margin-bottom:-1.25em}\n"],components:[{type:D.MatFormField,selector:"mat-form-field",inputs:["color","floatLabel","appearance","hideRequiredMarker","hintLabel"],exportAs:["matFormField"]},{type:te.MatChipList,selector:"mat-chip-list",inputs:["aria-orientation","multiple","compareWith","value","required","placeholder","disabled","selectable","tabIndex","errorStateMatcher"],outputs:["change","valueChange"],exportAs:["matChipList"]},{type:oe.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{type:Fe.MatAutocomplete,selector:"mat-autocomplete",inputs:["disableRipple"],exportAs:["matAutocomplete"]},{type:K.MatOption,selector:"mat-option",exportAs:["matOption"]},{type:ie.TbErrorComponent,selector:"tb-error",inputs:["error"]},{type:V.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex","aria-label","aria-labelledby","id","labelPosition","name","required","checked","disabled","indeterminate","aria-describedby","value"],outputs:["change","indeterminateChange"],exportAs:["matCheckbox"]}],directives:[{type:E.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{type:q.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{type:q.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{type:D.MatLabel,selector:"mat-label"},{type:P.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{type:w.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{type:te.MatChip,selector:"mat-basic-chip, [mat-basic-chip], mat-chip, [mat-chip]",inputs:["color","disableRipple","tabIndex","selected","value","selectable","disabled","removable"],outputs:["selectionChange","destroyed","removed"],exportAs:["matChip"]},{type:te.MatChipRemove,selector:"[matChipRemove]"},{type:R.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["id","disabled","required","type","value","readonly","placeholder","errorStateMatcher","aria-describedby"],exportAs:["matInput"]},{type:q.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{type:Fe.MatAutocompleteTrigger,selector:"input[matAutocomplete], textarea[matAutocomplete]",exportAs:["matAutocompleteTrigger"]},{type:te.MatChipInput,selector:"input[matChipInputFor]",inputs:["matChipInputSeparatorKeyCodes","placeholder","id","matChipInputFor","matChipInputAddOnBlur","disabled"],outputs:["matChipInputTokenEnd"],exportAs:["matChipInput","matChipInputFor"]},{type:Fe.MatAutocompleteOrigin,selector:"[matAutocompleteOrigin]",exportAs:["matAutocompleteOrigin"]},{type:q.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{type:q.FormControlDirective,selector:"[formControl]",inputs:["disabled","formControl","ngModel"],outputs:["ngModelChange"],exportAs:["ngForm"]},{type:w.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{type:E.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{type:q.FormControlName,selector:"[formControlName]",inputs:["disabled","formControlName","ngModel"],outputs:["ngModelChange"]}],pipes:{translate:P.TranslatePipe,async:w.AsyncPipe,highlight:Le.HighlightPipe}}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Vt,decorators:[{type:o,args:[{selector:"tb-enrichment-node-entity-details-config",templateUrl:"./entity-details-config.component.html",styleUrls:["./entity-details-config.component.scss"]}]}],ctorParameters:function(){return[{type:T.Store},{type:P.TranslateService},{type:q.FormBuilder}]},propDecorators:{detailsInput:[{type:a,args:["detailsInput",{static:!1}]}]}});class Et extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.separatorKeysCodes=[Z,X,ee],this.aggregationTypes=L,this.aggregations=Object.keys(L),this.aggregationTypesTranslations=v,this.fetchMode=Oe,this.fetchModes=Object.keys(Oe),this.samplingOrders=Object.keys(He),this.timeUnits=Object.values(De),this.timeUnitsTranslationMap=Ve}configForm(){return this.getTelemetryFromDatabaseConfigForm}onConfigurationSet(e){this.getTelemetryFromDatabaseConfigForm=this.fb.group({latestTsKeyNames:[e?e.latestTsKeyNames:null,[]],aggregation:[e?e.aggregation:null,[k.required]],fetchMode:[e?e.fetchMode:null,[k.required]],orderBy:[e?e.orderBy:null,[]],limit:[e?e.limit:null,[]],useMetadataIntervalPatterns:[!!e&&e.useMetadataIntervalPatterns,[]],startInterval:[e?e.startInterval:null,[]],startIntervalTimeUnit:[e?e.startIntervalTimeUnit:null,[]],endInterval:[e?e.endInterval:null,[]],endIntervalTimeUnit:[e?e.endIntervalTimeUnit:null,[]],startIntervalPattern:[e?e.startIntervalPattern:null,[]],endIntervalPattern:[e?e.endIntervalPattern:null,[]]})}validatorTriggers(){return["fetchMode","useMetadataIntervalPatterns"]}updateValidators(e){const t=this.getTelemetryFromDatabaseConfigForm.get("fetchMode").value,o=this.getTelemetryFromDatabaseConfigForm.get("useMetadataIntervalPatterns").value;t&&t===Oe.ALL?(this.getTelemetryFromDatabaseConfigForm.get("aggregation").setValidators([k.required]),this.getTelemetryFromDatabaseConfigForm.get("orderBy").setValidators([k.required]),this.getTelemetryFromDatabaseConfigForm.get("limit").setValidators([k.required,k.min(2),k.max(1e3)])):(this.getTelemetryFromDatabaseConfigForm.get("aggregation").setValidators([]),this.getTelemetryFromDatabaseConfigForm.get("orderBy").setValidators([]),this.getTelemetryFromDatabaseConfigForm.get("limit").setValidators([])),o?(this.getTelemetryFromDatabaseConfigForm.get("startInterval").setValidators([]),this.getTelemetryFromDatabaseConfigForm.get("startIntervalTimeUnit").setValidators([]),this.getTelemetryFromDatabaseConfigForm.get("endInterval").setValidators([]),this.getTelemetryFromDatabaseConfigForm.get("endIntervalTimeUnit").setValidators([]),this.getTelemetryFromDatabaseConfigForm.get("startIntervalPattern").setValidators([k.required]),this.getTelemetryFromDatabaseConfigForm.get("endIntervalPattern").setValidators([k.required])):(this.getTelemetryFromDatabaseConfigForm.get("startInterval").setValidators([k.required,k.min(1),k.max(2147483647)]),this.getTelemetryFromDatabaseConfigForm.get("startIntervalTimeUnit").setValidators([k.required]),this.getTelemetryFromDatabaseConfigForm.get("endInterval").setValidators([k.required,k.min(1),k.max(2147483647)]),this.getTelemetryFromDatabaseConfigForm.get("endIntervalTimeUnit").setValidators([k.required]),this.getTelemetryFromDatabaseConfigForm.get("startIntervalPattern").setValidators([]),this.getTelemetryFromDatabaseConfigForm.get("endIntervalPattern").setValidators([])),this.getTelemetryFromDatabaseConfigForm.get("aggregation").updateValueAndValidity({emitEvent:e}),this.getTelemetryFromDatabaseConfigForm.get("orderBy").updateValueAndValidity({emitEvent:e}),this.getTelemetryFromDatabaseConfigForm.get("limit").updateValueAndValidity({emitEvent:e}),this.getTelemetryFromDatabaseConfigForm.get("startInterval").updateValueAndValidity({emitEvent:e}),this.getTelemetryFromDatabaseConfigForm.get("startIntervalTimeUnit").updateValueAndValidity({emitEvent:e}),this.getTelemetryFromDatabaseConfigForm.get("endInterval").updateValueAndValidity({emitEvent:e}),this.getTelemetryFromDatabaseConfigForm.get("endIntervalTimeUnit").updateValueAndValidity({emitEvent:e}),this.getTelemetryFromDatabaseConfigForm.get("startIntervalPattern").updateValueAndValidity({emitEvent:e}),this.getTelemetryFromDatabaseConfigForm.get("endIntervalPattern").updateValueAndValidity({emitEvent:e})}removeKey(e,t){const o=this.getTelemetryFromDatabaseConfigForm.get(t).value,r=o.indexOf(e);r>=0&&(o.splice(r,1),this.getTelemetryFromDatabaseConfigForm.get(t).setValue(o,{emitEvent:!0}))}addKey(e,t){const o=e.input;let r=e.value;if((r||"").trim()){r=r.trim();let e=this.getTelemetryFromDatabaseConfigForm.get(t).value;e&&-1!==e.indexOf(r)||(e||(e=[]),e.push(r),this.getTelemetryFromDatabaseConfigForm.get(t).setValue(e,{emitEvent:!0}))}o&&(o.value="")}}e("GetTelemetryFromDatabaseConfigComponent",Et),Et.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Et,deps:[{token:T.Store},{token:q.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),Et.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"12.0.0",version:"12.2.15",type:Et,selector:"tb-enrichment-node-get-telemetry-from-database",usesInheritance:!0,ngImport:t,template:'
\n \n \n \n \n \n {{key}}\n close\n \n \n \n \n \n \n tb.rulenode.fetch-mode\n \n \n {{ mode }}\n \n \n tb.rulenode.fetch-mode-hint\n \n
\n \n aggregation.function\n \n \n {{ aggregationTypesTranslations.get(aggregationTypes[aggregation]) | translate }}\n \n \n \n \n tb.rulenode.order-by\n \n \n {{ order }}\n \n \n tb.rulenode.order-by-hint\n \n \n tb.rulenode.limit\n \n tb.rulenode.limit-hint\n \n
\n \n {{ \'tb.rulenode.use-metadata-interval-patterns\' | translate }}\n \n
tb.rulenode.use-metadata-interval-patterns-hint
\n
\n
\n \n tb.rulenode.start-interval\n \n \n {{ \'tb.rulenode.start-interval-value-required\' | translate }}\n \n \n {{ \'tb.rulenode.time-value-range\' | translate }}\n \n \n {{ \'tb.rulenode.time-value-range\' | translate }}\n \n \n \n tb.rulenode.start-interval-time-unit\n \n \n {{ timeUnitsTranslationMap.get(timeUnit) | translate }}\n \n \n \n
\n
\n \n tb.rulenode.end-interval\n \n \n {{ \'tb.rulenode.end-interval-value-required\' | translate }}\n \n \n {{ \'tb.rulenode.time-value-range\' | translate }}\n \n \n {{ \'tb.rulenode.time-value-range\' | translate }}\n \n \n \n tb.rulenode.end-interval-time-unit\n \n \n {{ timeUnitsTranslationMap.get(timeUnit) | translate }}\n \n \n \n
\n
\n \n \n tb.rulenode.start-interval-pattern\n \n \n {{ \'tb.rulenode.start-interval-pattern-required\' | translate }}\n \n \n \n \n tb.rulenode.end-interval-pattern\n \n \n {{ \'tb.rulenode.end-interval-pattern-required\' | translate }}\n \n \n \n \n
\n',styles:[":host label.tb-title{margin-bottom:-10px}\n"],components:[{type:D.MatFormField,selector:"mat-form-field",inputs:["color","floatLabel","appearance","hideRequiredMarker","hintLabel"],exportAs:["matFormField"]},{type:te.MatChipList,selector:"mat-chip-list",inputs:["aria-orientation","multiple","compareWith","value","required","placeholder","disabled","selectable","tabIndex","errorStateMatcher"],outputs:["change","valueChange"],exportAs:["matChipList"]},{type:oe.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{type:U.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex"],exportAs:["matSelect"]},{type:K.MatOption,selector:"mat-option",exportAs:["matOption"]},{type:V.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex","aria-label","aria-labelledby","id","labelPosition","name","required","checked","disabled","indeterminate","aria-describedby","value"],outputs:["change","indeterminateChange"],exportAs:["matCheckbox"]}],directives:[{type:E.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{type:q.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{type:q.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{type:P.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{type:D.MatLabel,selector:"mat-label"},{type:w.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{type:te.MatChip,selector:"mat-basic-chip, [mat-basic-chip], mat-chip, [mat-chip]",inputs:["color","disableRipple","tabIndex","selected","value","selectable","disabled","removable"],outputs:["selectionChange","destroyed","removed"],exportAs:["matChip"]},{type:te.MatChipRemove,selector:"[matChipRemove]"},{type:R.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["id","disabled","required","type","value","readonly","placeholder","errorStateMatcher","aria-describedby"],exportAs:["matInput"]},{type:te.MatChipInput,selector:"input[matChipInputFor]",inputs:["matChipInputSeparatorKeyCodes","placeholder","id","matChipInputFor","matChipInputAddOnBlur","disabled"],outputs:["matChipInputTokenEnd"],exportAs:["matChipInput","matChipInputFor"]},{type:D.MatHint,selector:"mat-hint",inputs:["align","id"]},{type:q.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{type:q.FormControlName,selector:"[formControlName]",inputs:["disabled","formControlName","ngModel"],outputs:["ngModelChange"]},{type:q.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{type:w.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{type:q.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{type:q.MaxValidator,selector:"input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]",inputs:["max"]},{type:q.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{type:q.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{type:E.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{type:E.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{type:D.MatError,selector:"mat-error",inputs:["id"]}],pipes:{translate:P.TranslatePipe,safeHtml:Te}}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Et,decorators:[{type:o,args:[{selector:"tb-enrichment-node-get-telemetry-from-database",templateUrl:"./get-telemetry-from-database-config.component.html",styleUrls:["./get-telemetry-from-database-config.component.scss"]}]}],ctorParameters:function(){return[{type:T.Store},{type:q.FormBuilder}]}});class Pt extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.separatorKeysCodes=[Z,X,ee]}configForm(){return this.originatorAttributesConfigForm}onConfigurationSet(e){this.originatorAttributesConfigForm=this.fb.group({tellFailureIfAbsent:[!!e&&e.tellFailureIfAbsent,[]],clientAttributeNames:[e?e.clientAttributeNames:null,[]],sharedAttributeNames:[e?e.sharedAttributeNames:null,[]],serverAttributeNames:[e?e.serverAttributeNames:null,[]],latestTsKeyNames:[e?e.latestTsKeyNames:null,[]],getLatestValueWithTs:[!!e&&e.getLatestValueWithTs,[]]})}removeKey(e,t){const o=this.originatorAttributesConfigForm.get(t).value,r=o.indexOf(e);r>=0&&(o.splice(r,1),this.originatorAttributesConfigForm.get(t).setValue(o,{emitEvent:!0}))}addKey(e,t){const o=e.input;let r=e.value;if((r||"").trim()){r=r.trim();let e=this.originatorAttributesConfigForm.get(t).value;e&&-1!==e.indexOf(r)||(e||(e=[]),e.push(r),this.originatorAttributesConfigForm.get(t).setValue(e,{emitEvent:!0}))}o&&(o.value="")}}e("OriginatorAttributesConfigComponent",Pt),Pt.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Pt,deps:[{token:T.Store},{token:q.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),Pt.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"12.0.0",version:"12.2.15",type:Pt,selector:"tb-enrichment-node-originator-attributes-config",usesInheritance:!0,ngImport:t,template:'
\n \n {{ \'tb.rulenode.tell-failure-if-absent\' | translate }}\n \n
tb.rulenode.tell-failure-if-absent-hint
\n \n \n \n \n \n {{key}}\n close\n \n \n \n \n \n \n \n \n \n \n {{key}}\n close\n \n \n \n \n \n \n \n \n \n \n {{key}}\n close\n \n \n \n \n \n \n \n \n \n \n {{key}}\n close\n \n \n \n \n \n \n {{ \'tb.rulenode.get-latest-value-with-ts\' | translate }}\n \n
\n
\n',styles:[":host label.tb-title{margin-bottom:-10px}\n"],components:[{type:V.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex","aria-label","aria-labelledby","id","labelPosition","name","required","checked","disabled","indeterminate","aria-describedby","value"],outputs:["change","indeterminateChange"],exportAs:["matCheckbox"]},{type:D.MatFormField,selector:"mat-form-field",inputs:["color","floatLabel","appearance","hideRequiredMarker","hintLabel"],exportAs:["matFormField"]},{type:te.MatChipList,selector:"mat-chip-list",inputs:["aria-orientation","multiple","compareWith","value","required","placeholder","disabled","selectable","tabIndex","errorStateMatcher"],outputs:["change","valueChange"],exportAs:["matChipList"]},{type:oe.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]}],directives:[{type:E.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{type:q.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{type:q.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{type:E.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{type:q.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{type:q.FormControlName,selector:"[formControlName]",inputs:["disabled","formControlName","ngModel"],outputs:["ngModelChange"]},{type:P.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{type:D.MatLabel,selector:"mat-label"},{type:w.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{type:te.MatChip,selector:"mat-basic-chip, [mat-basic-chip], mat-chip, [mat-chip]",inputs:["color","disableRipple","tabIndex","selected","value","selectable","disabled","removable"],outputs:["selectionChange","destroyed","removed"],exportAs:["matChip"]},{type:te.MatChipRemove,selector:"[matChipRemove]"},{type:R.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["id","disabled","required","type","value","readonly","placeholder","errorStateMatcher","aria-describedby"],exportAs:["matInput"]},{type:te.MatChipInput,selector:"input[matChipInputFor]",inputs:["matChipInputSeparatorKeyCodes","placeholder","id","matChipInputFor","matChipInputAddOnBlur","disabled"],outputs:["matChipInputTokenEnd"],exportAs:["matChipInput","matChipInputFor"]},{type:D.MatHint,selector:"mat-hint",inputs:["align","id"]}],pipes:{translate:P.TranslatePipe,safeHtml:Te}}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Pt,decorators:[{type:o,args:[{selector:"tb-enrichment-node-originator-attributes-config",templateUrl:"./originator-attributes-config.component.html",styleUrls:["./originator-attributes-config.component.scss"]}]}],ctorParameters:function(){return[{type:T.Store},{type:q.FormBuilder}]}});class Rt extends s{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.originatorFieldsConfigForm}onConfigurationSet(e){this.originatorFieldsConfigForm=this.fb.group({fieldsMapping:[e?e.fieldsMapping:null,[k.required]]})}}e("OriginatorFieldsConfigComponent",Rt),Rt.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Rt,deps:[{token:T.Store},{token:q.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),Rt.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"12.0.0",version:"12.2.15",type:Rt,selector:"tb-enrichment-node-originator-fields-config",usesInheritance:!0,ngImport:t,template:'
\n \n \n \n
\n',components:[{type:nt,selector:"tb-kv-map-config",inputs:["disabled","requiredText","keyText","keyRequiredText","valText","valRequiredText","hintText","required"]}],directives:[{type:E.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{type:q.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{type:q.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{type:P.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{type:q.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{type:q.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{type:q.FormControlName,selector:"[formControlName]",inputs:["disabled","formControlName","ngModel"],outputs:["ngModelChange"]}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Rt,decorators:[{type:o,args:[{selector:"tb-enrichment-node-originator-fields-config",templateUrl:"./originator-fields-config.component.html",styleUrls:[]}]}],ctorParameters:function(){return[{type:T.Store},{type:q.FormBuilder}]}});class wt extends s{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.relatedAttributesConfigForm}onConfigurationSet(e){this.relatedAttributesConfigForm=this.fb.group({relationsQuery:[e?e.relationsQuery:null,[k.required]],telemetry:[!!e&&e.telemetry,[]],attrMapping:[e?e.attrMapping:null,[k.required]]})}}e("RelatedAttributesConfigComponent",wt),wt.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:wt,deps:[{token:T.Store},{token:q.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),wt.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"12.0.0",version:"12.2.15",type:wt,selector:"tb-enrichment-node-related-attributes-config",usesInheritance:!0,ngImport:t,template:'
\n \n \n \n \n \n {{ \'tb.rulenode.latest-telemetry\' | translate }}\n \n \n \n
\n',components:[{type:qt,selector:"tb-relations-query-config",inputs:["disabled","required"]},{type:V.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex","aria-label","aria-labelledby","id","labelPosition","name","required","checked","disabled","indeterminate","aria-describedby","value"],outputs:["change","indeterminateChange"],exportAs:["matCheckbox"]},{type:nt,selector:"tb-kv-map-config",inputs:["disabled","requiredText","keyText","keyRequiredText","valText","valRequiredText","hintText","required"]}],directives:[{type:E.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{type:q.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{type:q.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{type:P.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{type:q.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{type:q.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{type:q.FormControlName,selector:"[formControlName]",inputs:["disabled","formControlName","ngModel"],outputs:["ngModelChange"]},{type:E.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]}],pipes:{translate:P.TranslatePipe}}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:wt,decorators:[{type:o,args:[{selector:"tb-enrichment-node-related-attributes-config",templateUrl:"./related-attributes-config.component.html",styleUrls:[]}]}],ctorParameters:function(){return[{type:T.Store},{type:q.FormBuilder}]}});class Ot extends s{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.tenantAttributesConfigForm}onConfigurationSet(e){this.tenantAttributesConfigForm=this.fb.group({telemetry:[!!e&&e.telemetry,[]],attrMapping:[e?e.attrMapping:null,[k.required]]})}}e("TenantAttributesConfigComponent",Ot),Ot.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Ot,deps:[{token:T.Store},{token:q.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),Ot.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"12.0.0",version:"12.2.15",type:Ot,selector:"tb-enrichment-node-tenant-attributes-config",usesInheritance:!0,ngImport:t,template:'
\n \n \n {{ \'tb.rulenode.latest-telemetry\' | translate }}\n \n \n \n
\n',components:[{type:V.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex","aria-label","aria-labelledby","id","labelPosition","name","required","checked","disabled","indeterminate","aria-describedby","value"],outputs:["change","indeterminateChange"],exportAs:["matCheckbox"]},{type:nt,selector:"tb-kv-map-config",inputs:["disabled","requiredText","keyText","keyRequiredText","valText","valRequiredText","hintText","required"]}],directives:[{type:E.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{type:q.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{type:q.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{type:P.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{type:E.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{type:q.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{type:q.FormControlName,selector:"[formControlName]",inputs:["disabled","formControlName","ngModel"],outputs:["ngModelChange"]},{type:q.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]}],pipes:{translate:P.TranslatePipe}}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Ot,decorators:[{type:o,args:[{selector:"tb-enrichment-node-tenant-attributes-config",templateUrl:"./tenant-attributes-config.component.html",styleUrls:[]}]}],ctorParameters:function(){return[{type:T.Store},{type:q.FormBuilder}]}});class Ht{}e("RulenodeCoreConfigEnrichmentModule",Ht),Ht.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Ht,deps:[],target:t.ɵɵFactoryTarget.NgModule}),Ht.ɵmod=t.ɵɵngDeclareNgModule({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Ht,declarations:[Gt,Vt,Dt,Pt,Rt,Et,wt,Ot,At],imports:[O,F,Mt],exports:[Gt,Vt,Dt,Pt,Rt,Et,wt,Ot,At]}),Ht.ɵinj=t.ɵɵngDeclareInjector({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Ht,imports:[[O,F,Mt]]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Ht,decorators:[{type:i,args:[{declarations:[Gt,Vt,Dt,Pt,Rt,Et,wt,Ot,At],imports:[O,F,Mt],exports:[Gt,Vt,Dt,Pt,Rt,Et,wt,Ot,At]}]}]});class Ut extends s{constructor(e,t,o){super(e),this.store=e,this.translate=t,this.fb=o,this.alarmStatusTranslationsMap=I,this.alarmStatusList=[],this.searchText="",this.displayStatusFn=this.displayStatus.bind(this);for(const e of Object.keys(N))this.alarmStatusList.push(N[e]);this.statusFormControl=new G(""),this.filteredAlarmStatus=this.statusFormControl.valueChanges.pipe(ue(""),pe((e=>e||"")),de((e=>this.fetchAlarmStatus(e))),fe())}ngOnInit(){super.ngOnInit()}configForm(){return this.alarmStatusConfigForm}prepareInputConfig(e){return this.searchText="",this.statusFormControl.patchValue("",{emitEvent:!0}),e}onConfigurationSet(e){this.alarmStatusConfigForm=this.fb.group({alarmStatusList:[e?e.alarmStatusList:null,[k.required]]})}displayStatus(e){return e?this.translate.instant(I.get(e)):void 0}fetchAlarmStatus(e){const t=this.getAlarmStatusList();if(this.searchText=e,this.searchText&&this.searchText.length){const e=this.searchText.toUpperCase();return Ce(t.filter((t=>this.translate.instant(I.get(N[t])).toUpperCase().includes(e))))}return Ce(t)}alarmStatusSelected(e){this.addAlarmStatus(e.option.value),this.clear("")}removeAlarmStatus(e){const t=this.alarmStatusConfigForm.get("alarmStatusList").value;if(t){const o=t.indexOf(e);o>=0&&(t.splice(o,1),this.alarmStatusConfigForm.get("alarmStatusList").setValue(t))}}addAlarmStatus(e){let t=this.alarmStatusConfigForm.get("alarmStatusList").value;t||(t=[]);-1===t.indexOf(e)&&(t.push(e),this.alarmStatusConfigForm.get("alarmStatusList").setValue(t))}getAlarmStatusList(){return this.alarmStatusList.filter((e=>-1===this.alarmStatusConfigForm.get("alarmStatusList").value.indexOf(e)))}onAlarmStatusInputFocus(){this.statusFormControl.updateValueAndValidity({onlySelf:!0,emitEvent:!0})}clear(e=""){this.alarmStatusInput.nativeElement.value=e,this.statusFormControl.patchValue(null,{emitEvent:!0}),setTimeout((()=>{this.alarmStatusInput.nativeElement.blur(),this.alarmStatusInput.nativeElement.focus()}),0)}}e("CheckAlarmStatusComponent",Ut),Ut.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Ut,deps:[{token:T.Store},{token:P.TranslateService},{token:q.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),Ut.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"12.0.0",version:"12.2.15",type:Ut,selector:"tb-filter-node-check-alarm-status-config",viewQueries:[{propertyName:"alarmStatusInput",first:!0,predicate:["alarmStatusInput"],descendants:!0}],usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.alarm-status-filter\n \n \n \n {{alarmStatusTranslationsMap.get(alarmStatus) | translate}}\n \n close\n \n \n \n \n \n \n \n \n
\n
\n tb.rulenode.no-alarm-status-matching\n
\n
\n
\n
\n
\n \n
\n\n\n\n',components:[{type:D.MatFormField,selector:"mat-form-field",inputs:["color","floatLabel","appearance","hideRequiredMarker","hintLabel"],exportAs:["matFormField"]},{type:te.MatChipList,selector:"mat-chip-list",inputs:["aria-orientation","multiple","compareWith","value","required","placeholder","disabled","selectable","tabIndex","errorStateMatcher"],outputs:["change","valueChange"],exportAs:["matChipList"]},{type:oe.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{type:Fe.MatAutocomplete,selector:"mat-autocomplete",inputs:["disableRipple"],exportAs:["matAutocomplete"]},{type:K.MatOption,selector:"mat-option",exportAs:["matOption"]},{type:ie.TbErrorComponent,selector:"tb-error",inputs:["error"]}],directives:[{type:E.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{type:q.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{type:q.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{type:D.MatLabel,selector:"mat-label"},{type:P.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{type:w.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{type:te.MatChip,selector:"mat-basic-chip, [mat-basic-chip], mat-chip, [mat-chip]",inputs:["color","disableRipple","tabIndex","selected","value","selectable","disabled","removable"],outputs:["selectionChange","destroyed","removed"],exportAs:["matChip"]},{type:te.MatChipRemove,selector:"[matChipRemove]"},{type:R.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["id","disabled","required","type","value","readonly","placeholder","errorStateMatcher","aria-describedby"],exportAs:["matInput"]},{type:q.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{type:Fe.MatAutocompleteTrigger,selector:"input[matAutocomplete], textarea[matAutocomplete]",exportAs:["matAutocompleteTrigger"]},{type:te.MatChipInput,selector:"input[matChipInputFor]",inputs:["matChipInputSeparatorKeyCodes","placeholder","id","matChipInputFor","matChipInputAddOnBlur","disabled"],outputs:["matChipInputTokenEnd"],exportAs:["matChipInput","matChipInputFor"]},{type:Fe.MatAutocompleteOrigin,selector:"[matAutocompleteOrigin]",exportAs:["matAutocompleteOrigin"]},{type:q.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{type:q.FormControlDirective,selector:"[formControl]",inputs:["disabled","formControl","ngModel"],outputs:["ngModelChange"],exportAs:["ngForm"]},{type:w.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]}],pipes:{translate:P.TranslatePipe,async:w.AsyncPipe,highlight:Le.HighlightPipe}}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Ut,decorators:[{type:o,args:[{selector:"tb-filter-node-check-alarm-status-config",templateUrl:"./check-alarm-status.component.html",styleUrls:[]}]}],ctorParameters:function(){return[{type:T.Store},{type:P.TranslateService},{type:q.FormBuilder}]},propDecorators:{alarmStatusInput:[{type:a,args:["alarmStatusInput",{static:!1}]}]}});class Kt extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.separatorKeysCodes=[Z,X,ee]}configForm(){return this.checkMessageConfigForm}onConfigurationSet(e){this.checkMessageConfigForm=this.fb.group({messageNames:[e?e.messageNames:null,[]],metadataNames:[e?e.metadataNames:null,[]],checkAllKeys:[!!e&&e.checkAllKeys,[]]})}validateConfig(){const e=this.checkMessageConfigForm.get("messageNames").value,t=this.checkMessageConfigForm.get("metadataNames").value;return e.length>0||t.length>0}removeMessageName(e){const t=this.checkMessageConfigForm.get("messageNames").value,o=t.indexOf(e);o>=0&&(t.splice(o,1),this.checkMessageConfigForm.get("messageNames").setValue(t,{emitEvent:!0}))}removeMetadataName(e){const t=this.checkMessageConfigForm.get("metadataNames").value,o=t.indexOf(e);o>=0&&(t.splice(o,1),this.checkMessageConfigForm.get("metadataNames").setValue(t,{emitEvent:!0}))}addMessageName(e){const t=e.input;let o=e.value;if((o||"").trim()){o=o.trim();let e=this.checkMessageConfigForm.get("messageNames").value;e&&-1!==e.indexOf(o)||(e||(e=[]),e.push(o),this.checkMessageConfigForm.get("messageNames").setValue(e,{emitEvent:!0}))}t&&(t.value="")}addMetadataName(e){const t=e.input;let o=e.value;if((o||"").trim()){o=o.trim();let e=this.checkMessageConfigForm.get("metadataNames").value;e&&-1!==e.indexOf(o)||(e||(e=[]),e.push(o),this.checkMessageConfigForm.get("metadataNames").setValue(e,{emitEvent:!0}))}t&&(t.value="")}}e("CheckMessageConfigComponent",Kt),Kt.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Kt,deps:[{token:T.Store},{token:q.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),Kt.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"12.0.0",version:"12.2.15",type:Kt,selector:"tb-filter-node-check-message-config",usesInheritance:!0,ngImport:t,template:'
\n \n \n \n \n \n {{messageName}}\n close\n \n \n \n \n
tb.rulenode.separator-hint
\n \n \n \n \n \n {{metadataName}}\n close\n \n \n \n \n
tb.rulenode.separator-hint
\n \n {{ \'tb.rulenode.check-all-keys\' | translate }}\n \n
tb.rulenode.check-all-keys-hint
\n
\n',styles:[":host label.tb-title{margin-bottom:-10px}\n"],components:[{type:D.MatFormField,selector:"mat-form-field",inputs:["color","floatLabel","appearance","hideRequiredMarker","hintLabel"],exportAs:["matFormField"]},{type:te.MatChipList,selector:"mat-chip-list",inputs:["aria-orientation","multiple","compareWith","value","required","placeholder","disabled","selectable","tabIndex","errorStateMatcher"],outputs:["change","valueChange"],exportAs:["matChipList"]},{type:oe.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{type:V.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex","aria-label","aria-labelledby","id","labelPosition","name","required","checked","disabled","indeterminate","aria-describedby","value"],outputs:["change","indeterminateChange"],exportAs:["matCheckbox"]}],directives:[{type:E.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{type:q.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{type:q.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{type:P.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{type:D.MatLabel,selector:"mat-label"},{type:w.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{type:te.MatChip,selector:"mat-basic-chip, [mat-basic-chip], mat-chip, [mat-chip]",inputs:["color","disableRipple","tabIndex","selected","value","selectable","disabled","removable"],outputs:["selectionChange","destroyed","removed"],exportAs:["matChip"]},{type:te.MatChipRemove,selector:"[matChipRemove]"},{type:R.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["id","disabled","required","type","value","readonly","placeholder","errorStateMatcher","aria-describedby"],exportAs:["matInput"]},{type:te.MatChipInput,selector:"input[matChipInputFor]",inputs:["matChipInputSeparatorKeyCodes","placeholder","id","matChipInputFor","matChipInputAddOnBlur","disabled"],outputs:["matChipInputTokenEnd"],exportAs:["matChipInput","matChipInputFor"]},{type:E.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{type:q.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{type:q.FormControlName,selector:"[formControlName]",inputs:["disabled","formControlName","ngModel"],outputs:["ngModelChange"]}],pipes:{translate:P.TranslatePipe}}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Kt,decorators:[{type:o,args:[{selector:"tb-filter-node-check-message-config",templateUrl:"./check-message-config.component.html",styleUrls:["./check-message-config.component.scss"]}]}],ctorParameters:function(){return[{type:T.Store},{type:q.FormBuilder}]}});class Bt extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.entitySearchDirection=Object.keys(c),this.entitySearchDirectionTranslationsMap=g}configForm(){return this.checkRelationConfigForm}onConfigurationSet(e){this.checkRelationConfigForm=this.fb.group({checkForSingleEntity:[!!e&&e.checkForSingleEntity,[]],direction:[e?e.direction:null,[]],entityType:[e?e.entityType:null,e&&e.checkForSingleEntity?[k.required]:[]],entityId:[e?e.entityId:null,e&&e.checkForSingleEntity?[k.required]:[]],relationType:[e?e.relationType:null,[k.required]]})}validatorTriggers(){return["checkForSingleEntity"]}updateValidators(e){const t=this.checkRelationConfigForm.get("checkForSingleEntity").value;this.checkRelationConfigForm.get("entityType").setValidators(t?[k.required]:[]),this.checkRelationConfigForm.get("entityType").updateValueAndValidity({emitEvent:e}),this.checkRelationConfigForm.get("entityId").setValidators(t?[k.required]:[]),this.checkRelationConfigForm.get("entityId").updateValueAndValidity({emitEvent:e})}}e("CheckRelationConfigComponent",Bt),Bt.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Bt,deps:[{token:T.Store},{token:q.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),Bt.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"12.0.0",version:"12.2.15",type:Bt,selector:"tb-filter-node-check-relation-config",usesInheritance:!0,ngImport:t,template:'
\n \n {{ \'tb.rulenode.check-relation-to-specific-entity\' | translate }}\n \n
tb.rulenode.check-relation-hint
\n \n relation.direction\n \n \n {{ entitySearchDirectionTranslationsMap.get(direction) | translate }}\n \n \n \n
\n \n \n \n \n
\n \n \n
\n',components:[{type:V.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex","aria-label","aria-labelledby","id","labelPosition","name","required","checked","disabled","indeterminate","aria-describedby","value"],outputs:["change","indeterminateChange"],exportAs:["matCheckbox"]},{type:D.MatFormField,selector:"mat-form-field",inputs:["color","floatLabel","appearance","hideRequiredMarker","hintLabel"],exportAs:["matFormField"]},{type:U.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex"],exportAs:["matSelect"]},{type:K.MatOption,selector:"mat-option",exportAs:["matOption"]},{type:ae.EntityTypeSelectComponent,selector:"tb-entity-type-select",inputs:["allowedEntityTypes","useAliasEntityTypes","showLabel","required","disabled"]},{type:ve.EntityAutocompleteComponent,selector:"tb-entity-autocomplete",inputs:["entityType","entitySubtype","excludeEntityIds","labelText","requiredText","required","disabled"],outputs:["entityChanged"]},{type:ye.RelationTypeAutocompleteComponent,selector:"tb-relation-type-autocomplete",inputs:["required","disabled"]}],directives:[{type:E.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{type:q.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{type:q.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{type:E.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{type:q.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{type:q.FormControlName,selector:"[formControlName]",inputs:["disabled","formControlName","ngModel"],outputs:["ngModelChange"]},{type:P.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{type:D.MatLabel,selector:"mat-label"},{type:q.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{type:w.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{type:w.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]}],pipes:{translate:P.TranslatePipe}}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Bt,decorators:[{type:o,args:[{selector:"tb-filter-node-check-relation-config",templateUrl:"./check-relation-config.component.html",styleUrls:[]}]}],ctorParameters:function(){return[{type:T.Store},{type:q.FormBuilder}]}});class jt extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.perimeterType=Ae,this.perimeterTypes=Object.keys(Ae),this.perimeterTypeTranslationMap=Ge,this.rangeUnits=Object.keys(Ee),this.rangeUnitTranslationMap=Pe}configForm(){return this.geoFilterConfigForm}onConfigurationSet(e){this.geoFilterConfigForm=this.fb.group({latitudeKeyName:[e?e.latitudeKeyName:null,[k.required]],longitudeKeyName:[e?e.longitudeKeyName:null,[k.required]],perimeterType:[e?e.perimeterType:null,[k.required]],fetchPerimeterInfoFromMessageMetadata:[!!e&&e.fetchPerimeterInfoFromMessageMetadata,[]],perimeterKeyName:[e?e.perimeterKeyName:null,[]],centerLatitude:[e?e.centerLatitude:null,[]],centerLongitude:[e?e.centerLatitude:null,[]],range:[e?e.range:null,[]],rangeUnit:[e?e.rangeUnit:null,[]],polygonsDefinition:[e?e.polygonsDefinition:null,[]]})}validatorTriggers(){return["fetchPerimeterInfoFromMessageMetadata","perimeterType"]}updateValidators(e){const t=this.geoFilterConfigForm.get("fetchPerimeterInfoFromMessageMetadata").value,o=this.geoFilterConfigForm.get("perimeterType").value;t?this.geoFilterConfigForm.get("perimeterKeyName").setValidators([k.required]):this.geoFilterConfigForm.get("perimeterKeyName").setValidators([]),t||o!==Ae.CIRCLE?(this.geoFilterConfigForm.get("centerLatitude").setValidators([]),this.geoFilterConfigForm.get("centerLongitude").setValidators([]),this.geoFilterConfigForm.get("range").setValidators([]),this.geoFilterConfigForm.get("rangeUnit").setValidators([])):(this.geoFilterConfigForm.get("centerLatitude").setValidators([k.required,k.min(-90),k.max(90)]),this.geoFilterConfigForm.get("centerLongitude").setValidators([k.required,k.min(-180),k.max(180)]),this.geoFilterConfigForm.get("range").setValidators([k.required,k.min(0)]),this.geoFilterConfigForm.get("rangeUnit").setValidators([k.required])),t||o!==Ae.POLYGON?this.geoFilterConfigForm.get("polygonsDefinition").setValidators([]):this.geoFilterConfigForm.get("polygonsDefinition").setValidators([k.required]),this.geoFilterConfigForm.get("perimeterKeyName").updateValueAndValidity({emitEvent:e}),this.geoFilterConfigForm.get("centerLatitude").updateValueAndValidity({emitEvent:e}),this.geoFilterConfigForm.get("centerLongitude").updateValueAndValidity({emitEvent:e}),this.geoFilterConfigForm.get("range").updateValueAndValidity({emitEvent:e}),this.geoFilterConfigForm.get("rangeUnit").updateValueAndValidity({emitEvent:e}),this.geoFilterConfigForm.get("polygonsDefinition").updateValueAndValidity({emitEvent:e})}}e("GpsGeoFilterConfigComponent",jt),jt.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:jt,deps:[{token:T.Store},{token:q.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),jt.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"12.0.0",version:"12.2.15",type:jt,selector:"tb-filter-node-gps-geofencing-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.latitude-key-name\n \n \n {{ \'tb.rulenode.latitude-key-name-required\' | translate }}\n \n \n \n tb.rulenode.longitude-key-name\n \n \n {{ \'tb.rulenode.longitude-key-name-required\' | translate }}\n \n \n \n tb.rulenode.perimeter-type\n \n \n {{ perimeterTypeTranslationMap.get(type) | translate }}\n \n \n \n \n {{ \'tb.rulenode.fetch-perimeter-info-from-message-metadata\' | translate }}\n \n \n tb.rulenode.perimeter-key-name\n \n \n {{ \'tb.rulenode.perimeter-key-name-required\' | translate }}\n \n \n
\n
\n \n tb.rulenode.circle-center-latitude\n \n \n {{ \'tb.rulenode.circle-center-latitude-required\' | translate }}\n \n \n \n tb.rulenode.circle-center-longitude\n \n \n {{ \'tb.rulenode.circle-center-longitude-required\' | translate }}\n \n \n
\n
\n \n tb.rulenode.range\n \n \n {{ \'tb.rulenode.range-required\' | translate }}\n \n \n \n tb.rulenode.range-units\n \n \n {{ rangeUnitTranslationMap.get(type) | translate }}\n \n \n \n
\n
\n
\n
\n \n tb.rulenode.polygon-definition\n \n \n {{ \'tb.rulenode.polygon-definition-required\' | translate }}\n \n \n
\n
\n
\n',components:[{type:D.MatFormField,selector:"mat-form-field",inputs:["color","floatLabel","appearance","hideRequiredMarker","hintLabel"],exportAs:["matFormField"]},{type:U.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex"],exportAs:["matSelect"]},{type:K.MatOption,selector:"mat-option",exportAs:["matOption"]},{type:V.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex","aria-label","aria-labelledby","id","labelPosition","name","required","checked","disabled","indeterminate","aria-describedby","value"],outputs:["change","indeterminateChange"],exportAs:["matCheckbox"]}],directives:[{type:E.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{type:q.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{type:q.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{type:D.MatLabel,selector:"mat-label"},{type:P.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{type:R.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["id","disabled","required","type","value","readonly","placeholder","errorStateMatcher","aria-describedby"],exportAs:["matInput"]},{type:q.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{type:q.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{type:q.FormControlName,selector:"[formControlName]",inputs:["disabled","formControlName","ngModel"],outputs:["ngModelChange"]},{type:q.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{type:w.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{type:D.MatError,selector:"mat-error",inputs:["id"]},{type:E.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{type:w.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{type:E.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{type:q.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{type:q.MaxValidator,selector:"input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]",inputs:["max"]},{type:q.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"}],pipes:{translate:P.TranslatePipe}}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:jt,decorators:[{type:o,args:[{selector:"tb-filter-node-gps-geofencing-config",templateUrl:"./gps-geo-filter-config.component.html",styleUrls:[]}]}],ctorParameters:function(){return[{type:T.Store},{type:q.FormBuilder}]}});class zt extends s{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.messageTypeConfigForm}onConfigurationSet(e){this.messageTypeConfigForm=this.fb.group({messageTypes:[e?e.messageTypes:null,[k.required]]})}}e("MessageTypeConfigComponent",zt),zt.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:zt,deps:[{token:T.Store},{token:q.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),zt.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"12.0.0",version:"12.2.15",type:zt,selector:"tb-filter-node-message-type-config",usesInheritance:!0,ngImport:t,template:'
\n \n
\n',components:[{type:kt,selector:"tb-message-types-config",inputs:["required","label","placeholder","disabled"]}],directives:[{type:E.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{type:q.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{type:q.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{type:q.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{type:q.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{type:q.FormControlName,selector:"[formControlName]",inputs:["disabled","formControlName","ngModel"],outputs:["ngModelChange"]}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:zt,decorators:[{type:o,args:[{selector:"tb-filter-node-message-type-config",templateUrl:"./message-type-config.component.html",styleUrls:[]}]}],ctorParameters:function(){return[{type:T.Store},{type:q.FormBuilder}]}});class _t extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.allowedEntityTypes=[x.DEVICE,x.ASSET,x.ENTITY_VIEW,x.TENANT,x.CUSTOMER,x.USER,x.DASHBOARD,x.RULE_CHAIN,x.RULE_NODE]}configForm(){return this.originatorTypeConfigForm}onConfigurationSet(e){this.originatorTypeConfigForm=this.fb.group({originatorTypes:[e?e.originatorTypes:null,[k.required]]})}}e("OriginatorTypeConfigComponent",_t),_t.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:_t,deps:[{token:T.Store},{token:q.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),_t.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"12.0.0",version:"12.2.15",type:_t,selector:"tb-filter-node-originator-type-config",usesInheritance:!0,ngImport:t,template:'
\n \n \n \n
\n',styles:[":host ::ng-deep tb-entity-type-list .mat-form-field-flex{padding-top:0}:host ::ng-deep tb-entity-type-list .mat-form-field-infix{border-top:0}\n"],components:[{type:Ie.EntityTypeListComponent,selector:"tb-entity-type-list",inputs:["required","disabled","allowedEntityTypes","ignoreAuthorityFilter"]}],directives:[{type:E.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{type:q.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{type:q.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{type:P.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{type:E.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{type:q.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{type:q.FormControlName,selector:"[formControlName]",inputs:["disabled","formControlName","ngModel"],outputs:["ngModelChange"]},{type:q.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:_t,decorators:[{type:o,args:[{selector:"tb-filter-node-originator-type-config",templateUrl:"./originator-type-config.component.html",styleUrls:["./originator-type-config.component.scss"]}]}],ctorParameters:function(){return[{type:T.Store},{type:q.FormBuilder}]}});class Qt extends s{constructor(e,t,o,r){super(e),this.store=e,this.fb=t,this.nodeScriptTestService=o,this.translate=r}configForm(){return this.scriptConfigForm}onConfigurationSet(e){this.scriptConfigForm=this.fb.group({jsScript:[e?e.jsScript:null,[k.required]]})}testScript(){const e=this.scriptConfigForm.get("jsScript").value;this.nodeScriptTestService.testNodeScript(e,"filter",this.translate.instant("tb.rulenode.filter"),"Filter",["msg","metadata","msgType"],this.ruleNodeId,"rulenode/filter_node_script_fn").subscribe((e=>{e&&this.scriptConfigForm.get("jsScript").setValue(e)}))}onValidate(){this.jsFuncComponent.validateOnSubmit()}}e("ScriptConfigComponent",Qt),Qt.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Qt,deps:[{token:T.Store},{token:q.FormBuilder},{token:Q.NodeScriptTestService},{token:P.TranslateService}],target:t.ɵɵFactoryTarget.Component}),Qt.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"12.0.0",version:"12.2.15",type:Qt,selector:"tb-filter-node-script-config",viewQueries:[{propertyName:"jsFuncComponent",first:!0,predicate:["jsFuncComponent"],descendants:!0,static:!0}],usesInheritance:!0,ngImport:t,template:'
\n \n \n \n
\n \n
\n
\n',components:[{type:Y.JsFuncComponent,selector:"tb-js-func",inputs:["functionName","functionArgs","validationArgs","resultType","disabled","fillHeight","editorCompleter","globalVariables","disableUndefinedCheck","helpId","noValidate","required"]},{type:J.MatButton,selector:"button[mat-button], button[mat-raised-button], button[mat-icon-button], button[mat-fab], button[mat-mini-fab], button[mat-stroked-button], button[mat-flat-button]",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]}],directives:[{type:E.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{type:q.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{type:q.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{type:P.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{type:q.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{type:q.FormControlName,selector:"[formControlName]",inputs:["disabled","formControlName","ngModel"],outputs:["ngModelChange"]}],pipes:{translate:P.TranslatePipe}}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Qt,decorators:[{type:o,args:[{selector:"tb-filter-node-script-config",templateUrl:"./script-config.component.html",styleUrls:[]}]}],ctorParameters:function(){return[{type:T.Store},{type:q.FormBuilder},{type:Q.NodeScriptTestService},{type:P.TranslateService}]},propDecorators:{jsFuncComponent:[{type:a,args:["jsFuncComponent",{static:!0}]}]}});class $t extends s{constructor(e,t,o,r){super(e),this.store=e,this.fb=t,this.nodeScriptTestService=o,this.translate=r}configForm(){return this.switchConfigForm}onConfigurationSet(e){this.switchConfigForm=this.fb.group({jsScript:[e?e.jsScript:null,[k.required]]})}testScript(){const e=this.switchConfigForm.get("jsScript").value;this.nodeScriptTestService.testNodeScript(e,"switch",this.translate.instant("tb.rulenode.switch"),"Switch",["msg","metadata","msgType"],this.ruleNodeId,"rulenode/switch_node_script_fn").subscribe((e=>{e&&this.switchConfigForm.get("jsScript").setValue(e)}))}onValidate(){this.jsFuncComponent.validateOnSubmit()}}e("SwitchConfigComponent",$t),$t.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:$t,deps:[{token:T.Store},{token:q.FormBuilder},{token:Q.NodeScriptTestService},{token:P.TranslateService}],target:t.ɵɵFactoryTarget.Component}),$t.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"12.0.0",version:"12.2.15",type:$t,selector:"tb-filter-node-switch-config",viewQueries:[{propertyName:"jsFuncComponent",first:!0,predicate:["jsFuncComponent"],descendants:!0,static:!0}],usesInheritance:!0,ngImport:t,template:'
\n \n \n \n
\n \n
\n
\n',components:[{type:Y.JsFuncComponent,selector:"tb-js-func",inputs:["functionName","functionArgs","validationArgs","resultType","disabled","fillHeight","editorCompleter","globalVariables","disableUndefinedCheck","helpId","noValidate","required"]},{type:J.MatButton,selector:"button[mat-button], button[mat-raised-button], button[mat-icon-button], button[mat-fab], button[mat-mini-fab], button[mat-stroked-button], button[mat-flat-button]",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]}],directives:[{type:E.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{type:q.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{type:q.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{type:P.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{type:q.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{type:q.FormControlName,selector:"[formControlName]",inputs:["disabled","formControlName","ngModel"],outputs:["ngModelChange"]}],pipes:{translate:P.TranslatePipe}}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:$t,decorators:[{type:o,args:[{selector:"tb-filter-node-switch-config",templateUrl:"./switch-config.component.html",styleUrls:[]}]}],ctorParameters:function(){return[{type:T.Store},{type:q.FormBuilder},{type:Q.NodeScriptTestService},{type:P.TranslateService}]},propDecorators:{jsFuncComponent:[{type:a,args:["jsFuncComponent",{static:!0}]}]}});class Wt{}e("RuleNodeCoreConfigFilterModule",Wt),Wt.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Wt,deps:[],target:t.ɵɵFactoryTarget.NgModule}),Wt.ɵmod=t.ɵɵngDeclareNgModule({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Wt,declarations:[Kt,Bt,jt,zt,_t,Qt,$t,Ut],imports:[O,F,Mt],exports:[Kt,Bt,jt,zt,_t,Qt,$t,Ut]}),Wt.ɵinj=t.ɵɵngDeclareInjector({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Wt,imports:[[O,F,Mt]]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Wt,decorators:[{type:i,args:[{declarations:[Kt,Bt,jt,zt,_t,Qt,$t,Ut],imports:[O,F,Mt],exports:[Kt,Bt,jt,zt,_t,Qt,$t,Ut]}]}]});class Yt extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.originatorSource=Me,this.originatorSources=Object.keys(Me),this.originatorSourceTranslationMap=Se}configForm(){return this.changeOriginatorConfigForm}onConfigurationSet(e){this.changeOriginatorConfigForm=this.fb.group({originatorSource:[e?e.originatorSource:null,[k.required]],relationsQuery:[e?e.relationsQuery:null,[]]})}validatorTriggers(){return["originatorSource"]}updateValidators(e){const t=this.changeOriginatorConfigForm.get("originatorSource").value;t&&t===Me.RELATED?this.changeOriginatorConfigForm.get("relationsQuery").setValidators([k.required]):this.changeOriginatorConfigForm.get("relationsQuery").setValidators([]),this.changeOriginatorConfigForm.get("relationsQuery").updateValueAndValidity({emitEvent:e})}}e("ChangeOriginatorConfigComponent",Yt),Yt.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Yt,deps:[{token:T.Store},{token:q.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),Yt.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"12.0.0",version:"12.2.15",type:Yt,selector:"tb-transformation-node-change-originator-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.originator-source\n \n \n {{ originatorSourceTranslationMap.get(source) | translate }}\n \n \n \n
\n \n \n \n
\n
\n',components:[{type:D.MatFormField,selector:"mat-form-field",inputs:["color","floatLabel","appearance","hideRequiredMarker","hintLabel"],exportAs:["matFormField"]},{type:U.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex"],exportAs:["matSelect"]},{type:K.MatOption,selector:"mat-option",exportAs:["matOption"]},{type:qt,selector:"tb-relations-query-config",inputs:["disabled","required"]}],directives:[{type:E.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{type:q.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{type:q.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{type:D.MatLabel,selector:"mat-label"},{type:P.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{type:q.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{type:q.FormControlName,selector:"[formControlName]",inputs:["disabled","formControlName","ngModel"],outputs:["ngModelChange"]},{type:q.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{type:w.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{type:w.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]}],pipes:{translate:P.TranslatePipe}}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Yt,decorators:[{type:o,args:[{selector:"tb-transformation-node-change-originator-config",templateUrl:"./change-originator-config.component.html",styleUrls:[]}]}],ctorParameters:function(){return[{type:T.Store},{type:q.FormBuilder}]}});class Jt extends s{constructor(e,t,o,r){super(e),this.store=e,this.fb=t,this.nodeScriptTestService=o,this.translate=r}configForm(){return this.scriptConfigForm}onConfigurationSet(e){this.scriptConfigForm=this.fb.group({jsScript:[e?e.jsScript:null,[k.required]]})}testScript(){const e=this.scriptConfigForm.get("jsScript").value;this.nodeScriptTestService.testNodeScript(e,"update",this.translate.instant("tb.rulenode.transformer"),"Transform",["msg","metadata","msgType"],this.ruleNodeId,"rulenode/transformation_node_script_fn").subscribe((e=>{e&&this.scriptConfigForm.get("jsScript").setValue(e)}))}onValidate(){this.jsFuncComponent.validateOnSubmit()}}e("TransformScriptConfigComponent",Jt),Jt.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Jt,deps:[{token:T.Store},{token:q.FormBuilder},{token:Q.NodeScriptTestService},{token:P.TranslateService}],target:t.ɵɵFactoryTarget.Component}),Jt.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"12.0.0",version:"12.2.15",type:Jt,selector:"tb-transformation-node-script-config",viewQueries:[{propertyName:"jsFuncComponent",first:!0,predicate:["jsFuncComponent"],descendants:!0,static:!0}],usesInheritance:!0,ngImport:t,template:'
\n \n \n \n
\n \n
\n
\n',components:[{type:Y.JsFuncComponent,selector:"tb-js-func",inputs:["functionName","functionArgs","validationArgs","resultType","disabled","fillHeight","editorCompleter","globalVariables","disableUndefinedCheck","helpId","noValidate","required"]},{type:J.MatButton,selector:"button[mat-button], button[mat-raised-button], button[mat-icon-button], button[mat-fab], button[mat-mini-fab], button[mat-stroked-button], button[mat-flat-button]",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]}],directives:[{type:E.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{type:q.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{type:q.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{type:P.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{type:q.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{type:q.FormControlName,selector:"[formControlName]",inputs:["disabled","formControlName","ngModel"],outputs:["ngModelChange"]}],pipes:{translate:P.TranslatePipe}}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Jt,decorators:[{type:o,args:[{selector:"tb-transformation-node-script-config",templateUrl:"./script-config.component.html",styleUrls:[]}]}],ctorParameters:function(){return[{type:T.Store},{type:q.FormBuilder},{type:Q.NodeScriptTestService},{type:P.TranslateService}]},propDecorators:{jsFuncComponent:[{type:a,args:["jsFuncComponent",{static:!0}]}]}});class Zt extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.mailBodyTypes=[{name:"tb.mail-body-type.plain-text",value:"false"},{name:"tb.mail-body-type.html",value:"true"},{name:"tb.mail-body-type.dynamic",value:"dynamic"}]}configForm(){return this.toEmailConfigForm}onConfigurationSet(e){this.toEmailConfigForm=this.fb.group({fromTemplate:[e?e.fromTemplate:null,[k.required]],toTemplate:[e?e.toTemplate:null,[k.required]],ccTemplate:[e?e.ccTemplate:null,[]],bccTemplate:[e?e.bccTemplate:null,[]],subjectTemplate:[e?e.subjectTemplate:null,[k.required]],mailBodyType:[e?e.mailBodyType:null],isHtmlTemplate:[e?e.isHtmlTemplate:null],bodyTemplate:[e?e.bodyTemplate:null,[k.required]]}),this.toEmailConfigForm.get("mailBodyType").valueChanges.pipe(ue([null==e?void 0:e.subjectTemplate])).subscribe((e=>{"dynamic"===e?(this.toEmailConfigForm.get("isHtmlTemplate").patchValue("",{emitEvent:!1}),this.toEmailConfigForm.get("isHtmlTemplate").setValidators(k.required)):this.toEmailConfigForm.get("isHtmlTemplate").clearValidators(),this.toEmailConfigForm.get("isHtmlTemplate").updateValueAndValidity()}))}}e("ToEmailConfigComponent",Zt),Zt.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Zt,deps:[{token:T.Store},{token:q.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),Zt.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"12.0.0",version:"12.2.15",type:Zt,selector:"tb-transformation-node-to-email-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.from-template\n \n \n {{ \'tb.rulenode.from-template-required\' | translate }}\n \n \n \n \n tb.rulenode.to-template\n \n \n {{ \'tb.rulenode.to-template-required\' | translate }}\n \n \n \n \n tb.rulenode.cc-template\n \n \n \n \n tb.rulenode.bcc-template\n \n \n \n \n tb.rulenode.subject-template\n \n \n {{ \'tb.rulenode.subject-template-required\' | translate }}\n \n \n \n \n tb.rulenode.mail-body-type\n \n \n {{ type.name | translate }}\n \n \n \n \n tb.rulenode.dynamic-mail-body-type\n \n \n \n \n tb.rulenode.body-template\n \n \n {{ \'tb.rulenode.body-template-required\' | translate }}\n \n \n \n
\n',components:[{type:D.MatFormField,selector:"mat-form-field",inputs:["color","floatLabel","appearance","hideRequiredMarker","hintLabel"],exportAs:["matFormField"]},{type:U.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex"],exportAs:["matSelect"]},{type:K.MatOption,selector:"mat-option",exportAs:["matOption"]}],directives:[{type:E.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{type:q.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{type:q.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{type:D.MatLabel,selector:"mat-label"},{type:P.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{type:R.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["id","disabled","required","type","value","readonly","placeholder","errorStateMatcher","aria-describedby"],exportAs:["matInput"]},{type:q.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{type:q.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{type:q.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{type:q.FormControlName,selector:"[formControlName]",inputs:["disabled","formControlName","ngModel"],outputs:["ngModelChange"]},{type:w.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{type:D.MatError,selector:"mat-error",inputs:["id"]},{type:D.MatHint,selector:"mat-hint",inputs:["align","id"]},{type:w.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]}],pipes:{translate:P.TranslatePipe,safeHtml:Te}}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Zt,decorators:[{type:o,args:[{selector:"tb-transformation-node-to-email-config",templateUrl:"./to-email-config.component.html",styleUrls:[]}]}],ctorParameters:function(){return[{type:T.Store},{type:q.FormBuilder}]}});class Xt{}e("RulenodeCoreConfigTransformModule",Xt),Xt.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Xt,deps:[],target:t.ɵɵFactoryTarget.NgModule}),Xt.ɵmod=t.ɵɵngDeclareNgModule({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Xt,declarations:[Yt,Jt,Zt],imports:[O,F,Mt],exports:[Yt,Jt,Zt]}),Xt.ɵinj=t.ɵɵngDeclareInjector({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Xt,imports:[[O,F,Mt]]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Xt,decorators:[{type:i,args:[{declarations:[Yt,Jt,Zt],imports:[O,F,Mt],exports:[Yt,Jt,Zt]}]}]});class eo extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.entityType=x}configForm(){return this.ruleChainInputConfigForm}onConfigurationSet(e){this.ruleChainInputConfigForm=this.fb.group({ruleChainId:[e?e.ruleChainId:null,[k.required]]})}}e("RuleChainInputComponent",eo),eo.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:eo,deps:[{token:T.Store},{token:q.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),eo.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"12.0.0",version:"12.2.15",type:eo,selector:"tb-flow-node-rule-chain-input-config",usesInheritance:!0,ngImport:t,template:'
\n \n \n
\n',components:[{type:ve.EntityAutocompleteComponent,selector:"tb-entity-autocomplete",inputs:["entityType","entitySubtype","excludeEntityIds","labelText","requiredText","required","disabled"],outputs:["entityChanged"]}],directives:[{type:E.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{type:q.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{type:q.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{type:q.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{type:q.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{type:q.FormControlName,selector:"[formControlName]",inputs:["disabled","formControlName","ngModel"],outputs:["ngModelChange"]}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:eo,decorators:[{type:o,args:[{selector:"tb-flow-node-rule-chain-input-config",templateUrl:"./rule-chain-input.component.html",styleUrls:[]}]}],ctorParameters:function(){return[{type:T.Store},{type:q.FormBuilder}]}});class to extends s{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.ruleChainOutputConfigForm}onConfigurationSet(e){this.ruleChainOutputConfigForm=this.fb.group({})}}e("RuleChainOutputComponent",to),to.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:to,deps:[{token:T.Store},{token:q.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),to.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"12.0.0",version:"12.2.15",type:to,selector:"tb-flow-node-rule-chain-output-config",usesInheritance:!0,ngImport:t,template:'
\n
\n
\n',directives:[{type:E.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{type:q.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{type:q.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]}],pipes:{translate:P.TranslatePipe}}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:to,decorators:[{type:o,args:[{selector:"tb-flow-node-rule-chain-output-config",templateUrl:"./rule-chain-output.component.html",styleUrls:[]}]}],ctorParameters:function(){return[{type:T.Store},{type:q.FormBuilder}]}});class oo{}e("RuleNodeCoreConfigFlowModule",oo),oo.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:oo,deps:[],target:t.ɵɵFactoryTarget.NgModule}),oo.ɵmod=t.ɵɵngDeclareNgModule({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:oo,declarations:[eo,to],imports:[O,F,Mt],exports:[eo,to]}),oo.ɵinj=t.ɵɵngDeclareInjector({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:oo,imports:[[O,F,Mt]]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:oo,decorators:[{type:i,args:[{declarations:[eo,to],imports:[O,F,Mt],exports:[eo,to]}]}]});class ro{constructor(e){!function(e){e.setTranslation("en_US",{tb:{rulenode:{"create-entity-if-not-exists":"Create new entity if not exists","create-entity-if-not-exists-hint":"Create a new entity set above if it does not exist.","entity-name-pattern":"Name pattern","entity-name-pattern-required":"Name pattern is required","entity-type-pattern":"Type pattern","entity-type-pattern-required":"Type pattern is required","entity-cache-expiration":"Entities cache expiration time (sec)","entity-cache-expiration-hint":"Specifies maximum time interval allowed to store found entity records. 0 value means that records will never expire.","entity-cache-expiration-required":"Entities cache expiration time is required.","entity-cache-expiration-range":"Entities cache expiration time should be greater than or equal to 0.","customer-name-pattern":"Customer name pattern","customer-name-pattern-required":"Customer name pattern is required","create-customer-if-not-exists":"Create new customer if not exists","customer-cache-expiration":"Customers cache expiration time (sec)","customer-cache-expiration-hint":"Specifies maximum time interval allowed to store found customer records. 0 value means that records will never expire.","customer-cache-expiration-required":"Customers cache expiration time is required.","customer-cache-expiration-range":"Customers cache expiration time should be greater than or equal to 0.","start-interval":"Start Interval","end-interval":"End Interval","start-interval-time-unit":"Start Interval Time Unit","end-interval-time-unit":"End Interval Time Unit","fetch-mode":"Fetch mode","fetch-mode-hint":"If selected fetch mode 'ALL' you able to choose telemetry sampling order.","order-by":"Order by","order-by-hint":"Select to choose telemetry sampling order.",limit:"Limit","limit-hint":"Min limit value is 2, max - 1000. In case you want to fetch a single entry, select fetch mode 'FIRST' or 'LAST'.","time-unit-milliseconds":"Milliseconds","time-unit-seconds":"Seconds","time-unit-minutes":"Minutes","time-unit-hours":"Hours","time-unit-days":"Days","time-value-range":"Time value should be in a range from 1 to 2147483647.","start-interval-value-required":"Start interval value is required.","end-interval-value-required":"End interval value is required.",filter:"Filter",switch:"Switch","message-type":"Message type","message-type-required":"Message type is required.","message-types-filter":"Message types filter","no-message-types-found":"No message types found","no-message-type-matching":"'{{messageType}}' not found.","create-new-message-type":"Create a new one!","message-types-required":"Message types are required.","client-attributes":"Client attributes","shared-attributes":"Shared attributes","server-attributes":"Server attributes","notify-device":"Notify Device","notify-device-hint":"If the message arrives from the device, we will push it back to the device by default.","latest-timeseries":"Latest timeseries","timeseries-key":"Timeseries key","data-keys":"Message data","metadata-keys":"Message metadata","relations-query":"Relations query","device-relations-query":"Device relations query","max-relation-level":"Max relation level","relation-type-pattern":"Relation type pattern","relation-type-pattern-required":"Relation type pattern is required","relation-types-list":"Relation types to propagate","relation-types-list-hint":"If Propagate relation types are not selected, alarms will be propagated without filtering by relation type.","unlimited-level":"Unlimited level","latest-telemetry":"Latest telemetry","attr-mapping":"Attributes mapping","source-attribute":"Source attribute","source-attribute-required":"Source attribute is required.","source-telemetry":"Source telemetry","source-telemetry-required":"Source telemetry is required.","target-attribute":"Target attribute","target-attribute-required":"Target attribute is required.","attr-mapping-required":"At least one attribute mapping should be specified.","fields-mapping":"Fields mapping","fields-mapping-required":"At least one field mapping should be specified.","source-field":"Source field","source-field-required":"Source field is required.","originator-source":"Originator source","originator-customer":"Customer","originator-tenant":"Tenant","originator-related":"Related","originator-alarm-originator":"Alarm Originator","clone-message":"Clone message",transform:"Transform","default-ttl":"Default TTL in seconds","default-ttl-required":"Default TTL is required.","min-default-ttl-message":"Only 0 minimum TTL is allowed.","message-count":"Message count (0 - unlimited)","message-count-required":"Message count is required.","min-message-count-message":"Only 0 minimum message count is allowed.","period-seconds":"Period in seconds","period-seconds-required":"Period is required.","use-metadata-period-in-seconds-patterns":"Use period in seconds pattern","use-metadata-period-in-seconds-patterns-hint":"If selected, rule node use period in seconds interval pattern from message metadata or data assuming that intervals are in the seconds.","period-in-seconds-pattern":"Period in seconds pattern","period-in-seconds-pattern-required":"Period in seconds pattern is required","min-period-seconds-message":"Only 1 second minimum period is allowed.",originator:"Originator","message-body":"Message body","message-metadata":"Message metadata",generate:"Generate","test-generator-function":"Test generator function",generator:"Generator","test-filter-function":"Test filter function","test-switch-function":"Test switch function","test-transformer-function":"Test transformer function",transformer:"Transformer","alarm-create-condition":"Alarm create condition","test-condition-function":"Test condition function","alarm-clear-condition":"Alarm clear condition","alarm-details-builder":"Alarm details builder","test-details-function":"Test details function","alarm-type":"Alarm type","alarm-type-required":"Alarm type is required.","alarm-severity":"Alarm severity","alarm-severity-required":"Alarm severity is required","alarm-severity-pattern":"Alarm severity pattern","alarm-status-filter":"Alarm status filter","alarm-status-list-empty":"Alarm status list is empty","no-alarm-status-matching":"No alarm status matching were found.",propagate:"Propagate alarm to related entities","propagate-to-owner":"Propagate alarm to entity owner (Customer or Tenant)","propagate-to-tenant":"Propagate alarm to Tenant",condition:"Condition",details:"Details","to-string":"To string","test-to-string-function":"Test to string function","from-template":"From Template","from-template-required":"From Template is required","to-template":"To Template","to-template-required":"To Template is required","mail-address-list-template-hint":'Comma separated address list, use ${metadataKey} for value from metadata, $[messageKey] for value from message body',"cc-template":"Cc Template","bcc-template":"Bcc Template","subject-template":"Subject Template","subject-template-required":"Subject Template is required","body-template":"Body Template","body-template-required":"Body Template is required","dynamic-mail-body-type":"Dynamic mail body type","mail-body-type":"Mail body type","request-id-metadata-attribute":"Request Id Metadata attribute name","timeout-sec":"Timeout in seconds","timeout-required":"Timeout is required","min-timeout-message":"Only 0 minimum timeout value is allowed.","endpoint-url-pattern":"Endpoint URL pattern","endpoint-url-pattern-required":"Endpoint URL pattern is required","request-method":"Request method","use-simple-client-http-factory":"Use simple client HTTP factory","ignore-request-body":"Without request body","read-timeout":"Read timeout in millis","read-timeout-hint":"The value of 0 means an infinite timeout","max-parallel-requests-count":"Max number of parallel requests","max-parallel-requests-count-hint":"The value of 0 specifies no limit in parallel processing",headers:"Headers","headers-hint":'Use ${metadataKey} for value from metadata, $[messageKey] for value from message body in header/value fields',header:"Header","header-required":"Header is required",value:"Value","value-required":"Value is required","topic-pattern":"Topic pattern","topic-pattern-required":"Topic pattern is required",topic:"Topic","topic-required":"Topic is required","bootstrap-servers":"Bootstrap servers","bootstrap-servers-required":"Bootstrap servers value is required","other-properties":"Other properties",key:"Key","key-required":"Key is required",retries:"Automatically retry times if fails","min-retries-message":"Only 0 minimum retries is allowed.","batch-size-bytes":"Produces batch size in bytes","min-batch-size-bytes-message":"Only 0 minimum batch size is allowed.","linger-ms":"Time to buffer locally (ms)","min-linger-ms-message":"Only 0 ms minimum value is allowed.","buffer-memory-bytes":"Client buffer max size in bytes","min-buffer-memory-message":"Only 0 minimum buffer size is allowed.",acks:"Number of acknowledgments","key-serializer":"Key serializer","key-serializer-required":"Key serializer is required","value-serializer":"Value serializer","value-serializer-required":"Value serializer is required","topic-arn-pattern":"Topic ARN pattern","topic-arn-pattern-required":"Topic ARN pattern is required","aws-access-key-id":"AWS Access Key ID","aws-access-key-id-required":"AWS Access Key ID is required","aws-secret-access-key":"AWS Secret Access Key","aws-secret-access-key-required":"AWS Secret Access Key is required","aws-region":"AWS Region","aws-region-required":"AWS Region is required","exchange-name-pattern":"Exchange name pattern","routing-key-pattern":"Routing key pattern","message-properties":"Message properties",host:"Host","host-required":"Host is required",port:"Port","port-required":"Port is required","port-range":"Port should be in a range from 1 to 65535.","virtual-host":"Virtual host",username:"Username",password:"Password","automatic-recovery":"Automatic recovery","connection-timeout-ms":"Connection timeout (ms)","min-connection-timeout-ms-message":"Only 0 ms minimum value is allowed.","handshake-timeout-ms":"Handshake timeout (ms)","min-handshake-timeout-ms-message":"Only 0 ms minimum value is allowed.","client-properties":"Client properties","queue-url-pattern":"Queue URL pattern","queue-url-pattern-required":"Queue URL pattern is required","delay-seconds":"Delay (seconds)","min-delay-seconds-message":"Only 0 seconds minimum value is allowed.","max-delay-seconds-message":"Only 900 seconds maximum value is allowed.",name:"Name","name-required":"Name is required","queue-type":"Queue type","sqs-queue-standard":"Standard","sqs-queue-fifo":"FIFO","gcp-project-id":"GCP project ID","gcp-project-id-required":"GCP project ID is required","gcp-service-account-key":"GCP service account key file","gcp-service-account-key-required":"GCP service account key file is required","pubsub-topic-name":"Topic name","pubsub-topic-name-required":"Topic name is required","message-attributes":"Message attributes","message-attributes-hint":'Use ${metadataKey} for value from metadata, $[messageKey] for value from message body in name/value fields',"connect-timeout":"Connection timeout (sec)","connect-timeout-required":"Connection timeout is required.","connect-timeout-range":"Connection timeout should be in a range from 1 to 200.","client-id":"Client ID","client-id-hint":'Hint: Optional. Leave empty for auto-generated Client ID. Be careful when specifying the Client ID. Majority of the MQTT brokers will not allow multiple connections with the same Client ID. To connect to such brokers, your mqtt Client ID must be unique. When platform is running in a micro-services mode, the copy of rule node is launched in each micro-service. This will automatically lead to multiple mqtt clients with the same ID and may cause failures of the rule node. To avoid such failures enable "Add Service ID as suffix to Client ID" option below.',"append-client-id-suffix":"Add Service ID as suffix to Client ID","client-id-suffix-hint":'Hint: Optional. Applied when "Client ID" specified explicitly. If selected then Service ID will be added to Client ID as a suffix. Helps to avoid failures when platform is running in a micro-services mode.',"device-id":"Device ID","device-id-required":"Device ID is required.","clean-session":"Clean session","enable-ssl":"Enable SSL",credentials:"Credentials","credentials-type":"Credentials type","credentials-type-required":"Credentials type is required.","credentials-anonymous":"Anonymous","credentials-basic":"Basic","credentials-pem":"PEM","credentials-pem-hint":"At least Server CA certificate file or a pair of Client certificate and Client private key files are required","credentials-sas":"Shared Access Signature","sas-key":"SAS Key","sas-key-required":"SAS Key is required.",hostname:"Hostname","hostname-required":"Hostname is required.","azure-ca-cert":"CA certificate file","username-required":"Username is required.","password-required":"Password is required.","ca-cert":"Server CA certificate file *","private-key":"Client private key file *",cert:"Client certificate file *","no-file":"No file selected.","drop-file":"Drop a file or click to select a file to upload.","private-key-password":"Private key password","use-system-smtp-settings":"Use system SMTP settings","use-metadata-interval-patterns":"Use interval patterns","use-metadata-interval-patterns-hint":"If selected, rule node use start and end interval patterns from message metadata or data assuming that intervals are in the milliseconds.","use-message-alarm-data":"Use message alarm data","overwrite-alarm-details":"Overwrite alarm details","use-alarm-severity-pattern":"Use alarm severity pattern","check-all-keys":"Check that all selected keys are present","check-all-keys-hint":"If selected, checks that all specified keys are present in the message data and metadata.","check-relation-to-specific-entity":"Check relation to specific entity","check-relation-hint":"Checks existence of relation to specific entity or to any entity based on direction and relation type.","delete-relation-to-specific-entity":"Delete relation to specific entity","delete-relation-hint":"Deletes relation from the originator of the incoming message to the specified entity or list of entities based on direction and type.","remove-current-relations":"Remove current relations","remove-current-relations-hint":"Removes current relations from the originator of the incoming message based on direction and type.","change-originator-to-related-entity":"Change originator to related entity","change-originator-to-related-entity-hint":"Used to process submitted message as a message from another entity.","start-interval-pattern":"Start interval pattern","end-interval-pattern":"End interval pattern","start-interval-pattern-required":"Start interval pattern is required","end-interval-pattern-required":"End interval pattern is required","smtp-protocol":"Protocol","smtp-host":"SMTP host","smtp-host-required":"SMTP host is required.","smtp-port":"SMTP port","smtp-port-required":"You must supply a smtp port.","smtp-port-range":"SMTP port should be in a range from 1 to 65535.","timeout-msec":"Timeout ms","min-timeout-msec-message":"Only 0 ms minimum value is allowed.","enter-username":"Enter username","enter-password":"Enter password","enable-tls":"Enable TLS","tls-version":"TLS version","enable-proxy":"Enable proxy","use-system-proxy-properties":"Use system proxy properties","proxy-host":"Proxy host","proxy-host-required":"Proxy host is required.","proxy-port":"Proxy port","proxy-port-required":"Proxy port is required.","proxy-port-range":"Proxy port should be in a range from 1 to 65535.","proxy-user":"Proxy user","proxy-password":"Proxy password","proxy-scheme":"Proxy scheme","numbers-to-template":"Phone Numbers To Template","numbers-to-template-required":"Phone Numbers To Template is required","numbers-to-template-hint":'Comma separated Phone Numbers, use ${metadataKey} for value from metadata, $[messageKey] for value from message body',"sms-message-template":"SMS message Template","sms-message-template-required":"SMS message Template is required","use-system-sms-settings":"Use system SMS provider settings","min-period-0-seconds-message":"Only 0 second minimum period is allowed.","max-pending-messages":"Maximum pending messages","max-pending-messages-required":"Maximum pending messages is required.","max-pending-messages-range":"Maximum pending messages should be in a range from 1 to 100000.","originator-types-filter":"Originator types filter","interval-seconds":"Interval in seconds","interval-seconds-required":"Interval is required.","min-interval-seconds-message":"Only 1 second minimum interval is allowed.","output-timeseries-key-prefix":"Output timeseries key prefix","output-timeseries-key-prefix-required":"Output timeseries key prefix required.","separator-hint":'You should press "enter" to complete field input.',"entity-details":"Select entity details:","entity-details-title":"Title","entity-details-country":"Country","entity-details-state":"State","entity-details-city":"City","entity-details-zip":"Zip","entity-details-address":"Address","entity-details-address2":"Address2","entity-details-additional_info":"Additional Info","entity-details-phone":"Phone","entity-details-email":"Email","add-to-metadata":"Add selected details to message metadata","add-to-metadata-hint":"If selected, adds the selected details keys to the message metadata instead of message data.","entity-details-list-empty":"No entity details selected.","no-entity-details-matching":"No entity details matching were found.","custom-table-name":"Custom table name","custom-table-name-required":"Table Name is required","custom-table-hint":"You should enter the table name without prefix 'cs_tb_'.","message-field":"Message field","message-field-required":"Message field is required.","table-col":"Table column","table-col-required":"Table column is required.","latitude-key-name":"Latitude key name","longitude-key-name":"Longitude key name","latitude-key-name-required":"Latitude key name is required.","longitude-key-name-required":"Longitude key name is required.","fetch-perimeter-info-from-message-metadata":"Fetch perimeter information from message metadata","perimeter-key-name":"Perimeter key name","perimeter-key-name-required":"Perimeter key name is required.","perimeter-circle":"Circle","perimeter-polygon":"Polygon","perimeter-type":"Perimeter type","circle-center-latitude":"Center latitude","circle-center-latitude-required":"Center latitude is required.","circle-center-longitude":"Center longitude","circle-center-longitude-required":"Center longitude is required.","range-unit-meter":"Meter","range-unit-kilometer":"Kilometer","range-unit-foot":"Foot","range-unit-mile":"Mile","range-unit-nautical-mile":"Nautical mile","range-units":"Range units",range:"Range","range-required":"Range is required.","polygon-definition":"Polygon definition","polygon-definition-required":"Polygon definition is required.","polygon-definition-hint":"Please, use the following format for manual definition of polygon: [[lat1,lon1],[lat2,lon2], ... ,[latN,lonN]].","min-inside-duration":"Minimal inside duration","min-inside-duration-value-required":"Minimal inside duration is required","min-inside-duration-time-unit":"Minimal inside duration time unit","min-outside-duration":"Minimal outside duration","min-outside-duration-value-required":"Minimal outside duration is required","min-outside-duration-time-unit":"Minimal outside duration time unit","tell-failure-if-absent":"Tell Failure","tell-failure-if-absent-hint":'If at least one selected key doesn\'t exist the outbound message will report "Failure".',"get-latest-value-with-ts":"Fetch Latest telemetry with Timestamp","get-latest-value-with-ts-hint":'If selected, latest telemetry values will be added to the outbound message metadata with timestamp, e.g: "temp": "{"ts":1574329385897, "value":42}"',"use-redis-queue":"Use redis queue for message persistence","trim-redis-queue":"Trim redis queue","redis-queue-max-size":"Redis queue max size","add-metadata-key-values-as-kafka-headers":"Add Message metadata key-value pairs to Kafka record headers","add-metadata-key-values-as-kafka-headers-hint":"If selected, key-value pairs from message metadata will be added to the outgoing records headers as byte arrays with predefined charset encoding.","charset-encoding":"Charset encoding","charset-encoding-required":"Charset encoding is required.","charset-us-ascii":"US-ASCII","charset-iso-8859-1":"ISO-8859-1","charset-utf-8":"UTF-8","charset-utf-16be":"UTF-16BE","charset-utf-16le":"UTF-16LE","charset-utf-16":"UTF-16","select-queue-hint":"The queue name can be selected from a drop-down list or add a custom name.","persist-alarm-rules":"Persist state of alarm rules","fetch-alarm-rules":"Fetch state of alarm rules","input-value-key":"Input value key","input-value-key-required":"Input value key is required.","output-value-key":"Output value key","output-value-key-required":"Output value key is required.",round:"Decimals","round-range":"Decimals should be in a range from 0 to 15.","use-cache":"Use cache for latest value","tell-failure-if-delta-is-negative":"Tell Failure if delta is negative","add-period-between-msgs":"Add period between messages","period-value-key":"Period value key","period-value-key-required":"Period value key is required.","general-pattern-hint":'Hint: use ${metadataKey} for value from metadata, $[messageKey] for value from message body',"alarm-severity-pattern-hint":'Hint: use ${metadataKey} for value from metadata, $[messageKey] for value from message body. Alarm severity should be system (CRITICAL, MAJOR etc.)',"output-node-name-hint":"The rule node name corresponds to the relation type of the output message, and it is used to forward messages to other rule nodes in the caller rule chain.","skip-latest-persistence":"Skip latest persistence","use-server-ts":"Use server ts","use-server-ts-hint":"Enable this setting to use the timestamp of the message processing instead of the timestamp from the message. Useful for all sorts of sequential processing if you merge messages from multiple sources (devices, assets, etc).","kv-map-pattern-hint":'Hint: use ${metadataKey} for value from metadata, $[messageKey] for value from message body to substitute "Source" and "Target" key names'},"key-val":{key:"Key",value:"Value","remove-entry":"Remove entry","add-entry":"Add entry"},"mail-body-type":{"plain-text":"Plain Text",html:"HTML",dynamic:"Dynamic"}}},!0)}(e)}}e("RuleNodeCoreConfigModule",ro),ro.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:ro,deps:[{token:P.TranslateService}],target:t.ɵɵFactoryTarget.NgModule}),ro.ɵmod=t.ɵɵngDeclareNgModule({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:ro,declarations:[Ne],imports:[O,F],exports:[St,Wt,Ht,Xt,oo,Ne]}),ro.ɵinj=t.ɵɵngDeclareInjector({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:ro,imports:[[O,F],St,Wt,Ht,Xt,oo]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:ro,decorators:[{type:i,args:[{declarations:[Ne],imports:[O,F],exports:[St,Wt,Ht,Xt,oo,Ne]}]}],ctorParameters:function(){return[{type:P.TranslateService}]}})}}}));//# sourceMappingURL=rulenode-core-config.js.map +System.register(["@angular/core","@shared/public-api","@ngrx/store","@angular/forms","@angular/material/form-field","@angular/material/checkbox","@angular/flex-layout/flex","@ngx-translate/core","@angular/material/input","@angular/common","@angular/platform-browser","@angular/material/select","@angular/material/core","@angular/material/expansion","@shared/components/button/toggle-password.component","@shared/components/file-input.component","@shared/components/queue/queue-autocomplete.component","@core/public-api","@shared/components/js-func.component","@angular/material/button","@angular/cdk/keycodes","@angular/material/chips","@angular/material/icon","@angular/flex-layout/extended","@shared/components/entity/entity-type-select.component","@shared/components/entity/entity-select.component","@angular/cdk/coercion","@shared/components/tb-error.component","@angular/material/tooltip","rxjs/operators","@shared/components/tb-checkbox.component","@home/components/sms/sms-provider-configuration.component","@home/components/public-api","@shared/components/relation/relation-type-autocomplete.component","@shared/components/entity/entity-subtype-list.component","@home/components/relation/relation-filters.component","rxjs","@angular/material/autocomplete","@shared/pipe/highlight.pipe","@shared/components/entity/entity-autocomplete.component","@shared/components/entity/entity-type-list.component"],(function(e){"use strict";var t,o,r,a,n,l,i,s,m,u,p,d,f,c,g,x,y,b,h,C,F,L,v,I,N,T,q,k,M,S,A,G,D,V,E,P,R,w,O,H,U,K,B,j,z,_,Q,$,W,Y,J,Z,X,ee,te,oe,re,ae,ne,le,ie,se,me,ue,pe,de,fe,ce,ge,xe,ye,be,he,Ce,Fe,Le,ve,Ie;return{setters:[function(e){t=e,o=e.Component,r=e.Pipe,a=e.ViewChild,n=e.forwardRef,l=e.Input,i=e.NgModule},function(e){s=e.RuleNodeConfigurationComponent,m=e.AttributeScope,u=e.telemetryTypeTranslations,p=e.ServiceType,d=e.AlarmSeverity,f=e.alarmSeverityTranslations,c=e.EntitySearchDirection,g=e.entitySearchDirectionTranslations,x=e.EntityType,y=e.PageComponent,b=e.MessageType,h=e.messageTypeNames,C=e,F=e.SharedModule,L=e.AggregationType,v=e.aggregationTranslations,I=e.alarmStatusTranslations,N=e.AlarmStatus},function(e){T=e},function(e){q=e,k=e.Validators,M=e.NgControl,S=e.NG_VALUE_ACCESSOR,A=e.NG_VALIDATORS,G=e.FormControl},function(e){D=e},function(e){V=e},function(e){E=e},function(e){P=e},function(e){R=e},function(e){w=e,O=e.CommonModule},function(e){H=e},function(e){U=e},function(e){K=e},function(e){B=e},function(e){j=e},function(e){z=e},function(e){_=e},function(e){Q=e,$=e.isDefinedAndNotNull,W=e.isNotEmptyStr},function(e){Y=e},function(e){J=e},function(e){Z=e.ENTER,X=e.COMMA,ee=e.SEMICOLON},function(e){te=e},function(e){oe=e},function(e){re=e},function(e){ae=e},function(e){ne=e},function(e){le=e.coerceBooleanProperty},function(e){ie=e},function(e){se=e},function(e){me=e.distinctUntilChanged,ue=e.startWith,pe=e.map,de=e.mergeMap,fe=e.share},function(e){ce=e},function(e){ge=e},function(e){xe=e.HomeComponentsModule},function(e){ye=e},function(e){be=e},function(e){he=e},function(e){Ce=e.of},function(e){Fe=e},function(e){Le=e},function(e){ve=e},function(e){Ie=e}],execute:function(){class Ne extends s{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.emptyConfigForm}onConfigurationSet(e){this.emptyConfigForm=this.fb.group({})}}e("EmptyConfigComponent",Ne),Ne.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Ne,deps:[{token:T.Store},{token:q.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),Ne.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"12.0.0",version:"12.2.15",type:Ne,selector:"tb-node-empty-config",usesInheritance:!0,ngImport:t,template:"
",isInline:!0}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Ne,decorators:[{type:o,args:[{selector:"tb-node-empty-config",template:"
",styleUrls:[]}]}],ctorParameters:function(){return[{type:T.Store},{type:q.FormBuilder}]}});class Te{constructor(e){this.sanitizer=e}transform(e){return this.sanitizer.bypassSecurityTrustHtml(e)}}e("SafeHtmlPipe",Te),Te.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Te,deps:[{token:H.DomSanitizer}],target:t.ɵɵFactoryTarget.Pipe}),Te.ɵpipe=t.ɵɵngDeclarePipe({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Te,name:"safeHtml"}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Te,decorators:[{type:r,args:[{name:"safeHtml"}]}],ctorParameters:function(){return[{type:H.DomSanitizer}]}});class qe extends s{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.assignCustomerConfigForm}onConfigurationSet(e){this.assignCustomerConfigForm=this.fb.group({customerNamePattern:[e?e.customerNamePattern:null,[k.required,k.pattern(/.*\S.*/)]],createCustomerIfNotExists:[!!e&&e.createCustomerIfNotExists,[]],customerCacheExpiration:[e?e.customerCacheExpiration:null,[k.required,k.min(0)]]})}prepareOutputConfig(e){return e.customerNamePattern=e.customerNamePattern.trim(),e}}e("AssignCustomerConfigComponent",qe),qe.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:qe,deps:[{token:T.Store},{token:q.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),qe.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"12.0.0",version:"12.2.15",type:qe,selector:"tb-action-node-assign-to-customer-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.customer-name-pattern\n \n \n {{ \'tb.rulenode.customer-name-pattern-required\' | translate }}\n \n \n \n \n {{ \'tb.rulenode.create-customer-if-not-exists\' | translate }}\n \n \n tb.rulenode.customer-cache-expiration\n \n \n {{ \'tb.rulenode.customer-cache-expiration-required\' | translate }}\n \n \n {{ \'tb.rulenode.customer-cache-expiration-range\' | translate }}\n \n tb.rulenode.customer-cache-expiration-hint\n \n
\n',components:[{type:D.MatFormField,selector:"mat-form-field",inputs:["color","floatLabel","appearance","hideRequiredMarker","hintLabel"],exportAs:["matFormField"]},{type:V.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex","aria-label","aria-labelledby","id","labelPosition","name","required","checked","disabled","indeterminate","aria-describedby","value"],outputs:["change","indeterminateChange"],exportAs:["matCheckbox"]}],directives:[{type:E.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{type:q.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{type:q.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{type:D.MatLabel,selector:"mat-label"},{type:P.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{type:R.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["id","disabled","required","type","value","readonly","placeholder","errorStateMatcher","aria-describedby"],exportAs:["matInput"]},{type:q.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{type:q.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{type:q.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{type:q.FormControlName,selector:"[formControlName]",inputs:["disabled","formControlName","ngModel"],outputs:["ngModelChange"]},{type:w.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{type:D.MatError,selector:"mat-error",inputs:["id"]},{type:D.MatHint,selector:"mat-hint",inputs:["align","id"]},{type:E.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{type:q.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{type:q.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"}],pipes:{translate:P.TranslatePipe,safeHtml:Te}}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:qe,decorators:[{type:o,args:[{selector:"tb-action-node-assign-to-customer-config",templateUrl:"./assign-customer-config.component.html",styleUrls:[]}]}],ctorParameters:function(){return[{type:T.Store},{type:q.FormBuilder}]}});class ke extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.attributeScopes=Object.keys(m),this.telemetryTypeTranslationsMap=u}configForm(){return this.attributesConfigForm}onConfigurationSet(e){this.attributesConfigForm=this.fb.group({scope:[e?e.scope:null,[k.required]],notifyDevice:[!e||e.notifyDevice,[]]})}}var Me;e("AttributesConfigComponent",ke),ke.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:ke,deps:[{token:T.Store},{token:q.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),ke.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"12.0.0",version:"12.2.15",type:ke,selector:"tb-action-node-attributes-config",usesInheritance:!0,ngImport:t,template:'
\n \n attribute.attributes-scope\n \n \n {{ telemetryTypeTranslationsMap.get(scope) | translate }}\n \n \n \n
\n \n {{ \'tb.rulenode.notify-device\' | translate }}\n \n
tb.rulenode.notify-device-hint
\n
\n
\n',components:[{type:D.MatFormField,selector:"mat-form-field",inputs:["color","floatLabel","appearance","hideRequiredMarker","hintLabel"],exportAs:["matFormField"]},{type:U.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex"],exportAs:["matSelect"]},{type:K.MatOption,selector:"mat-option",exportAs:["matOption"]},{type:V.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex","aria-label","aria-labelledby","id","labelPosition","name","required","checked","disabled","indeterminate","aria-describedby","value"],outputs:["change","indeterminateChange"],exportAs:["matCheckbox"]}],directives:[{type:E.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{type:q.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{type:q.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{type:E.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{type:D.MatLabel,selector:"mat-label"},{type:P.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{type:q.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{type:q.FormControlName,selector:"[formControlName]",inputs:["disabled","formControlName","ngModel"],outputs:["ngModelChange"]},{type:q.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{type:w.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{type:w.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]}],pipes:{translate:P.TranslatePipe}}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:ke,decorators:[{type:o,args:[{selector:"tb-action-node-attributes-config",templateUrl:"./attributes-config.component.html",styleUrls:[]}]}],ctorParameters:function(){return[{type:T.Store},{type:q.FormBuilder}]}}),function(e){e.CUSTOMER="CUSTOMER",e.TENANT="TENANT",e.RELATED="RELATED",e.ALARM_ORIGINATOR="ALARM_ORIGINATOR"}(Me||(Me={}));const Se=new Map([[Me.CUSTOMER,"tb.rulenode.originator-customer"],[Me.TENANT,"tb.rulenode.originator-tenant"],[Me.RELATED,"tb.rulenode.originator-related"],[Me.ALARM_ORIGINATOR,"tb.rulenode.originator-alarm-originator"]]);var Ae;!function(e){e.CIRCLE="CIRCLE",e.POLYGON="POLYGON"}(Ae||(Ae={}));const Ge=new Map([[Ae.CIRCLE,"tb.rulenode.perimeter-circle"],[Ae.POLYGON,"tb.rulenode.perimeter-polygon"]]);var De;!function(e){e.MILLISECONDS="MILLISECONDS",e.SECONDS="SECONDS",e.MINUTES="MINUTES",e.HOURS="HOURS",e.DAYS="DAYS"}(De||(De={}));const Ve=new Map([[De.MILLISECONDS,"tb.rulenode.time-unit-milliseconds"],[De.SECONDS,"tb.rulenode.time-unit-seconds"],[De.MINUTES,"tb.rulenode.time-unit-minutes"],[De.HOURS,"tb.rulenode.time-unit-hours"],[De.DAYS,"tb.rulenode.time-unit-days"]]);var Ee;!function(e){e.METER="METER",e.KILOMETER="KILOMETER",e.FOOT="FOOT",e.MILE="MILE",e.NAUTICAL_MILE="NAUTICAL_MILE"}(Ee||(Ee={}));const Pe=new Map([[Ee.METER,"tb.rulenode.range-unit-meter"],[Ee.KILOMETER,"tb.rulenode.range-unit-kilometer"],[Ee.FOOT,"tb.rulenode.range-unit-foot"],[Ee.MILE,"tb.rulenode.range-unit-mile"],[Ee.NAUTICAL_MILE,"tb.rulenode.range-unit-nautical-mile"]]);var Re;!function(e){e.TITLE="TITLE",e.COUNTRY="COUNTRY",e.STATE="STATE",e.CITY="CITY",e.ZIP="ZIP",e.ADDRESS="ADDRESS",e.ADDRESS2="ADDRESS2",e.PHONE="PHONE",e.EMAIL="EMAIL",e.ADDITIONAL_INFO="ADDITIONAL_INFO"}(Re||(Re={}));const we=new Map([[Re.TITLE,"tb.rulenode.entity-details-title"],[Re.COUNTRY,"tb.rulenode.entity-details-country"],[Re.STATE,"tb.rulenode.entity-details-state"],[Re.CITY,"tb.rulenode.entity-details-city"],[Re.ZIP,"tb.rulenode.entity-details-zip"],[Re.ADDRESS,"tb.rulenode.entity-details-address"],[Re.ADDRESS2,"tb.rulenode.entity-details-address2"],[Re.PHONE,"tb.rulenode.entity-details-phone"],[Re.EMAIL,"tb.rulenode.entity-details-email"],[Re.ADDITIONAL_INFO,"tb.rulenode.entity-details-additional_info"]]);var Oe,He,Ue;!function(e){e.FIRST="FIRST",e.LAST="LAST",e.ALL="ALL"}(Oe||(Oe={})),function(e){e.ASC="ASC",e.DESC="DESC"}(He||(He={})),function(e){e.STANDARD="STANDARD",e.FIFO="FIFO"}(Ue||(Ue={}));const Ke=new Map([[Ue.STANDARD,"tb.rulenode.sqs-queue-standard"],[Ue.FIFO,"tb.rulenode.sqs-queue-fifo"]]),Be=["anonymous","basic","cert.PEM"],je=new Map([["anonymous","tb.rulenode.credentials-anonymous"],["basic","tb.rulenode.credentials-basic"],["cert.PEM","tb.rulenode.credentials-pem"]]),ze=["sas","cert.PEM"],_e=new Map([["sas","tb.rulenode.credentials-sas"],["cert.PEM","tb.rulenode.credentials-pem"]]);var Qe;!function(e){e.GET="GET",e.POST="POST",e.PUT="PUT",e.DELETE="DELETE"}(Qe||(Qe={}));const $e=["US-ASCII","ISO-8859-1","UTF-8","UTF-16BE","UTF-16LE","UTF-16"],We=new Map([["US-ASCII","tb.rulenode.charset-us-ascii"],["ISO-8859-1","tb.rulenode.charset-iso-8859-1"],["UTF-8","tb.rulenode.charset-utf-8"],["UTF-16BE","tb.rulenode.charset-utf-16be"],["UTF-16LE","tb.rulenode.charset-utf-16le"],["UTF-16","tb.rulenode.charset-utf-16"]]);class Ye extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.allAzureIotHubCredentialsTypes=ze,this.azureIotHubCredentialsTypeTranslationsMap=_e}configForm(){return this.azureIotHubConfigForm}onConfigurationSet(e){this.azureIotHubConfigForm=this.fb.group({topicPattern:[e?e.topicPattern:null,[k.required]],host:[e?e.host:null,[k.required]],port:[e?e.port:null,[k.required,k.min(1),k.max(65535)]],connectTimeoutSec:[e?e.connectTimeoutSec:null,[k.required,k.min(1),k.max(200)]],clientId:[e?e.clientId:null,[k.required]],cleanSession:[!!e&&e.cleanSession,[]],ssl:[!!e&&e.ssl,[]],credentials:this.fb.group({type:[e&&e.credentials?e.credentials.type:null,[k.required]],sasKey:[e&&e.credentials?e.credentials.sasKey:null,[]],caCert:[e&&e.credentials?e.credentials.caCert:null,[]],caCertFileName:[e&&e.credentials?e.credentials.caCertFileName:null,[]],privateKey:[e&&e.credentials?e.credentials.privateKey:null,[]],privateKeyFileName:[e&&e.credentials?e.credentials.privateKeyFileName:null,[]],cert:[e&&e.credentials?e.credentials.cert:null,[]],certFileName:[e&&e.credentials?e.credentials.certFileName:null,[]],password:[e&&e.credentials?e.credentials.password:null,[]]})})}prepareOutputConfig(e){const t=e.credentials.type;return"sas"===t&&(e.credentials={type:t,sasKey:e.credentials.sasKey,caCert:e.credentials.caCert,caCertFileName:e.credentials.caCertFileName}),e}validatorTriggers(){return["credentials.type"]}updateValidators(e){const t=this.azureIotHubConfigForm.get("credentials"),o=t.get("type").value;switch(e&&t.reset({type:o},{emitEvent:!1}),t.get("sasKey").setValidators([]),t.get("privateKey").setValidators([]),t.get("privateKeyFileName").setValidators([]),t.get("cert").setValidators([]),t.get("certFileName").setValidators([]),o){case"sas":t.get("sasKey").setValidators([k.required]);break;case"cert.PEM":t.get("privateKey").setValidators([k.required]),t.get("privateKeyFileName").setValidators([k.required]),t.get("cert").setValidators([k.required]),t.get("certFileName").setValidators([k.required])}t.get("sasKey").updateValueAndValidity({emitEvent:e}),t.get("privateKey").updateValueAndValidity({emitEvent:e}),t.get("privateKeyFileName").updateValueAndValidity({emitEvent:e}),t.get("cert").updateValueAndValidity({emitEvent:e}),t.get("certFileName").updateValueAndValidity({emitEvent:e})}}e("AzureIotHubConfigComponent",Ye),Ye.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Ye,deps:[{token:T.Store},{token:q.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),Ye.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"12.0.0",version:"12.2.15",type:Ye,selector:"tb-action-node-azure-iot-hub-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.topic\n \n \n {{ \'tb.rulenode.topic-required\' | translate }}\n \n \n \n \n tb.rulenode.hostname\n \n \n {{ \'tb.rulenode.hostname-required\' | translate }}\n \n \n \n tb.rulenode.device-id\n \n \n {{ \'tb.rulenode.device-id-required\' | translate }}\n \n \n \n \n \n tb.rulenode.credentials\n \n {{ azureIotHubCredentialsTypeTranslationsMap.get(azureIotHubConfigForm.get(\'credentials.type\').value) | translate }}\n \n \n
\n \n tb.rulenode.credentials-type\n \n \n {{ azureIotHubCredentialsTypeTranslationsMap.get(credentialsType) | translate }}\n \n \n \n {{ \'tb.rulenode.credentials-type-required\' | translate }}\n \n \n
\n \n \n \n \n tb.rulenode.sas-key\n \n \n \n {{ \'tb.rulenode.sas-key-required\' | translate }}\n \n \n \n \n \n \n \n \n \n \n \n \n \n tb.rulenode.private-key-password\n \n \n \n \n
\n
\n
\n
\n
\n',styles:[":host .tb-mqtt-credentials-panel-group{margin:0 6px}:host .tb-hint.client-id{margin-top:-1.25em;max-width:-moz-fit-content;max-width:fit-content}:host mat-checkbox{padding-bottom:16px}\n"],components:[{type:D.MatFormField,selector:"mat-form-field",inputs:["color","floatLabel","appearance","hideRequiredMarker","hintLabel"],exportAs:["matFormField"]},{type:B.MatExpansionPanel,selector:"mat-expansion-panel",inputs:["disabled","expanded","hideToggle","togglePosition"],outputs:["opened","closed","expandedChange","afterExpand","afterCollapse"],exportAs:["matExpansionPanel"]},{type:B.MatExpansionPanelHeader,selector:"mat-expansion-panel-header",inputs:["tabIndex","expandedHeight","collapsedHeight"]},{type:U.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex"],exportAs:["matSelect"]},{type:K.MatOption,selector:"mat-option",exportAs:["matOption"]},{type:j.TogglePasswordComponent,selector:"tb-toggle-password"},{type:z.FileInputComponent,selector:"tb-file-input",inputs:["label","accept","noFileText","inputId","allowedExtensions","dropLabel","contentConvertFunction","required","requiredAsError","disabled","existingFileName","readAsBinary","workFromFileObj","multipleFile"],outputs:["fileNameChanged"]}],directives:[{type:E.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{type:q.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{type:q.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{type:D.MatLabel,selector:"mat-label"},{type:P.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{type:R.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["id","disabled","required","type","value","readonly","placeholder","errorStateMatcher","aria-describedby"],exportAs:["matInput"]},{type:q.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{type:q.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{type:q.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{type:q.FormControlName,selector:"[formControlName]",inputs:["disabled","formControlName","ngModel"],outputs:["ngModelChange"]},{type:w.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{type:D.MatError,selector:"mat-error",inputs:["id"]},{type:D.MatHint,selector:"mat-hint",inputs:["align","id"]},{type:B.MatAccordion,selector:"mat-accordion",inputs:["multi","displayMode","togglePosition","hideToggle"],exportAs:["matAccordion"]},{type:B.MatExpansionPanelTitle,selector:"mat-panel-title"},{type:B.MatExpansionPanelDescription,selector:"mat-panel-description"},{type:q.FormGroupName,selector:"[formGroupName]",inputs:["formGroupName"]},{type:w.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{type:w.NgSwitch,selector:"[ngSwitch]",inputs:["ngSwitch"]},{type:w.NgSwitchCase,selector:"[ngSwitchCase]",inputs:["ngSwitchCase"]},{type:D.MatSuffix,selector:"[matSuffix]"}],pipes:{translate:P.TranslatePipe,safeHtml:Te}}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Ye,decorators:[{type:o,args:[{selector:"tb-action-node-azure-iot-hub-config",templateUrl:"./azure-iot-hub-config.component.html",styleUrls:["./mqtt-config.component.scss"]}]}],ctorParameters:function(){return[{type:T.Store},{type:q.FormBuilder}]}});class Je extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.serviceType=p.TB_RULE_ENGINE}configForm(){return this.checkPointConfigForm}onConfigurationSet(e){this.checkPointConfigForm=this.fb.group({queueId:[e?e.queueId:null,[k.required]]})}}e("CheckPointConfigComponent",Je),Je.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Je,deps:[{token:T.Store},{token:q.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),Je.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"12.0.0",version:"12.2.15",type:Je,selector:"tb-action-node-check-point-config",usesInheritance:!0,ngImport:t,template:'
\n \n \n
\n',components:[{type:_.QueueAutocompleteComponent,selector:"tb-queue-autocomplete",inputs:["labelText","requiredText","required","queueType","disabled"]}],directives:[{type:E.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{type:q.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{type:q.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{type:q.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{type:q.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{type:q.FormControlName,selector:"[formControlName]",inputs:["disabled","formControlName","ngModel"],outputs:["ngModelChange"]}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Je,decorators:[{type:o,args:[{selector:"tb-action-node-check-point-config",templateUrl:"./check-point-config.component.html",styleUrls:[]}]}],ctorParameters:function(){return[{type:T.Store},{type:q.FormBuilder}]}});class Ze extends s{constructor(e,t,o,r){super(e),this.store=e,this.fb=t,this.nodeScriptTestService=o,this.translate=r}configForm(){return this.clearAlarmConfigForm}onConfigurationSet(e){this.clearAlarmConfigForm=this.fb.group({alarmDetailsBuildJs:[e?e.alarmDetailsBuildJs:null,[k.required]],alarmType:[e?e.alarmType:null,[k.required]]})}testScript(){const e=this.clearAlarmConfigForm.get("alarmDetailsBuildJs").value;this.nodeScriptTestService.testNodeScript(e,"json",this.translate.instant("tb.rulenode.details"),"Details",["msg","metadata","msgType"],this.ruleNodeId,"rulenode/clear_alarm_node_script_fn").subscribe((e=>{e&&this.clearAlarmConfigForm.get("alarmDetailsBuildJs").setValue(e)}))}onValidate(){this.jsFuncComponent.validateOnSubmit()}}e("ClearAlarmConfigComponent",Ze),Ze.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Ze,deps:[{token:T.Store},{token:q.FormBuilder},{token:Q.NodeScriptTestService},{token:P.TranslateService}],target:t.ɵɵFactoryTarget.Component}),Ze.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"12.0.0",version:"12.2.15",type:Ze,selector:"tb-action-node-clear-alarm-config",viewQueries:[{propertyName:"jsFuncComponent",first:!0,predicate:["jsFuncComponent"],descendants:!0,static:!0}],usesInheritance:!0,ngImport:t,template:'
\n \n \n \n
\n \n
\n \n tb.rulenode.alarm-type\n \n \n {{ \'tb.rulenode.alarm-type-required\' | translate }}\n \n \n \n
\n',components:[{type:Y.JsFuncComponent,selector:"tb-js-func",inputs:["functionTitle","functionName","functionArgs","validationArgs","resultType","disabled","fillHeight","minHeight","editorCompleter","globalVariables","disableUndefinedCheck","helpId","noValidate","required"]},{type:J.MatButton,selector:"button[mat-button], button[mat-raised-button], button[mat-icon-button], button[mat-fab], button[mat-mini-fab], button[mat-stroked-button], button[mat-flat-button]",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{type:D.MatFormField,selector:"mat-form-field",inputs:["color","floatLabel","appearance","hideRequiredMarker","hintLabel"],exportAs:["matFormField"]}],directives:[{type:E.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{type:q.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{type:q.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{type:P.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{type:q.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{type:q.FormControlName,selector:"[formControlName]",inputs:["disabled","formControlName","ngModel"],outputs:["ngModelChange"]},{type:D.MatLabel,selector:"mat-label"},{type:R.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["id","disabled","required","type","value","readonly","placeholder","errorStateMatcher","aria-describedby"],exportAs:["matInput"]},{type:q.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{type:q.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{type:w.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{type:D.MatError,selector:"mat-error",inputs:["id"]},{type:D.MatHint,selector:"mat-hint",inputs:["align","id"]}],pipes:{translate:P.TranslatePipe,safeHtml:Te}}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Ze,decorators:[{type:o,args:[{selector:"tb-action-node-clear-alarm-config",templateUrl:"./clear-alarm-config.component.html",styleUrls:[]}]}],ctorParameters:function(){return[{type:T.Store},{type:q.FormBuilder},{type:Q.NodeScriptTestService},{type:P.TranslateService}]},propDecorators:{jsFuncComponent:[{type:a,args:["jsFuncComponent",{static:!0}]}]}});class Xe extends s{constructor(e,t,o,r){super(e),this.store=e,this.fb=t,this.nodeScriptTestService=o,this.translate=r,this.alarmSeverities=Object.keys(d),this.alarmSeverityTranslationMap=f,this.separatorKeysCodes=[Z,X,ee]}configForm(){return this.createAlarmConfigForm}onConfigurationSet(e){this.createAlarmConfigForm=this.fb.group({alarmDetailsBuildJs:[e?e.alarmDetailsBuildJs:null,[k.required]],useMessageAlarmData:[!!e&&e.useMessageAlarmData,[]],overwriteAlarmDetails:[!!e&&e.overwriteAlarmDetails,[]],alarmType:[e?e.alarmType:null,[]],severity:[e?e.severity:null,[]],propagate:[!!e&&e.propagate,[]],relationTypes:[e?e.relationTypes:null,[]],propagateToOwner:[!!e&&e.propagateToOwner,[]],propagateToTenant:[!!e&&e.propagateToTenant,[]],dynamicSeverity:!1}),this.createAlarmConfigForm.get("dynamicSeverity").valueChanges.subscribe((e=>{e?this.createAlarmConfigForm.get("severity").patchValue("",{emitEvent:!1}):this.createAlarmConfigForm.get("severity").patchValue(this.alarmSeverities[0],{emitEvent:!1})}))}validatorTriggers(){return["useMessageAlarmData"]}updateValidators(e){this.createAlarmConfigForm.get("useMessageAlarmData").value?(this.createAlarmConfigForm.get("alarmType").setValidators([]),this.createAlarmConfigForm.get("severity").setValidators([])):(this.createAlarmConfigForm.get("alarmType").setValidators([k.required]),this.createAlarmConfigForm.get("severity").setValidators([k.required])),this.createAlarmConfigForm.get("alarmType").updateValueAndValidity({emitEvent:e}),this.createAlarmConfigForm.get("severity").updateValueAndValidity({emitEvent:e})}testScript(){const e=this.createAlarmConfigForm.get("alarmDetailsBuildJs").value;this.nodeScriptTestService.testNodeScript(e,"json",this.translate.instant("tb.rulenode.details"),"Details",["msg","metadata","msgType"],this.ruleNodeId,"rulenode/create_alarm_node_script_fn").subscribe((e=>{e&&this.createAlarmConfigForm.get("alarmDetailsBuildJs").setValue(e)}))}removeKey(e,t){const o=this.createAlarmConfigForm.get(t).value,r=o.indexOf(e);r>=0&&(o.splice(r,1),this.createAlarmConfigForm.get(t).setValue(o,{emitEvent:!0}))}addKey(e,t){const o=e.input;let r=e.value;if((r||"").trim()){r=r.trim();let e=this.createAlarmConfigForm.get(t).value;e&&-1!==e.indexOf(r)||(e||(e=[]),e.push(r),this.createAlarmConfigForm.get(t).setValue(e,{emitEvent:!0}))}o&&(o.value="")}onValidate(){const e=this.createAlarmConfigForm.get("useMessageAlarmData").value,t=this.createAlarmConfigForm.get("overwriteAlarmDetails").value;e&&!t||this.jsFuncComponent.validateOnSubmit()}}e("CreateAlarmConfigComponent",Xe),Xe.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Xe,deps:[{token:T.Store},{token:q.FormBuilder},{token:Q.NodeScriptTestService},{token:P.TranslateService}],target:t.ɵɵFactoryTarget.Component}),Xe.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"12.0.0",version:"12.2.15",type:Xe,selector:"tb-action-node-create-alarm-config",viewQueries:[{propertyName:"jsFuncComponent",first:!0,predicate:["jsFuncComponent"],descendants:!0,static:!0}],usesInheritance:!0,ngImport:t,template:'
\n \n {{ \'tb.rulenode.use-message-alarm-data\' | translate }}\n \n \n {{ \'tb.rulenode.overwrite-alarm-details\' | translate }}\n \n
\n \n \n \n
\n \n
\n
\n
\n \n tb.rulenode.alarm-type\n \n \n {{ \'tb.rulenode.alarm-type-required\' | translate }}\n \n \n \n \n {{ \'tb.rulenode.use-alarm-severity-pattern\' | translate }}\n \n \n tb.rulenode.alarm-severity\n \n \n {{ alarmSeverityTranslationMap.get(severity) | translate }}\n \n \n \n {{ \'tb.rulenode.alarm-severity-required\' | translate }}\n \n \n \n tb.rulenode.alarm-severity-pattern\n \n \n {{ \'tb.rulenode.alarm-severity-required\' | translate }}\n \n \n \n \n {{ \'tb.rulenode.propagate\' | translate }}\n \n
\n \n tb.rulenode.relation-types-list\n \n \n {{key}}\n close\n \n \n \n tb.rulenode.relation-types-list-hint\n \n
\n \n {{ \'tb.rulenode.propagate-to-owner\' | translate }}\n \n \n {{ \'tb.rulenode.propagate-to-tenant\' | translate }}\n \n
\n
\n',components:[{type:V.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex","aria-label","aria-labelledby","id","labelPosition","name","required","checked","disabled","indeterminate","aria-describedby","value"],outputs:["change","indeterminateChange"],exportAs:["matCheckbox"]},{type:Y.JsFuncComponent,selector:"tb-js-func",inputs:["functionTitle","functionName","functionArgs","validationArgs","resultType","disabled","fillHeight","minHeight","editorCompleter","globalVariables","disableUndefinedCheck","helpId","noValidate","required"]},{type:J.MatButton,selector:"button[mat-button], button[mat-raised-button], button[mat-icon-button], button[mat-fab], button[mat-mini-fab], button[mat-stroked-button], button[mat-flat-button]",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{type:D.MatFormField,selector:"mat-form-field",inputs:["color","floatLabel","appearance","hideRequiredMarker","hintLabel"],exportAs:["matFormField"]},{type:U.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex"],exportAs:["matSelect"]},{type:K.MatOption,selector:"mat-option",exportAs:["matOption"]},{type:te.MatChipList,selector:"mat-chip-list",inputs:["aria-orientation","multiple","compareWith","value","required","placeholder","disabled","selectable","tabIndex","errorStateMatcher"],outputs:["change","valueChange"],exportAs:["matChipList"]},{type:oe.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]}],directives:[{type:E.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{type:q.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{type:q.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{type:q.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{type:q.FormControlName,selector:"[formControlName]",inputs:["disabled","formControlName","ngModel"],outputs:["ngModelChange"]},{type:re.DefaultShowHideDirective,selector:" [fxShow], [fxShow.print], [fxShow.xs], [fxShow.sm], [fxShow.md], [fxShow.lg], [fxShow.xl], [fxShow.lt-sm], [fxShow.lt-md], [fxShow.lt-lg], [fxShow.lt-xl], [fxShow.gt-xs], [fxShow.gt-sm], [fxShow.gt-md], [fxShow.gt-lg], [fxHide], [fxHide.print], [fxHide.xs], [fxHide.sm], [fxHide.md], [fxHide.lg], [fxHide.xl], [fxHide.lt-sm], [fxHide.lt-md], [fxHide.lt-lg], [fxHide.lt-xl], [fxHide.gt-xs], [fxHide.gt-sm], [fxHide.gt-md], [fxHide.gt-lg]",inputs:["fxShow","fxShow.print","fxShow.xs","fxShow.sm","fxShow.md","fxShow.lg","fxShow.xl","fxShow.lt-sm","fxShow.lt-md","fxShow.lt-lg","fxShow.lt-xl","fxShow.gt-xs","fxShow.gt-sm","fxShow.gt-md","fxShow.gt-lg","fxHide","fxHide.print","fxHide.xs","fxHide.sm","fxHide.md","fxHide.lg","fxHide.xl","fxHide.lt-sm","fxHide.lt-md","fxHide.lt-lg","fxHide.lt-xl","fxHide.gt-xs","fxHide.gt-sm","fxHide.gt-md","fxHide.gt-lg"]},{type:P.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{type:E.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{type:D.MatLabel,selector:"mat-label"},{type:R.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["id","disabled","required","type","value","readonly","placeholder","errorStateMatcher","aria-describedby"],exportAs:["matInput"]},{type:q.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{type:q.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{type:w.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{type:D.MatError,selector:"mat-error",inputs:["id"]},{type:D.MatHint,selector:"mat-hint",inputs:["align","id"]},{type:w.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{type:te.MatChip,selector:"mat-basic-chip, [mat-basic-chip], mat-chip, [mat-chip]",inputs:["color","disableRipple","tabIndex","selected","value","selectable","disabled","removable"],outputs:["selectionChange","destroyed","removed"],exportAs:["matChip"]},{type:te.MatChipRemove,selector:"[matChipRemove]"},{type:te.MatChipInput,selector:"input[matChipInputFor]",inputs:["matChipInputSeparatorKeyCodes","placeholder","id","matChipInputFor","matChipInputAddOnBlur","disabled"],outputs:["matChipInputTokenEnd"],exportAs:["matChipInput","matChipInputFor"]}],pipes:{translate:P.TranslatePipe,safeHtml:Te}}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Xe,decorators:[{type:o,args:[{selector:"tb-action-node-create-alarm-config",templateUrl:"./create-alarm-config.component.html",styleUrls:[]}]}],ctorParameters:function(){return[{type:T.Store},{type:q.FormBuilder},{type:Q.NodeScriptTestService},{type:P.TranslateService}]},propDecorators:{jsFuncComponent:[{type:a,args:["jsFuncComponent",{static:!0}]}]}});class et extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.directionTypes=Object.keys(c),this.directionTypeTranslations=g,this.entityType=x}configForm(){return this.createRelationConfigForm}onConfigurationSet(e){this.createRelationConfigForm=this.fb.group({direction:[e?e.direction:null,[k.required]],entityType:[e?e.entityType:null,[k.required]],entityNamePattern:[e?e.entityNamePattern:null,[]],entityTypePattern:[e?e.entityTypePattern:null,[]],relationType:[e?e.relationType:null,[k.required]],createEntityIfNotExists:[!!e&&e.createEntityIfNotExists,[]],removeCurrentRelations:[!!e&&e.removeCurrentRelations,[]],changeOriginatorToRelatedEntity:[!!e&&e.changeOriginatorToRelatedEntity,[]],entityCacheExpiration:[e?e.entityCacheExpiration:null,[k.required,k.min(0)]]})}validatorTriggers(){return["entityType"]}updateValidators(e){const t=this.createRelationConfigForm.get("entityType").value;t?this.createRelationConfigForm.get("entityNamePattern").setValidators([k.required,k.pattern(/.*\S.*/)]):this.createRelationConfigForm.get("entityNamePattern").setValidators([]),!t||t!==x.DEVICE&&t!==x.ASSET?this.createRelationConfigForm.get("entityTypePattern").setValidators([]):this.createRelationConfigForm.get("entityTypePattern").setValidators([k.required,k.pattern(/.*\S.*/)]),this.createRelationConfigForm.get("entityNamePattern").updateValueAndValidity({emitEvent:e}),this.createRelationConfigForm.get("entityTypePattern").updateValueAndValidity({emitEvent:e})}prepareOutputConfig(e){return e.entityNamePattern=e.entityNamePattern?e.entityNamePattern.trim():null,e.entityTypePattern=e.entityTypePattern?e.entityTypePattern.trim():null,e}}e("CreateRelationConfigComponent",et),et.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:et,deps:[{token:T.Store},{token:q.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),et.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"12.0.0",version:"12.2.15",type:et,selector:"tb-action-node-create-relation-config",usesInheritance:!0,ngImport:t,template:'
\n \n relation.direction\n \n \n {{ directionTypeTranslations.get(type) | translate }}\n \n \n \n
\n \n \n \n tb.rulenode.entity-name-pattern\n \n \n {{ \'tb.rulenode.entity-name-pattern-required\' | translate }}\n \n \n \n \n tb.rulenode.entity-type-pattern\n \n \n {{ \'tb.rulenode.entity-type-pattern-required\' | translate }}\n \n \n \n
\n \n tb.rulenode.relation-type-pattern\n \n \n {{ \'tb.rulenode.relation-type-pattern-required\' | translate }}\n \n \n \n
\n \n {{ \'tb.rulenode.create-entity-if-not-exists\' | translate }}\n \n
tb.rulenode.create-entity-if-not-exists-hint
\n
\n \n {{ \'tb.rulenode.remove-current-relations\' | translate }}\n \n
tb.rulenode.remove-current-relations-hint
\n \n {{ \'tb.rulenode.change-originator-to-related-entity\' | translate }}\n \n
tb.rulenode.change-originator-to-related-entity-hint
\n \n tb.rulenode.entity-cache-expiration\n \n \n {{ \'tb.rulenode.entity-cache-expiration-required\' | translate }}\n \n \n {{ \'tb.rulenode.entity-cache-expiration-range\' | translate }}\n \n tb.rulenode.entity-cache-expiration-hint\n \n
\n',components:[{type:D.MatFormField,selector:"mat-form-field",inputs:["color","floatLabel","appearance","hideRequiredMarker","hintLabel"],exportAs:["matFormField"]},{type:U.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex"],exportAs:["matSelect"]},{type:K.MatOption,selector:"mat-option",exportAs:["matOption"]},{type:ae.EntityTypeSelectComponent,selector:"tb-entity-type-select",inputs:["allowedEntityTypes","useAliasEntityTypes","showLabel","required","disabled"]},{type:V.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex","aria-label","aria-labelledby","id","labelPosition","name","required","checked","disabled","indeterminate","aria-describedby","value"],outputs:["change","indeterminateChange"],exportAs:["matCheckbox"]}],directives:[{type:E.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{type:q.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{type:q.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{type:D.MatLabel,selector:"mat-label"},{type:P.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{type:q.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{type:q.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{type:q.FormControlName,selector:"[formControlName]",inputs:["disabled","formControlName","ngModel"],outputs:["ngModelChange"]},{type:w.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{type:E.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{type:w.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{type:E.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{type:R.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["id","disabled","required","type","value","readonly","placeholder","errorStateMatcher","aria-describedby"],exportAs:["matInput"]},{type:q.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{type:D.MatError,selector:"mat-error",inputs:["id"]},{type:D.MatHint,selector:"mat-hint",inputs:["align","id"]},{type:q.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{type:q.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"}],pipes:{translate:P.TranslatePipe,safeHtml:Te}}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:et,decorators:[{type:o,args:[{selector:"tb-action-node-create-relation-config",templateUrl:"./create-relation-config.component.html",styleUrls:[]}]}],ctorParameters:function(){return[{type:T.Store},{type:q.FormBuilder}]}});class tt extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.directionTypes=Object.keys(c),this.directionTypeTranslations=g,this.entityType=x}configForm(){return this.deleteRelationConfigForm}onConfigurationSet(e){this.deleteRelationConfigForm=this.fb.group({deleteForSingleEntity:[!!e&&e.deleteForSingleEntity,[]],direction:[e?e.direction:null,[k.required]],entityType:[e?e.entityType:null,[]],entityNamePattern:[e?e.entityNamePattern:null,[]],relationType:[e?e.relationType:null,[k.required]],entityCacheExpiration:[e?e.entityCacheExpiration:null,[k.required,k.min(0)]]})}validatorTriggers(){return["deleteForSingleEntity","entityType"]}updateValidators(e){const t=this.deleteRelationConfigForm.get("deleteForSingleEntity").value,o=this.deleteRelationConfigForm.get("entityType").value;t?this.deleteRelationConfigForm.get("entityType").setValidators([k.required]):this.deleteRelationConfigForm.get("entityType").setValidators([]),t&&o?this.deleteRelationConfigForm.get("entityNamePattern").setValidators([k.required,k.pattern(/.*\S.*/)]):this.deleteRelationConfigForm.get("entityNamePattern").setValidators([]),this.deleteRelationConfigForm.get("entityType").updateValueAndValidity({emitEvent:!1}),this.deleteRelationConfigForm.get("entityNamePattern").updateValueAndValidity({emitEvent:e})}prepareOutputConfig(e){return e.entityNamePattern=e.entityNamePattern?e.entityNamePattern.trim():null,e}}e("DeleteRelationConfigComponent",tt),tt.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:tt,deps:[{token:T.Store},{token:q.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),tt.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"12.0.0",version:"12.2.15",type:tt,selector:"tb-action-node-delete-relation-config",usesInheritance:!0,ngImport:t,template:'
\n \n {{ \'tb.rulenode.delete-relation-to-specific-entity\' | translate }}\n \n
tb.rulenode.delete-relation-hint
\n \n relation.direction\n \n \n {{ directionTypeTranslations.get(type) | translate }}\n \n \n \n
\n \n \n \n tb.rulenode.entity-name-pattern\n \n \n {{ \'tb.rulenode.entity-name-pattern-required\' | translate }}\n \n \n \n
\n \n tb.rulenode.relation-type-pattern\n \n \n {{ \'tb.rulenode.relation-type-pattern-required\' | translate }}\n \n \n \n \n tb.rulenode.entity-cache-expiration\n \n \n {{ \'tb.rulenode.entity-cache-expiration-required\' | translate }}\n \n \n {{ \'tb.rulenode.entity-cache-expiration-range\' | translate }}\n \n tb.rulenode.entity-cache-expiration-hint\n \n
\n',components:[{type:V.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex","aria-label","aria-labelledby","id","labelPosition","name","required","checked","disabled","indeterminate","aria-describedby","value"],outputs:["change","indeterminateChange"],exportAs:["matCheckbox"]},{type:D.MatFormField,selector:"mat-form-field",inputs:["color","floatLabel","appearance","hideRequiredMarker","hintLabel"],exportAs:["matFormField"]},{type:U.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex"],exportAs:["matSelect"]},{type:K.MatOption,selector:"mat-option",exportAs:["matOption"]},{type:ae.EntityTypeSelectComponent,selector:"tb-entity-type-select",inputs:["allowedEntityTypes","useAliasEntityTypes","showLabel","required","disabled"]}],directives:[{type:E.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{type:q.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{type:q.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{type:q.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{type:q.FormControlName,selector:"[formControlName]",inputs:["disabled","formControlName","ngModel"],outputs:["ngModelChange"]},{type:P.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{type:D.MatLabel,selector:"mat-label"},{type:q.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{type:w.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{type:w.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{type:E.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{type:E.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{type:R.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["id","disabled","required","type","value","readonly","placeholder","errorStateMatcher","aria-describedby"],exportAs:["matInput"]},{type:q.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{type:D.MatError,selector:"mat-error",inputs:["id"]},{type:D.MatHint,selector:"mat-hint",inputs:["align","id"]},{type:q.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{type:q.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"}],pipes:{translate:P.TranslatePipe,safeHtml:Te}}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:tt,decorators:[{type:o,args:[{selector:"tb-action-node-delete-relation-config",templateUrl:"./delete-relation-config.component.html",styleUrls:[]}]}],ctorParameters:function(){return[{type:T.Store},{type:q.FormBuilder}]}});class ot extends s{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.deviceProfile}onConfigurationSet(e){this.deviceProfile=this.fb.group({persistAlarmRulesState:[!!e&&e.persistAlarmRulesState,k.required],fetchAlarmRulesStateOnStart:[!!e&&e.fetchAlarmRulesStateOnStart,k.required]})}}e("DeviceProfileConfigComponent",ot),ot.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:ot,deps:[{token:T.Store},{token:q.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),ot.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"12.0.0",version:"12.2.15",type:ot,selector:"tb-device-profile-config",usesInheritance:!0,ngImport:t,template:'
\n \n {{ \'tb.rulenode.persist-alarm-rules\' | translate }}\n \n \n {{ \'tb.rulenode.fetch-alarm-rules\' | translate }}\n \n
\n',components:[{type:V.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex","aria-label","aria-labelledby","id","labelPosition","name","required","checked","disabled","indeterminate","aria-describedby","value"],outputs:["change","indeterminateChange"],exportAs:["matCheckbox"]}],directives:[{type:E.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{type:q.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{type:q.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{type:E.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{type:q.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{type:q.FormControlName,selector:"[formControlName]",inputs:["disabled","formControlName","ngModel"],outputs:["ngModelChange"]}],pipes:{translate:P.TranslatePipe}}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:ot,decorators:[{type:o,args:[{selector:"tb-device-profile-config",templateUrl:"./device-profile-config.component.html",styleUrls:[]}]}],ctorParameters:function(){return[{type:T.Store},{type:q.FormBuilder}]}});class rt extends s{constructor(e,t,o,r){super(e),this.store=e,this.fb=t,this.nodeScriptTestService=o,this.translate=r}configForm(){return this.generatorConfigForm}onConfigurationSet(e){this.generatorConfigForm=this.fb.group({msgCount:[e?e.msgCount:null,[k.required,k.min(0)]],periodInSeconds:[e?e.periodInSeconds:null,[k.required,k.min(1)]],originator:[e?e.originator:null,[]],jsScript:[e?e.jsScript:null,[k.required]]})}prepareInputConfig(e){return e&&(e.originatorId&&e.originatorType?e.originator={id:e.originatorId,entityType:e.originatorType}:e.originator=null,delete e.originatorId,delete e.originatorType),e}prepareOutputConfig(e){return e.originator?(e.originatorId=e.originator.id,e.originatorType=e.originator.entityType):(e.originatorId=null,e.originatorType=null),delete e.originator,e}testScript(){const e=this.generatorConfigForm.get("jsScript").value;this.nodeScriptTestService.testNodeScript(e,"generate",this.translate.instant("tb.rulenode.generator"),"Generate",["prevMsg","prevMetadata","prevMsgType"],this.ruleNodeId,"rulenode/generator_node_script_fn").subscribe((e=>{e&&this.generatorConfigForm.get("jsScript").setValue(e)}))}onValidate(){this.jsFuncComponent.validateOnSubmit()}}e("GeneratorConfigComponent",rt),rt.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:rt,deps:[{token:T.Store},{token:q.FormBuilder},{token:Q.NodeScriptTestService},{token:P.TranslateService}],target:t.ɵɵFactoryTarget.Component}),rt.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"12.0.0",version:"12.2.15",type:rt,selector:"tb-action-node-generator-config",viewQueries:[{propertyName:"jsFuncComponent",first:!0,predicate:["jsFuncComponent"],descendants:!0,static:!0}],usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.message-count\n \n \n {{ \'tb.rulenode.message-count-required\' | translate }}\n \n \n {{ \'tb.rulenode.min-message-count-message\' | translate }}\n \n \n \n tb.rulenode.period-seconds\n \n \n {{ \'tb.rulenode.period-seconds-required\' | translate }}\n \n \n {{ \'tb.rulenode.min-period-seconds-message\' | translate }}\n \n \n
\n \n \n \n
\n \n \n \n
\n \n
\n
\n',components:[{type:D.MatFormField,selector:"mat-form-field",inputs:["color","floatLabel","appearance","hideRequiredMarker","hintLabel"],exportAs:["matFormField"]},{type:ne.EntitySelectComponent,selector:"tb-entity-select",inputs:["allowedEntityTypes","useAliasEntityTypes","required","disabled"]},{type:Y.JsFuncComponent,selector:"tb-js-func",inputs:["functionTitle","functionName","functionArgs","validationArgs","resultType","disabled","fillHeight","minHeight","editorCompleter","globalVariables","disableUndefinedCheck","helpId","noValidate","required"]},{type:J.MatButton,selector:"button[mat-button], button[mat-raised-button], button[mat-icon-button], button[mat-fab], button[mat-mini-fab], button[mat-stroked-button], button[mat-flat-button]",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]}],directives:[{type:E.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{type:q.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{type:q.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{type:D.MatLabel,selector:"mat-label"},{type:P.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{type:q.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{type:q.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{type:R.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["id","disabled","required","type","value","readonly","placeholder","errorStateMatcher","aria-describedby"],exportAs:["matInput"]},{type:q.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{type:q.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{type:q.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{type:q.FormControlName,selector:"[formControlName]",inputs:["disabled","formControlName","ngModel"],outputs:["ngModelChange"]},{type:w.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{type:D.MatError,selector:"mat-error",inputs:["id"]}],pipes:{translate:P.TranslatePipe}}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:rt,decorators:[{type:o,args:[{selector:"tb-action-node-generator-config",templateUrl:"./generator-config.component.html",styleUrls:[]}]}],ctorParameters:function(){return[{type:T.Store},{type:q.FormBuilder},{type:Q.NodeScriptTestService},{type:P.TranslateService}]},propDecorators:{jsFuncComponent:[{type:a,args:["jsFuncComponent",{static:!0}]}]}});class at extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.perimeterType=Ae,this.perimeterTypes=Object.keys(Ae),this.perimeterTypeTranslationMap=Ge,this.rangeUnits=Object.keys(Ee),this.rangeUnitTranslationMap=Pe,this.timeUnits=Object.keys(De),this.timeUnitsTranslationMap=Ve}configForm(){return this.geoActionConfigForm}onConfigurationSet(e){this.geoActionConfigForm=this.fb.group({latitudeKeyName:[e?e.latitudeKeyName:null,[k.required]],longitudeKeyName:[e?e.longitudeKeyName:null,[k.required]],perimeterType:[e?e.perimeterType:null,[k.required]],fetchPerimeterInfoFromMessageMetadata:[!!e&&e.fetchPerimeterInfoFromMessageMetadata,[]],perimeterKeyName:[e?e.perimeterKeyName:null,[]],centerLatitude:[e?e.centerLatitude:null,[]],centerLongitude:[e?e.centerLatitude:null,[]],range:[e?e.range:null,[]],rangeUnit:[e?e.rangeUnit:null,[]],polygonsDefinition:[e?e.polygonsDefinition:null,[]],minInsideDuration:[e?e.minInsideDuration:null,[k.required,k.min(1),k.max(2147483647)]],minInsideDurationTimeUnit:[e?e.minInsideDurationTimeUnit:null,[k.required]],minOutsideDuration:[e?e.minOutsideDuration:null,[k.required,k.min(1),k.max(2147483647)]],minOutsideDurationTimeUnit:[e?e.minOutsideDurationTimeUnit:null,[k.required]]})}validatorTriggers(){return["fetchPerimeterInfoFromMessageMetadata","perimeterType"]}updateValidators(e){const t=this.geoActionConfigForm.get("fetchPerimeterInfoFromMessageMetadata").value,o=this.geoActionConfigForm.get("perimeterType").value;t?this.geoActionConfigForm.get("perimeterKeyName").setValidators([k.required]):this.geoActionConfigForm.get("perimeterKeyName").setValidators([]),t||o!==Ae.CIRCLE?(this.geoActionConfigForm.get("centerLatitude").setValidators([]),this.geoActionConfigForm.get("centerLongitude").setValidators([]),this.geoActionConfigForm.get("range").setValidators([]),this.geoActionConfigForm.get("rangeUnit").setValidators([])):(this.geoActionConfigForm.get("centerLatitude").setValidators([k.required,k.min(-90),k.max(90)]),this.geoActionConfigForm.get("centerLongitude").setValidators([k.required,k.min(-180),k.max(180)]),this.geoActionConfigForm.get("range").setValidators([k.required,k.min(0)]),this.geoActionConfigForm.get("rangeUnit").setValidators([k.required])),t||o!==Ae.POLYGON?this.geoActionConfigForm.get("polygonsDefinition").setValidators([]):this.geoActionConfigForm.get("polygonsDefinition").setValidators([k.required]),this.geoActionConfigForm.get("perimeterKeyName").updateValueAndValidity({emitEvent:e}),this.geoActionConfigForm.get("centerLatitude").updateValueAndValidity({emitEvent:e}),this.geoActionConfigForm.get("centerLongitude").updateValueAndValidity({emitEvent:e}),this.geoActionConfigForm.get("range").updateValueAndValidity({emitEvent:e}),this.geoActionConfigForm.get("rangeUnit").updateValueAndValidity({emitEvent:e}),this.geoActionConfigForm.get("polygonsDefinition").updateValueAndValidity({emitEvent:e})}}e("GpsGeoActionConfigComponent",at),at.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:at,deps:[{token:T.Store},{token:q.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),at.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"12.0.0",version:"12.2.15",type:at,selector:"tb-action-node-gps-geofencing-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.latitude-key-name\n \n \n {{ \'tb.rulenode.latitude-key-name-required\' | translate }}\n \n \n \n tb.rulenode.longitude-key-name\n \n \n {{ \'tb.rulenode.longitude-key-name-required\' | translate }}\n \n \n \n tb.rulenode.perimeter-type\n \n \n {{ perimeterTypeTranslationMap.get(type) | translate }}\n \n \n \n \n {{ \'tb.rulenode.fetch-perimeter-info-from-message-metadata\' | translate }}\n \n \n tb.rulenode.perimeter-key-name\n \n \n {{ \'tb.rulenode.perimeter-key-name-required\' | translate }}\n \n \n
\n
\n \n tb.rulenode.circle-center-latitude\n \n \n {{ \'tb.rulenode.circle-center-latitude-required\' | translate }}\n \n \n \n tb.rulenode.circle-center-longitude\n \n \n {{ \'tb.rulenode.circle-center-longitude-required\' | translate }}\n \n \n
\n
\n \n tb.rulenode.range\n \n \n {{ \'tb.rulenode.range-required\' | translate }}\n \n \n \n tb.rulenode.range-units\n \n \n {{ rangeUnitTranslationMap.get(type) | translate }}\n \n \n \n
\n
\n
\n \n tb.rulenode.polygon-definition\n \n \n {{ \'tb.rulenode.polygon-definition-required\' | translate }}\n \n \n
\n
\n \n tb.rulenode.min-inside-duration\n \n \n {{ \'tb.rulenode.min-inside-duration-value-required\' | translate }}\n \n \n {{ \'tb.rulenode.time-value-range\' | translate }}\n \n \n {{ \'tb.rulenode.time-value-range\' | translate }}\n \n \n \n tb.rulenode.min-inside-duration-time-unit\n \n \n {{ timeUnitsTranslationMap.get(timeUnit) | translate }}\n \n \n \n
\n
\n \n tb.rulenode.min-outside-duration\n \n \n {{ \'tb.rulenode.min-outside-duration-value-required\' | translate }}\n \n \n {{ \'tb.rulenode.time-value-range\' | translate }}\n \n \n {{ \'tb.rulenode.time-value-range\' | translate }}\n \n \n \n tb.rulenode.min-outside-duration-time-unit\n \n \n {{ timeUnitsTranslationMap.get(timeUnit) | translate }}\n \n \n \n
\n
\n',components:[{type:D.MatFormField,selector:"mat-form-field",inputs:["color","floatLabel","appearance","hideRequiredMarker","hintLabel"],exportAs:["matFormField"]},{type:U.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex"],exportAs:["matSelect"]},{type:K.MatOption,selector:"mat-option",exportAs:["matOption"]},{type:V.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex","aria-label","aria-labelledby","id","labelPosition","name","required","checked","disabled","indeterminate","aria-describedby","value"],outputs:["change","indeterminateChange"],exportAs:["matCheckbox"]}],directives:[{type:E.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{type:q.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{type:q.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{type:D.MatLabel,selector:"mat-label"},{type:P.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{type:R.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["id","disabled","required","type","value","readonly","placeholder","errorStateMatcher","aria-describedby"],exportAs:["matInput"]},{type:q.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{type:q.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{type:q.FormControlName,selector:"[formControlName]",inputs:["disabled","formControlName","ngModel"],outputs:["ngModelChange"]},{type:q.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{type:w.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{type:D.MatError,selector:"mat-error",inputs:["id"]},{type:E.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{type:w.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{type:E.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{type:q.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{type:q.MaxValidator,selector:"input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]",inputs:["max"]},{type:q.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"}],pipes:{translate:P.TranslatePipe}}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:at,decorators:[{type:o,args:[{selector:"tb-action-node-gps-geofencing-config",templateUrl:"./gps-geo-action-config.component.html",styleUrls:[]}]}],ctorParameters:function(){return[{type:T.Store},{type:q.FormBuilder}]}});class nt extends y{constructor(e,t,o,r){super(e),this.store=e,this.translate=t,this.injector=o,this.fb=r,this.propagateChange=null,this.valueChangeSubscription=null}get required(){return this.requiredValue}set required(e){this.requiredValue=le(e)}ngOnInit(){this.ngControl=this.injector.get(M),null!=this.ngControl&&(this.ngControl.valueAccessor=this),this.kvListFormGroup=this.fb.group({}),this.kvListFormGroup.addControl("keyVals",this.fb.array([]))}keyValsFormArray(){return this.kvListFormGroup.get("keyVals")}registerOnChange(e){this.propagateChange=e}registerOnTouched(e){}setDisabledState(e){this.disabled=e,this.disabled?this.kvListFormGroup.disable({emitEvent:!1}):this.kvListFormGroup.enable({emitEvent:!1})}writeValue(e){this.valueChangeSubscription&&this.valueChangeSubscription.unsubscribe();const t=[];if(e)for(const o of Object.keys(e))Object.prototype.hasOwnProperty.call(e,o)&&t.push(this.fb.group({key:[o,[k.required]],value:[e[o],[k.required]]}));this.kvListFormGroup.setControl("keyVals",this.fb.array(t)),this.valueChangeSubscription=this.kvListFormGroup.valueChanges.subscribe((()=>{this.updateModel()}))}removeKeyVal(e){this.kvListFormGroup.get("keyVals").removeAt(e)}addKeyVal(){this.kvListFormGroup.get("keyVals").push(this.fb.group({key:["",[k.required]],value:["",[k.required]]}))}validate(e){return!this.kvListFormGroup.get("keyVals").value.length&&this.required?{kvMapRequired:!0}:this.kvListFormGroup.valid?null:{kvFieldsRequired:!0}}updateModel(){const e=this.kvListFormGroup.get("keyVals").value;if(this.required&&!e.length||!this.kvListFormGroup.valid)this.propagateChange(null);else{const t={};e.forEach((e=>{t[e.key]=e.value})),this.propagateChange(t)}}}e("KvMapConfigComponent",nt),nt.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:nt,deps:[{token:T.Store},{token:P.TranslateService},{token:t.Injector},{token:q.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),nt.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"12.0.0",version:"12.2.15",type:nt,selector:"tb-kv-map-config",inputs:{disabled:"disabled",requiredText:"requiredText",keyText:"keyText",keyRequiredText:"keyRequiredText",valText:"valText",valRequiredText:"valRequiredText",hintText:"hintText",required:"required"},providers:[{provide:S,useExisting:n((()=>nt)),multi:!0},{provide:A,useExisting:n((()=>nt)),multi:!0}],usesInheritance:!0,ngImport:t,template:'
\n
\n {{ keyText | translate }}\n {{ valText | translate }}\n \n
\n
\n
\n \n \n \n \n {{ keyRequiredText | translate }}\n \n \n \n \n \n \n {{ valRequiredText | translate }}\n \n \n \n
\n
\n
\n \n
\n \n
\n
\n',styles:[":host .tb-kv-map-config{margin-bottom:16px}:host .tb-kv-map-config .header{padding-left:5px;padding-right:5px;padding-bottom:5px}:host .tb-kv-map-config .header .cell{padding-left:5px;padding-right:5px;color:#0000008a;font-size:12px;font-weight:700;white-space:nowrap}:host .tb-kv-map-config .body{padding-left:5px;padding-right:5px;padding-bottom:20px;max-height:300px;overflow:auto}:host .tb-kv-map-config .body .cell{padding-left:5px;padding-right:5px}:host ::ng-deep .tb-kv-map-config .body mat-form-field.cell{margin:0}:host ::ng-deep .tb-kv-map-config .body mat-form-field.cell .mat-form-field-infix{border-top:0}:host ::ng-deep .tb-kv-map-config .body button.mat-button{margin:0;align-self:baseline}\n"],components:[{type:D.MatFormField,selector:"mat-form-field",inputs:["color","floatLabel","appearance","hideRequiredMarker","hintLabel"],exportAs:["matFormField"]},{type:J.MatButton,selector:"button[mat-button], button[mat-raised-button], button[mat-icon-button], button[mat-fab], button[mat-mini-fab], button[mat-stroked-button], button[mat-flat-button]",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{type:oe.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{type:ie.TbErrorComponent,selector:"tb-error",inputs:["error"]}],directives:[{type:E.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{type:q.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{type:q.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{type:E.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{type:E.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{type:re.DefaultShowHideDirective,selector:" [fxShow], [fxShow.print], [fxShow.xs], [fxShow.sm], [fxShow.md], [fxShow.lg], [fxShow.xl], [fxShow.lt-sm], [fxShow.lt-md], [fxShow.lt-lg], [fxShow.lt-xl], [fxShow.gt-xs], [fxShow.gt-sm], [fxShow.gt-md], [fxShow.gt-lg], [fxHide], [fxHide.print], [fxHide.xs], [fxHide.sm], [fxHide.md], [fxHide.lg], [fxHide.xl], [fxHide.lt-sm], [fxHide.lt-md], [fxHide.lt-lg], [fxHide.lt-xl], [fxHide.gt-xs], [fxHide.gt-sm], [fxHide.gt-md], [fxHide.gt-lg]",inputs:["fxShow","fxShow.print","fxShow.xs","fxShow.sm","fxShow.md","fxShow.lg","fxShow.xl","fxShow.lt-sm","fxShow.lt-md","fxShow.lt-lg","fxShow.lt-xl","fxShow.gt-xs","fxShow.gt-sm","fxShow.gt-md","fxShow.gt-lg","fxHide","fxHide.print","fxHide.xs","fxHide.sm","fxHide.md","fxHide.lg","fxHide.xl","fxHide.lt-sm","fxHide.lt-md","fxHide.lt-lg","fxHide.lt-xl","fxHide.gt-xs","fxHide.gt-sm","fxHide.gt-md","fxHide.gt-lg"]},{type:w.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{type:E.DefaultLayoutAlignDirective,selector:" [fxLayoutAlign], [fxLayoutAlign.xs], [fxLayoutAlign.sm], [fxLayoutAlign.md], [fxLayoutAlign.lg], [fxLayoutAlign.xl], [fxLayoutAlign.lt-sm], [fxLayoutAlign.lt-md], [fxLayoutAlign.lt-lg], [fxLayoutAlign.lt-xl], [fxLayoutAlign.gt-xs], [fxLayoutAlign.gt-sm], [fxLayoutAlign.gt-md], [fxLayoutAlign.gt-lg]",inputs:["fxLayoutAlign","fxLayoutAlign.xs","fxLayoutAlign.sm","fxLayoutAlign.md","fxLayoutAlign.lg","fxLayoutAlign.xl","fxLayoutAlign.lt-sm","fxLayoutAlign.lt-md","fxLayoutAlign.lt-lg","fxLayoutAlign.lt-xl","fxLayoutAlign.gt-xs","fxLayoutAlign.gt-sm","fxLayoutAlign.gt-md","fxLayoutAlign.gt-lg"]},{type:q.FormArrayName,selector:"[formArrayName]",inputs:["formArrayName"]},{type:D.MatLabel,selector:"mat-label"},{type:R.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["id","disabled","required","type","value","readonly","placeholder","errorStateMatcher","aria-describedby"],exportAs:["matInput"]},{type:q.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{type:q.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{type:q.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{type:q.FormControlDirective,selector:"[formControl]",inputs:["disabled","formControl","ngModel"],outputs:["ngModelChange"],exportAs:["ngForm"]},{type:w.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{type:D.MatError,selector:"mat-error",inputs:["id"]},{type:se.MatTooltip,selector:"[matTooltip]",exportAs:["matTooltip"]}],pipes:{translate:P.TranslatePipe,async:w.AsyncPipe,safeHtml:Te}}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:nt,decorators:[{type:o,args:[{selector:"tb-kv-map-config",templateUrl:"./kv-map-config.component.html",styleUrls:["./kv-map-config.component.scss"],providers:[{provide:S,useExisting:n((()=>nt)),multi:!0},{provide:A,useExisting:n((()=>nt)),multi:!0}]}]}],ctorParameters:function(){return[{type:T.Store},{type:P.TranslateService},{type:t.Injector},{type:q.FormBuilder}]},propDecorators:{disabled:[{type:l}],requiredText:[{type:l}],keyText:[{type:l}],keyRequiredText:[{type:l}],valText:[{type:l}],valRequiredText:[{type:l}],hintText:[{type:l}],required:[{type:l}]}});class lt extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.ackValues=["all","-1","0","1"],this.ToByteStandartCharsetTypesValues=$e,this.ToByteStandartCharsetTypeTranslationMap=We}configForm(){return this.kafkaConfigForm}onConfigurationSet(e){this.kafkaConfigForm=this.fb.group({topicPattern:[e?e.topicPattern:null,[k.required]],bootstrapServers:[e?e.bootstrapServers:null,[k.required]],retries:[e?e.retries:null,[k.min(0)]],batchSize:[e?e.batchSize:null,[k.min(0)]],linger:[e?e.linger:null,[k.min(0)]],bufferMemory:[e?e.bufferMemory:null,[k.min(0)]],acks:[e?e.acks:null,[k.required]],keySerializer:[e?e.keySerializer:null,[k.required]],valueSerializer:[e?e.valueSerializer:null,[k.required]],otherProperties:[e?e.otherProperties:null,[]],addMetadataKeyValuesAsKafkaHeaders:[!!e&&e.addMetadataKeyValuesAsKafkaHeaders,[]],kafkaHeadersCharset:[e?e.kafkaHeadersCharset:null,[]]})}validatorTriggers(){return["addMetadataKeyValuesAsKafkaHeaders"]}updateValidators(e){this.kafkaConfigForm.get("addMetadataKeyValuesAsKafkaHeaders").value?this.kafkaConfigForm.get("kafkaHeadersCharset").setValidators([k.required]):this.kafkaConfigForm.get("kafkaHeadersCharset").setValidators([]),this.kafkaConfigForm.get("kafkaHeadersCharset").updateValueAndValidity({emitEvent:e})}}e("KafkaConfigComponent",lt),lt.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:lt,deps:[{token:T.Store},{token:q.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),lt.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"12.0.0",version:"12.2.15",type:lt,selector:"tb-action-node-kafka-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.topic-pattern\n \n \n {{ \'tb.rulenode.topic-pattern-required\' | translate }}\n \n \n \n \n tb.rulenode.bootstrap-servers\n \n \n {{ \'tb.rulenode.bootstrap-servers-required\' | translate }}\n \n \n \n tb.rulenode.retries\n \n \n {{ \'tb.rulenode.min-retries-message\' | translate }}\n \n \n \n tb.rulenode.batch-size-bytes\n \n \n {{ \'tb.rulenode.min-batch-size-bytes-message\' | translate }}\n \n \n \n tb.rulenode.linger-ms\n \n \n {{ \'tb.rulenode.min-linger-ms-message\' | translate }}\n \n \n \n tb.rulenode.buffer-memory-bytes\n \n \n {{ \'tb.rulenode.min-buffer-memory-bytes-message\' | translate }}\n \n \n \n tb.rulenode.acks\n \n \n {{ ackValue }}\n \n \n \n \n tb.rulenode.key-serializer\n \n \n {{ \'tb.rulenode.key-serializer-required\' | translate }}\n \n \n \n tb.rulenode.value-serializer\n \n \n {{ \'tb.rulenode.value-serializer-required\' | translate }}\n \n \n \n \n \n \n {{ \'tb.rulenode.add-metadata-key-values-as-kafka-headers\' | translate }}\n \n
tb.rulenode.add-metadata-key-values-as-kafka-headers-hint
\n \n tb.rulenode.charset-encoding\n \n \n {{ ToByteStandartCharsetTypeTranslationMap.get(charset) | translate }}\n \n \n \n
\n',components:[{type:D.MatFormField,selector:"mat-form-field",inputs:["color","floatLabel","appearance","hideRequiredMarker","hintLabel"],exportAs:["matFormField"]},{type:U.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex"],exportAs:["matSelect"]},{type:K.MatOption,selector:"mat-option",exportAs:["matOption"]},{type:nt,selector:"tb-kv-map-config",inputs:["disabled","requiredText","keyText","keyRequiredText","valText","valRequiredText","hintText","required"]},{type:V.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex","aria-label","aria-labelledby","id","labelPosition","name","required","checked","disabled","indeterminate","aria-describedby","value"],outputs:["change","indeterminateChange"],exportAs:["matCheckbox"]}],directives:[{type:E.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{type:q.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{type:q.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{type:D.MatLabel,selector:"mat-label"},{type:P.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{type:R.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["id","disabled","required","type","value","readonly","placeholder","errorStateMatcher","aria-describedby"],exportAs:["matInput"]},{type:q.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{type:q.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{type:q.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{type:q.FormControlName,selector:"[formControlName]",inputs:["disabled","formControlName","ngModel"],outputs:["ngModelChange"]},{type:w.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{type:D.MatError,selector:"mat-error",inputs:["id"]},{type:D.MatHint,selector:"mat-hint",inputs:["align","id"]},{type:q.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{type:q.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{type:w.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{type:E.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]}],pipes:{translate:P.TranslatePipe,safeHtml:Te}}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:lt,decorators:[{type:o,args:[{selector:"tb-action-node-kafka-config",templateUrl:"./kafka-config.component.html",styleUrls:[]}]}],ctorParameters:function(){return[{type:T.Store},{type:q.FormBuilder}]}});class it extends s{constructor(e,t,o,r){super(e),this.store=e,this.fb=t,this.nodeScriptTestService=o,this.translate=r}configForm(){return this.logConfigForm}onConfigurationSet(e){this.logConfigForm=this.fb.group({jsScript:[e?e.jsScript:null,[k.required]]})}testScript(){const e=this.logConfigForm.get("jsScript").value;this.nodeScriptTestService.testNodeScript(e,"string",this.translate.instant("tb.rulenode.to-string"),"ToString",["msg","metadata","msgType"],this.ruleNodeId,"rulenode/log_node_script_fn").subscribe((e=>{e&&this.logConfigForm.get("jsScript").setValue(e)}))}onValidate(){this.jsFuncComponent.validateOnSubmit()}}e("LogConfigComponent",it),it.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:it,deps:[{token:T.Store},{token:q.FormBuilder},{token:Q.NodeScriptTestService},{token:P.TranslateService}],target:t.ɵɵFactoryTarget.Component}),it.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"12.0.0",version:"12.2.15",type:it,selector:"tb-action-node-log-config",viewQueries:[{propertyName:"jsFuncComponent",first:!0,predicate:["jsFuncComponent"],descendants:!0,static:!0}],usesInheritance:!0,ngImport:t,template:'
\n \n \n \n
\n \n
\n
\n',components:[{type:Y.JsFuncComponent,selector:"tb-js-func",inputs:["functionTitle","functionName","functionArgs","validationArgs","resultType","disabled","fillHeight","minHeight","editorCompleter","globalVariables","disableUndefinedCheck","helpId","noValidate","required"]},{type:J.MatButton,selector:"button[mat-button], button[mat-raised-button], button[mat-icon-button], button[mat-fab], button[mat-mini-fab], button[mat-stroked-button], button[mat-flat-button]",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]}],directives:[{type:E.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{type:q.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{type:q.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{type:P.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{type:q.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{type:q.FormControlName,selector:"[formControlName]",inputs:["disabled","formControlName","ngModel"],outputs:["ngModelChange"]}],pipes:{translate:P.TranslatePipe}}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:it,decorators:[{type:o,args:[{selector:"tb-action-node-log-config",templateUrl:"./log-config.component.html",styleUrls:[]}]}],ctorParameters:function(){return[{type:T.Store},{type:q.FormBuilder},{type:Q.NodeScriptTestService},{type:P.TranslateService}]},propDecorators:{jsFuncComponent:[{type:a,args:["jsFuncComponent",{static:!0}]}]}});class st extends y{constructor(e,t){super(e),this.store=e,this.fb=t,this.subscriptions=[],this.disableCertPemCredentials=!1,this.passwordFieldRquired=!0,this.allCredentialsTypes=Be,this.credentialsTypeTranslationsMap=je,this.propagateChange=null}get required(){return this.requiredValue}set required(e){this.requiredValue=le(e)}ngOnInit(){this.credentialsConfigFormGroup=this.fb.group({type:[null,[k.required]],username:[null,[]],password:[null,[]],caCert:[null,[]],caCertFileName:[null,[]],privateKey:[null,[]],privateKeyFileName:[null,[]],cert:[null,[]],certFileName:[null,[]]}),this.subscriptions.push(this.credentialsConfigFormGroup.valueChanges.pipe(me()).subscribe((()=>{this.updateView()}))),this.subscriptions.push(this.credentialsConfigFormGroup.get("type").valueChanges.subscribe((()=>{this.credentialsTypeChanged()})))}ngOnChanges(e){for(const t of Object.keys(e)){const o=e[t];if(!o.firstChange&&o.currentValue!==o.previousValue&&o.currentValue&&"disableCertPemCredentials"===t){"cert.PEM"===this.credentialsConfigFormGroup.get("type").value&&setTimeout((()=>{this.credentialsConfigFormGroup.get("type").patchValue("anonymous",{emitEvent:!0})}))}}}ngOnDestroy(){this.subscriptions.forEach((e=>e.unsubscribe()))}writeValue(e){$(e)&&(this.credentialsConfigFormGroup.reset(e,{emitEvent:!1}),this.updateValidators(!1))}setDisabledState(e){e?this.credentialsConfigFormGroup.disable():(this.credentialsConfigFormGroup.enable(),this.updateValidators())}updateView(){let e=this.credentialsConfigFormGroup.value;const t=e.type;switch(t){case"anonymous":e={type:t};break;case"basic":e={type:t,username:e.username,password:e.password};break;case"cert.PEM":delete e.username}this.propagateChange(e)}registerOnChange(e){this.propagateChange=e}registerOnTouched(e){}validate(e){return this.credentialsConfigFormGroup.valid?null:{credentialsConfig:{valid:!1}}}credentialsTypeChanged(){this.credentialsConfigFormGroup.patchValue({username:null,password:null,caCert:null,caCertFileName:null,privateKey:null,privateKeyFileName:null,cert:null,certFileName:null}),this.updateValidators()}updateValidators(e=!1){const t=this.credentialsConfigFormGroup.get("type").value;switch(e&&this.credentialsConfigFormGroup.reset({type:t},{emitEvent:!1}),this.credentialsConfigFormGroup.setValidators([]),this.credentialsConfigFormGroup.get("username").setValidators([]),this.credentialsConfigFormGroup.get("password").setValidators([]),t){case"anonymous":break;case"basic":this.credentialsConfigFormGroup.get("username").setValidators([k.required]),this.credentialsConfigFormGroup.get("password").setValidators(this.passwordFieldRquired?[k.required]:[]);break;case"cert.PEM":this.credentialsConfigFormGroup.setValidators([this.requiredFilesSelected(k.required,[["caCert","caCertFileName"],["privateKey","privateKeyFileName","cert","certFileName"]])])}this.credentialsConfigFormGroup.get("username").updateValueAndValidity({emitEvent:e}),this.credentialsConfigFormGroup.get("password").updateValueAndValidity({emitEvent:e}),this.credentialsConfigFormGroup.updateValueAndValidity({emitEvent:e})}requiredFilesSelected(e,t=null){return o=>{t||(t=[Object.keys(o.controls)]);return(null==o?void 0:o.controls)&&t.some((t=>t.every((t=>!e(o.controls[t])))))?null:{notAllRequiredFilesSelected:!0}}}}e("CredentialsConfigComponent",st),st.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:st,deps:[{token:T.Store},{token:q.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),st.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"12.0.0",version:"12.2.15",type:st,selector:"tb-credentials-config",inputs:{required:"required",disableCertPemCredentials:"disableCertPemCredentials",passwordFieldRquired:"passwordFieldRquired"},providers:[{provide:S,useExisting:n((()=>st)),multi:!0},{provide:A,useExisting:n((()=>st)),multi:!0}],usesInheritance:!0,usesOnChanges:!0,ngImport:t,template:'
\n \n \n tb.rulenode.credentials\n \n {{ credentialsTypeTranslationsMap.get(credentialsConfigFormGroup.get(\'type\').value) | translate }}\n \n \n \n \n tb.rulenode.credentials-type\n \n \n {{ credentialsTypeTranslationsMap.get(credentialsType) | translate }}\n \n \n \n {{ \'tb.rulenode.credentials-type-required\' | translate }}\n \n \n
\n \n \n \n \n tb.rulenode.username\n \n \n {{ \'tb.rulenode.username-required\' | translate }}\n \n \n \n tb.rulenode.password\n \n \n \n {{ \'tb.rulenode.password-required\' | translate }}\n \n \n \n \n
{{ \'tb.rulenode.credentials-pem-hint\' | translate }}
\n \n \n \n \n \n \n \n tb.rulenode.private-key-password\n \n \n \n
\n
\n
\n
\n
\n',components:[{type:B.MatExpansionPanel,selector:"mat-expansion-panel",inputs:["disabled","expanded","hideToggle","togglePosition"],outputs:["opened","closed","expandedChange","afterExpand","afterCollapse"],exportAs:["matExpansionPanel"]},{type:B.MatExpansionPanelHeader,selector:"mat-expansion-panel-header",inputs:["tabIndex","expandedHeight","collapsedHeight"]},{type:D.MatFormField,selector:"mat-form-field",inputs:["color","floatLabel","appearance","hideRequiredMarker","hintLabel"],exportAs:["matFormField"]},{type:U.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex"],exportAs:["matSelect"]},{type:K.MatOption,selector:"mat-option",exportAs:["matOption"]},{type:j.TogglePasswordComponent,selector:"tb-toggle-password"},{type:z.FileInputComponent,selector:"tb-file-input",inputs:["label","accept","noFileText","inputId","allowedExtensions","dropLabel","contentConvertFunction","required","requiredAsError","disabled","existingFileName","readAsBinary","workFromFileObj","multipleFile"],outputs:["fileNameChanged"]}],directives:[{type:E.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{type:q.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{type:q.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{type:B.MatExpansionPanelTitle,selector:"mat-panel-title"},{type:P.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{type:B.MatExpansionPanelDescription,selector:"mat-panel-description"},{type:B.MatExpansionPanelContent,selector:"ng-template[matExpansionPanelContent]"},{type:D.MatLabel,selector:"mat-label"},{type:q.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{type:q.FormControlName,selector:"[formControlName]",inputs:["disabled","formControlName","ngModel"],outputs:["ngModelChange"]},{type:q.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{type:w.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{type:w.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{type:D.MatError,selector:"mat-error",inputs:["id"]},{type:w.NgSwitch,selector:"[ngSwitch]",inputs:["ngSwitch"]},{type:w.NgSwitchCase,selector:"[ngSwitchCase]",inputs:["ngSwitchCase"]},{type:R.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["id","disabled","required","type","value","readonly","placeholder","errorStateMatcher","aria-describedby"],exportAs:["matInput"]},{type:q.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{type:D.MatSuffix,selector:"[matSuffix]"}],pipes:{translate:P.TranslatePipe}}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:st,decorators:[{type:o,args:[{selector:"tb-credentials-config",templateUrl:"./credentials-config.component.html",styleUrls:[],providers:[{provide:S,useExisting:n((()=>st)),multi:!0},{provide:A,useExisting:n((()=>st)),multi:!0}]}]}],ctorParameters:function(){return[{type:T.Store},{type:q.FormBuilder}]},propDecorators:{required:[{type:l}],disableCertPemCredentials:[{type:l}],passwordFieldRquired:[{type:l}]}});class mt extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.subscriptions=[]}configForm(){return this.mqttConfigForm}onConfigurationSet(e){this.mqttConfigForm=this.fb.group({topicPattern:[e?e.topicPattern:null,[k.required]],host:[e?e.host:null,[k.required]],port:[e?e.port:null,[k.required,k.min(1),k.max(65535)]],connectTimeoutSec:[e?e.connectTimeoutSec:null,[k.required,k.min(1),k.max(200)]],clientId:[e?e.clientId:null,[]],appendClientIdSuffix:[{value:!!e&&e.appendClientIdSuffix,disabled:!(e&&W(e.clientId))},[]],cleanSession:[!!e&&e.cleanSession,[]],ssl:[!!e&&e.ssl,[]],credentials:[e?e.credentials:null,[]]}),this.subscriptions.push(this.mqttConfigForm.get("clientId").valueChanges.subscribe((e=>{W(e)?this.mqttConfigForm.get("appendClientIdSuffix").enable({emitEvent:!1}):this.mqttConfigForm.get("appendClientIdSuffix").disable({emitEvent:!1})})))}ngOnDestroy(){this.subscriptions.forEach((e=>e.unsubscribe()))}}e("MqttConfigComponent",mt),mt.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:mt,deps:[{token:T.Store},{token:q.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),mt.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"12.0.0",version:"12.2.15",type:mt,selector:"tb-action-node-mqtt-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.topic-pattern\n \n \n {{ \'tb.rulenode.topic-pattern-required\' | translate }}\n \n \n \n
\n \n tb.rulenode.host\n \n \n {{ \'tb.rulenode.host-required\' | translate }}\n \n \n \n tb.rulenode.port\n \n \n {{ \'tb.rulenode.port-required\' | translate }}\n \n \n {{ \'tb.rulenode.port-range\' | translate }}\n \n \n {{ \'tb.rulenode.port-range\' | translate }}\n \n \n \n tb.rulenode.connect-timeout\n \n \n {{ \'tb.rulenode.connect-timeout-required\' | translate }}\n \n \n {{ \'tb.rulenode.connect-timeout-range\' | translate }}\n \n \n {{ \'tb.rulenode.connect-timeout-range\' | translate }}\n \n \n
\n \n tb.rulenode.client-id\n \n \n
tb.rulenode.client-id-hint
\n \n {{ \'tb.rulenode.append-client-id-suffix\' | translate }}\n \n
{{ "tb.rulenode.client-id-suffix-hint" | translate }}
\n \n {{ \'tb.rulenode.clean-session\' | translate }}\n \n \n {{ \'tb.rulenode.enable-ssl\' | translate }}\n \n \n
\n',styles:[":host .tb-mqtt-credentials-panel-group{margin:0 6px}:host .tb-hint.client-id{margin-top:-1.25em;max-width:-moz-fit-content;max-width:fit-content}:host mat-checkbox{padding-bottom:16px}\n"],components:[{type:D.MatFormField,selector:"mat-form-field",inputs:["color","floatLabel","appearance","hideRequiredMarker","hintLabel"],exportAs:["matFormField"]},{type:V.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex","aria-label","aria-labelledby","id","labelPosition","name","required","checked","disabled","indeterminate","aria-describedby","value"],outputs:["change","indeterminateChange"],exportAs:["matCheckbox"]},{type:st,selector:"tb-credentials-config",inputs:["required","disableCertPemCredentials","passwordFieldRquired"]}],directives:[{type:E.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{type:q.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{type:q.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{type:D.MatLabel,selector:"mat-label"},{type:P.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{type:R.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["id","disabled","required","type","value","readonly","placeholder","errorStateMatcher","aria-describedby"],exportAs:["matInput"]},{type:q.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{type:q.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{type:q.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{type:q.FormControlName,selector:"[formControlName]",inputs:["disabled","formControlName","ngModel"],outputs:["ngModelChange"]},{type:w.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{type:D.MatError,selector:"mat-error",inputs:["id"]},{type:D.MatHint,selector:"mat-hint",inputs:["align","id"]},{type:E.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{type:E.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{type:q.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{type:q.MaxValidator,selector:"input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]",inputs:["max"]},{type:q.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"}],pipes:{translate:P.TranslatePipe,safeHtml:Te}}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:mt,decorators:[{type:o,args:[{selector:"tb-action-node-mqtt-config",templateUrl:"./mqtt-config.component.html",styleUrls:["./mqtt-config.component.scss"]}]}],ctorParameters:function(){return[{type:T.Store},{type:q.FormBuilder}]}});class ut extends s{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.msgCountConfigForm}onConfigurationSet(e){this.msgCountConfigForm=this.fb.group({interval:[e?e.interval:null,[k.required,k.min(1)]],telemetryPrefix:[e?e.telemetryPrefix:null,[k.required]]})}}e("MsgCountConfigComponent",ut),ut.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:ut,deps:[{token:T.Store},{token:q.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),ut.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"12.0.0",version:"12.2.15",type:ut,selector:"tb-action-node-msg-count-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.interval-seconds\n \n \n {{ \'tb.rulenode.interval-seconds-required\' | translate }}\n \n \n {{ \'tb.rulenode.min-interval-seconds-message\' | translate }}\n \n \n \n tb.rulenode.output-timeseries-key-prefix\n \n \n {{ \'tb.rulenode.output-timeseries-key-prefix-required\' | translate }}\n \n \n
\n',components:[{type:D.MatFormField,selector:"mat-form-field",inputs:["color","floatLabel","appearance","hideRequiredMarker","hintLabel"],exportAs:["matFormField"]}],directives:[{type:E.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{type:q.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{type:q.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{type:D.MatLabel,selector:"mat-label"},{type:P.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{type:q.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{type:q.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{type:R.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["id","disabled","required","type","value","readonly","placeholder","errorStateMatcher","aria-describedby"],exportAs:["matInput"]},{type:q.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{type:q.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{type:q.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{type:q.FormControlName,selector:"[formControlName]",inputs:["disabled","formControlName","ngModel"],outputs:["ngModelChange"]},{type:w.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{type:D.MatError,selector:"mat-error",inputs:["id"]}],pipes:{translate:P.TranslatePipe}}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:ut,decorators:[{type:o,args:[{selector:"tb-action-node-msg-count-config",templateUrl:"./msg-count-config.component.html",styleUrls:[]}]}],ctorParameters:function(){return[{type:T.Store},{type:q.FormBuilder}]}});class pt extends s{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.msgDelayConfigForm}onConfigurationSet(e){this.msgDelayConfigForm=this.fb.group({useMetadataPeriodInSecondsPatterns:[!!e&&e.useMetadataPeriodInSecondsPatterns,[]],periodInSeconds:[e?e.periodInSeconds:null,[]],periodInSecondsPattern:[e?e.periodInSecondsPattern:null,[]],maxPendingMsgs:[e?e.maxPendingMsgs:null,[k.required,k.min(1),k.max(1e5)]]})}validatorTriggers(){return["useMetadataPeriodInSecondsPatterns"]}updateValidators(e){this.msgDelayConfigForm.get("useMetadataPeriodInSecondsPatterns").value?(this.msgDelayConfigForm.get("periodInSecondsPattern").setValidators([k.required]),this.msgDelayConfigForm.get("periodInSeconds").setValidators([])):(this.msgDelayConfigForm.get("periodInSecondsPattern").setValidators([]),this.msgDelayConfigForm.get("periodInSeconds").setValidators([k.required,k.min(0)])),this.msgDelayConfigForm.get("periodInSecondsPattern").updateValueAndValidity({emitEvent:e}),this.msgDelayConfigForm.get("periodInSeconds").updateValueAndValidity({emitEvent:e})}}e("MsgDelayConfigComponent",pt),pt.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:pt,deps:[{token:T.Store},{token:q.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),pt.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"12.0.0",version:"12.2.15",type:pt,selector:"tb-action-node-msg-delay-config",usesInheritance:!0,ngImport:t,template:'
\n \n {{ \'tb.rulenode.use-metadata-period-in-seconds-patterns\' | translate }}\n \n
tb.rulenode.use-metadata-period-in-seconds-patterns-hint
\n \n tb.rulenode.period-seconds\n \n \n {{ \'tb.rulenode.period-seconds-required\' | translate }}\n \n \n {{ \'tb.rulenode.min-period-0-seconds-message\' | translate }}\n \n \n \n \n tb.rulenode.period-in-seconds-pattern\n \n \n {{ \'tb.rulenode.period-in-seconds-pattern-required\' | translate }}\n \n \n \n \n \n tb.rulenode.max-pending-messages\n \n \n {{ \'tb.rulenode.max-pending-messages-required\' | translate }}\n \n \n {{ \'tb.rulenode.max-pending-messages-range\' | translate }}\n \n \n {{ \'tb.rulenode.max-pending-messages-range\' | translate }}\n \n \n
\n',components:[{type:V.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex","aria-label","aria-labelledby","id","labelPosition","name","required","checked","disabled","indeterminate","aria-describedby","value"],outputs:["change","indeterminateChange"],exportAs:["matCheckbox"]},{type:D.MatFormField,selector:"mat-form-field",inputs:["color","floatLabel","appearance","hideRequiredMarker","hintLabel"],exportAs:["matFormField"]}],directives:[{type:E.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{type:q.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{type:q.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{type:q.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{type:q.FormControlName,selector:"[formControlName]",inputs:["disabled","formControlName","ngModel"],outputs:["ngModelChange"]},{type:P.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{type:w.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{type:D.MatLabel,selector:"mat-label"},{type:q.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{type:q.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{type:R.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["id","disabled","required","type","value","readonly","placeholder","errorStateMatcher","aria-describedby"],exportAs:["matInput"]},{type:q.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{type:q.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{type:D.MatError,selector:"mat-error",inputs:["id"]},{type:D.MatHint,selector:"mat-hint",inputs:["align","id"]},{type:q.MaxValidator,selector:"input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]",inputs:["max"]}],pipes:{translate:P.TranslatePipe,safeHtml:Te}}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:pt,decorators:[{type:o,args:[{selector:"tb-action-node-msg-delay-config",templateUrl:"./msg-delay-config.component.html",styleUrls:[]}]}],ctorParameters:function(){return[{type:T.Store},{type:q.FormBuilder}]}});class dt extends s{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.pubSubConfigForm}onConfigurationSet(e){this.pubSubConfigForm=this.fb.group({projectId:[e?e.projectId:null,[k.required]],topicName:[e?e.topicName:null,[k.required]],serviceAccountKey:[e?e.serviceAccountKey:null,[k.required]],serviceAccountKeyFileName:[e?e.serviceAccountKeyFileName:null,[k.required]],messageAttributes:[e?e.messageAttributes:null,[]]})}}e("PubSubConfigComponent",dt),dt.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:dt,deps:[{token:T.Store},{token:q.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),dt.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"12.0.0",version:"12.2.15",type:dt,selector:"tb-action-node-pub-sub-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.gcp-project-id\n \n \n {{ \'tb.rulenode.gcp-project-id-required\' | translate }}\n \n \n \n tb.rulenode.pubsub-topic-name\n \n \n {{ \'tb.rulenode.pubsub-topic-name-required\' | translate }}\n \n \n \n \n \n
\n \n \n
\n',components:[{type:D.MatFormField,selector:"mat-form-field",inputs:["color","floatLabel","appearance","hideRequiredMarker","hintLabel"],exportAs:["matFormField"]},{type:z.FileInputComponent,selector:"tb-file-input",inputs:["label","accept","noFileText","inputId","allowedExtensions","dropLabel","contentConvertFunction","required","requiredAsError","disabled","existingFileName","readAsBinary","workFromFileObj","multipleFile"],outputs:["fileNameChanged"]},{type:nt,selector:"tb-kv-map-config",inputs:["disabled","requiredText","keyText","keyRequiredText","valText","valRequiredText","hintText","required"]}],directives:[{type:E.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{type:q.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{type:q.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{type:D.MatLabel,selector:"mat-label"},{type:P.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{type:R.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["id","disabled","required","type","value","readonly","placeholder","errorStateMatcher","aria-describedby"],exportAs:["matInput"]},{type:q.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{type:q.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{type:q.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{type:q.FormControlName,selector:"[formControlName]",inputs:["disabled","formControlName","ngModel"],outputs:["ngModelChange"]},{type:w.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{type:D.MatError,selector:"mat-error",inputs:["id"]}],pipes:{translate:P.TranslatePipe,safeHtml:Te}}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:dt,decorators:[{type:o,args:[{selector:"tb-action-node-pub-sub-config",templateUrl:"./pubsub-config.component.html",styleUrls:[]}]}],ctorParameters:function(){return[{type:T.Store},{type:q.FormBuilder}]}});class ft extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.attributeScopes=Object.keys(m),this.telemetryTypeTranslationsMap=u}configForm(){return this.pushToCloudConfigForm}onConfigurationSet(e){this.pushToCloudConfigForm=this.fb.group({scope:[e?e.scope:null,[k.required]]})}}e("PushToCloudConfigComponent",ft),ft.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:ft,deps:[{token:T.Store},{token:q.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),ft.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"12.0.0",version:"12.2.15",type:ft,selector:"tb-action-node-push-to-cloud-config",usesInheritance:!0,ngImport:t,template:'
\n \n attribute.attributes-scope\n \n \n {{ telemetryTypeTranslationsMap.get(scope) | translate }}\n \n \n \n
\n',components:[{type:D.MatFormField,selector:"mat-form-field",inputs:["color","floatLabel","appearance","hideRequiredMarker","hintLabel"],exportAs:["matFormField"]},{type:U.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex"],exportAs:["matSelect"]},{type:K.MatOption,selector:"mat-option",exportAs:["matOption"]}],directives:[{type:E.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{type:q.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{type:q.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{type:E.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{type:D.MatLabel,selector:"mat-label"},{type:P.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{type:q.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{type:q.FormControlName,selector:"[formControlName]",inputs:["disabled","formControlName","ngModel"],outputs:["ngModelChange"]},{type:q.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{type:w.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]}],pipes:{translate:P.TranslatePipe}}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:ft,decorators:[{type:o,args:[{selector:"tb-action-node-push-to-cloud-config",templateUrl:"./push-to-cloud-config.component.html",styleUrls:[]}]}],ctorParameters:function(){return[{type:T.Store},{type:q.FormBuilder}]}});class ct extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.attributeScopes=Object.keys(m),this.telemetryTypeTranslationsMap=u}configForm(){return this.pushToEdgeConfigForm}onConfigurationSet(e){this.pushToEdgeConfigForm=this.fb.group({scope:[e?e.scope:null,[k.required]]})}}e("PushToEdgeConfigComponent",ct),ct.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:ct,deps:[{token:T.Store},{token:q.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),ct.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"12.0.0",version:"12.2.15",type:ct,selector:"tb-action-node-push-to-edge-config",usesInheritance:!0,ngImport:t,template:'
\n \n attribute.attributes-scope\n \n \n {{ telemetryTypeTranslationsMap.get(scope) | translate }}\n \n \n \n
\n',components:[{type:D.MatFormField,selector:"mat-form-field",inputs:["color","floatLabel","appearance","hideRequiredMarker","hintLabel"],exportAs:["matFormField"]},{type:U.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex"],exportAs:["matSelect"]},{type:K.MatOption,selector:"mat-option",exportAs:["matOption"]}],directives:[{type:E.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{type:q.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{type:q.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{type:E.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{type:D.MatLabel,selector:"mat-label"},{type:P.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{type:q.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{type:q.FormControlName,selector:"[formControlName]",inputs:["disabled","formControlName","ngModel"],outputs:["ngModelChange"]},{type:q.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{type:w.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]}],pipes:{translate:P.TranslatePipe}}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:ct,decorators:[{type:o,args:[{selector:"tb-action-node-push-to-edge-config",templateUrl:"./push-to-edge-config.component.html",styleUrls:[]}]}],ctorParameters:function(){return[{type:T.Store},{type:q.FormBuilder}]}});class gt extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.messageProperties=[null,"BASIC","TEXT_PLAIN","MINIMAL_BASIC","MINIMAL_PERSISTENT_BASIC","PERSISTENT_BASIC","PERSISTENT_TEXT_PLAIN"]}configForm(){return this.rabbitMqConfigForm}onConfigurationSet(e){this.rabbitMqConfigForm=this.fb.group({exchangeNamePattern:[e?e.exchangeNamePattern:null,[]],routingKeyPattern:[e?e.routingKeyPattern:null,[]],messageProperties:[e?e.messageProperties:null,[]],host:[e?e.host:null,[k.required]],port:[e?e.port:null,[k.required,k.min(1),k.max(65535)]],virtualHost:[e?e.virtualHost:null,[]],username:[e?e.username:null,[]],password:[e?e.password:null,[]],automaticRecoveryEnabled:[!!e&&e.automaticRecoveryEnabled,[]],connectionTimeout:[e?e.connectionTimeout:null,[k.min(0)]],handshakeTimeout:[e?e.handshakeTimeout:null,[k.min(0)]],clientProperties:[e?e.clientProperties:null,[]]})}}e("RabbitMqConfigComponent",gt),gt.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:gt,deps:[{token:T.Store},{token:q.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),gt.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"12.0.0",version:"12.2.15",type:gt,selector:"tb-action-node-rabbit-mq-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.exchange-name-pattern\n \n \n \n tb.rulenode.routing-key-pattern\n \n \n \n tb.rulenode.message-properties\n \n \n {{ property }}\n \n \n \n
\n \n tb.rulenode.host\n \n \n {{ \'tb.rulenode.host-required\' | translate }}\n \n \n \n tb.rulenode.port\n \n \n {{ \'tb.rulenode.port-required\' | translate }}\n \n \n {{ \'tb.rulenode.port-range\' | translate }}\n \n \n {{ \'tb.rulenode.port-range\' | translate }}\n \n \n
\n \n tb.rulenode.virtual-host\n \n \n \n tb.rulenode.username\n \n \n \n tb.rulenode.password\n \n \n \n \n {{ \'tb.rulenode.automatic-recovery\' | translate }}\n \n \n tb.rulenode.connection-timeout-ms\n \n \n {{ \'tb.rulenode.min-connection-timeout-ms-message\' | translate }}\n \n \n \n tb.rulenode.handshake-timeout-ms\n \n \n {{ \'tb.rulenode.min-handshake-timeout-ms-message\' | translate }}\n \n \n \n \n \n
\n',components:[{type:D.MatFormField,selector:"mat-form-field",inputs:["color","floatLabel","appearance","hideRequiredMarker","hintLabel"],exportAs:["matFormField"]},{type:U.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex"],exportAs:["matSelect"]},{type:K.MatOption,selector:"mat-option",exportAs:["matOption"]},{type:j.TogglePasswordComponent,selector:"tb-toggle-password"},{type:V.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex","aria-label","aria-labelledby","id","labelPosition","name","required","checked","disabled","indeterminate","aria-describedby","value"],outputs:["change","indeterminateChange"],exportAs:["matCheckbox"]},{type:nt,selector:"tb-kv-map-config",inputs:["disabled","requiredText","keyText","keyRequiredText","valText","valRequiredText","hintText","required"]}],directives:[{type:E.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{type:q.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{type:q.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{type:D.MatLabel,selector:"mat-label"},{type:P.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{type:R.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["id","disabled","required","type","value","readonly","placeholder","errorStateMatcher","aria-describedby"],exportAs:["matInput"]},{type:q.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{type:q.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{type:q.FormControlName,selector:"[formControlName]",inputs:["disabled","formControlName","ngModel"],outputs:["ngModelChange"]},{type:w.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{type:E.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{type:E.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{type:q.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{type:w.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{type:D.MatError,selector:"mat-error",inputs:["id"]},{type:q.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{type:q.MaxValidator,selector:"input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]",inputs:["max"]},{type:q.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{type:D.MatSuffix,selector:"[matSuffix]"}],pipes:{translate:P.TranslatePipe}}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:gt,decorators:[{type:o,args:[{selector:"tb-action-node-rabbit-mq-config",templateUrl:"./rabbit-mq-config.component.html",styleUrls:[]}]}],ctorParameters:function(){return[{type:T.Store},{type:q.FormBuilder}]}});class xt extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.proxySchemes=["http","https"],this.httpRequestTypes=Object.keys(Qe)}configForm(){return this.restApiCallConfigForm}onConfigurationSet(e){this.restApiCallConfigForm=this.fb.group({restEndpointUrlPattern:[e?e.restEndpointUrlPattern:null,[k.required]],requestMethod:[e?e.requestMethod:null,[k.required]],useSimpleClientHttpFactory:[!!e&&e.useSimpleClientHttpFactory,[]],ignoreRequestBody:[!!e&&e.ignoreRequestBody,[]],enableProxy:[!!e&&e.enableProxy,[]],useSystemProxyProperties:[!!e&&e.enableProxy,[]],proxyScheme:[e?e.proxyHost:null,[]],proxyHost:[e?e.proxyHost:null,[]],proxyPort:[e?e.proxyPort:null,[]],proxyUser:[e?e.proxyUser:null,[]],proxyPassword:[e?e.proxyPassword:null,[]],readTimeoutMs:[e?e.readTimeoutMs:null,[]],maxParallelRequestsCount:[e?e.maxParallelRequestsCount:null,[k.min(0)]],headers:[e?e.headers:null,[]],useRedisQueueForMsgPersistence:[!!e&&e.useRedisQueueForMsgPersistence,[]],trimQueue:[!!e&&e.trimQueue,[]],maxQueueSize:[e?e.maxQueueSize:null,[]],credentials:[e?e.credentials:null,[]]})}validatorTriggers(){return["useSimpleClientHttpFactory","useRedisQueueForMsgPersistence","enableProxy","useSystemProxyProperties"]}updateValidators(e){const t=this.restApiCallConfigForm.get("useSimpleClientHttpFactory").value,o=this.restApiCallConfigForm.get("useRedisQueueForMsgPersistence").value,r=this.restApiCallConfigForm.get("enableProxy").value,a=this.restApiCallConfigForm.get("useSystemProxyProperties").value;r&&!a?(this.restApiCallConfigForm.get("proxyHost").setValidators(r?[k.required]:[]),this.restApiCallConfigForm.get("proxyPort").setValidators(r?[k.required,k.min(1),k.max(65535)]:[])):(this.restApiCallConfigForm.get("proxyHost").setValidators([]),this.restApiCallConfigForm.get("proxyPort").setValidators([]),t?this.restApiCallConfigForm.get("readTimeoutMs").setValidators([]):this.restApiCallConfigForm.get("readTimeoutMs").setValidators([k.min(0)])),o?this.restApiCallConfigForm.get("maxQueueSize").setValidators([k.min(0)]):this.restApiCallConfigForm.get("maxQueueSize").setValidators([]),this.restApiCallConfigForm.get("readTimeoutMs").updateValueAndValidity({emitEvent:e}),this.restApiCallConfigForm.get("maxQueueSize").updateValueAndValidity({emitEvent:e}),this.restApiCallConfigForm.get("proxyHost").updateValueAndValidity({emitEvent:e}),this.restApiCallConfigForm.get("proxyPort").updateValueAndValidity({emitEvent:e}),this.restApiCallConfigForm.get("credentials").updateValueAndValidity({emitEvent:e})}}e("RestApiCallConfigComponent",xt),xt.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:xt,deps:[{token:T.Store},{token:q.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),xt.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"12.0.0",version:"12.2.15",type:xt,selector:"tb-action-node-rest-api-call-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.endpoint-url-pattern\n \n \n {{ \'tb.rulenode.endpoint-url-pattern-required\' | translate }}\n \n \n \n \n tb.rulenode.request-method\n \n \n {{ requestType }}\n \n \n \n \n {{ \'tb.rulenode.enable-proxy\' | translate }}\n \n \n {{ \'tb.rulenode.use-simple-client-http-factory\' | translate }}\n \n \n {{ \'tb.rulenode.ignore-request-body\' | translate }}\n \n
\n \n {{ \'tb.rulenode.use-system-proxy-properties\' | translate }}\n \n
\n
\n \n tb.rulenode.proxy-scheme\n \n \n {{ proxyScheme }}\n \n \n \n \n tb.rulenode.proxy-host\n \n \n {{ \'tb.rulenode.proxy-host-required\' | translate }}\n \n \n \n tb.rulenode.proxy-port\n \n \n {{ \'tb.rulenode.proxy-port-required\' | translate }}\n \n \n {{ \'tb.rulenode.proxy-port-range\' | translate }}\n \n \n
\n \n tb.rulenode.proxy-user\n \n \n \n tb.rulenode.proxy-password\n \n \n
\n
\n \n tb.rulenode.read-timeout\n \n tb.rulenode.read-timeout-hint\n \n \n tb.rulenode.max-parallel-requests-count\n \n tb.rulenode.max-parallel-requests-count-hint\n \n \n
\n \n \n \n {{ \'tb.rulenode.use-redis-queue\' | translate }}\n \n
\n \n {{ \'tb.rulenode.trim-redis-queue\' | translate }}\n \n \n tb.rulenode.redis-queue-max-size\n \n \n
\n \n
\n',components:[{type:D.MatFormField,selector:"mat-form-field",inputs:["color","floatLabel","appearance","hideRequiredMarker","hintLabel"],exportAs:["matFormField"]},{type:U.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex"],exportAs:["matSelect"]},{type:K.MatOption,selector:"mat-option",exportAs:["matOption"]},{type:V.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex","aria-label","aria-labelledby","id","labelPosition","name","required","checked","disabled","indeterminate","aria-describedby","value"],outputs:["change","indeterminateChange"],exportAs:["matCheckbox"]},{type:nt,selector:"tb-kv-map-config",inputs:["disabled","requiredText","keyText","keyRequiredText","valText","valRequiredText","hintText","required"]},{type:st,selector:"tb-credentials-config",inputs:["required","disableCertPemCredentials","passwordFieldRquired"]}],directives:[{type:E.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{type:q.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{type:q.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{type:D.MatLabel,selector:"mat-label"},{type:P.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{type:R.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["id","disabled","required","type","value","readonly","placeholder","errorStateMatcher","aria-describedby"],exportAs:["matInput"]},{type:q.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{type:q.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{type:q.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{type:q.FormControlName,selector:"[formControlName]",inputs:["disabled","formControlName","ngModel"],outputs:["ngModelChange"]},{type:w.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{type:D.MatError,selector:"mat-error",inputs:["id"]},{type:D.MatHint,selector:"mat-hint",inputs:["align","id"]},{type:w.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{type:E.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{type:E.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{type:q.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{type:q.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]}],pipes:{translate:P.TranslatePipe,safeHtml:Te}}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:xt,decorators:[{type:o,args:[{selector:"tb-action-node-rest-api-call-config",templateUrl:"./rest-api-call-config.component.html",styleUrls:[]}]}],ctorParameters:function(){return[{type:T.Store},{type:q.FormBuilder}]}});class yt extends s{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.rpcReplyConfigForm}onConfigurationSet(e){this.rpcReplyConfigForm=this.fb.group({requestIdMetaDataAttribute:[e?e.requestIdMetaDataAttribute:null,[]]})}}e("RpcReplyConfigComponent",yt),yt.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:yt,deps:[{token:T.Store},{token:q.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),yt.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"12.0.0",version:"12.2.15",type:yt,selector:"tb-action-node-rpc-reply-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.request-id-metadata-attribute\n \n \n
\n',components:[{type:D.MatFormField,selector:"mat-form-field",inputs:["color","floatLabel","appearance","hideRequiredMarker","hintLabel"],exportAs:["matFormField"]}],directives:[{type:E.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{type:q.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{type:q.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{type:D.MatLabel,selector:"mat-label"},{type:P.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{type:R.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["id","disabled","required","type","value","readonly","placeholder","errorStateMatcher","aria-describedby"],exportAs:["matInput"]},{type:q.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{type:q.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{type:q.FormControlName,selector:"[formControlName]",inputs:["disabled","formControlName","ngModel"],outputs:["ngModelChange"]}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:yt,decorators:[{type:o,args:[{selector:"tb-action-node-rpc-reply-config",templateUrl:"./rpc-reply-config.component.html",styleUrls:[]}]}],ctorParameters:function(){return[{type:T.Store},{type:q.FormBuilder}]}});class bt extends s{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.rpcRequestConfigForm}onConfigurationSet(e){this.rpcRequestConfigForm=this.fb.group({timeoutInSeconds:[e?e.timeoutInSeconds:null,[k.required,k.min(0)]]})}}e("RpcRequestConfigComponent",bt),bt.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:bt,deps:[{token:T.Store},{token:q.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),bt.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"12.0.0",version:"12.2.15",type:bt,selector:"tb-action-node-rpc-request-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.timeout-sec\n \n \n {{ \'tb.rulenode.timeout-required\' | translate }}\n \n \n {{ \'tb.rulenode.min-timeout-message\' | translate }}\n \n \n
\n',components:[{type:D.MatFormField,selector:"mat-form-field",inputs:["color","floatLabel","appearance","hideRequiredMarker","hintLabel"],exportAs:["matFormField"]}],directives:[{type:E.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{type:q.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{type:q.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{type:E.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{type:D.MatLabel,selector:"mat-label"},{type:P.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{type:q.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{type:q.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{type:R.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["id","disabled","required","type","value","readonly","placeholder","errorStateMatcher","aria-describedby"],exportAs:["matInput"]},{type:q.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{type:q.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{type:q.FormControlName,selector:"[formControlName]",inputs:["disabled","formControlName","ngModel"],outputs:["ngModelChange"]},{type:q.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{type:w.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{type:D.MatError,selector:"mat-error",inputs:["id"]}],pipes:{translate:P.TranslatePipe}}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:bt,decorators:[{type:o,args:[{selector:"tb-action-node-rpc-request-config",templateUrl:"./rpc-request-config.component.html",styleUrls:[]}]}],ctorParameters:function(){return[{type:T.Store},{type:q.FormBuilder}]}});class ht extends s{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.saveToCustomTableConfigForm}onConfigurationSet(e){this.saveToCustomTableConfigForm=this.fb.group({tableName:[e?e.tableName:null,[k.required,k.pattern(/.*\S.*/)]],fieldsMapping:[e?e.fieldsMapping:null,[k.required]]})}prepareOutputConfig(e){return e.tableName=e.tableName.trim(),e}}e("SaveToCustomTableConfigComponent",ht),ht.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:ht,deps:[{token:T.Store},{token:q.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),ht.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"12.0.0",version:"12.2.15",type:ht,selector:"tb-action-node-custom-table-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.custom-table-name\n \n \n {{ \'tb.rulenode.custom-table-name-required\' | translate }}\n \n tb.rulenode.custom-table-hint\n \n \n \n \n
\n',components:[{type:D.MatFormField,selector:"mat-form-field",inputs:["color","floatLabel","appearance","hideRequiredMarker","hintLabel"],exportAs:["matFormField"]},{type:nt,selector:"tb-kv-map-config",inputs:["disabled","requiredText","keyText","keyRequiredText","valText","valRequiredText","hintText","required"]}],directives:[{type:E.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{type:q.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{type:q.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{type:D.MatLabel,selector:"mat-label"},{type:P.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{type:R.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["id","disabled","required","type","value","readonly","placeholder","errorStateMatcher","aria-describedby"],exportAs:["matInput"]},{type:q.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{type:q.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{type:q.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{type:q.FormControlName,selector:"[formControlName]",inputs:["disabled","formControlName","ngModel"],outputs:["ngModelChange"]},{type:w.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{type:D.MatError,selector:"mat-error",inputs:["id"]},{type:D.MatHint,selector:"mat-hint",inputs:["align","id"]}],pipes:{translate:P.TranslatePipe}}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:ht,decorators:[{type:o,args:[{selector:"tb-action-node-custom-table-config",templateUrl:"./save-to-custom-table-config.component.html",styleUrls:[]}]}],ctorParameters:function(){return[{type:T.Store},{type:q.FormBuilder}]}});class Ct extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.smtpProtocols=["smtp","smtps"],this.tlsVersions=["TLSv1","TLSv1.1","TLSv1.2","TLSv1.3"]}configForm(){return this.sendEmailConfigForm}onConfigurationSet(e){this.sendEmailConfigForm=this.fb.group({useSystemSmtpSettings:[!!e&&e.useSystemSmtpSettings,[]],smtpProtocol:[e?e.smtpProtocol:null,[]],smtpHost:[e?e.smtpHost:null,[]],smtpPort:[e?e.smtpPort:null,[]],timeout:[e?e.timeout:null,[]],enableTls:[!!e&&e.enableTls,[]],tlsVersion:[e?e.tlsVersion:null,[]],enableProxy:[!!e&&e.enableProxy,[]],proxyHost:[e?e.proxyHost:null,[]],proxyPort:[e?e.proxyPort:null,[]],proxyUser:[e?e.proxyUser:null,[]],proxyPassword:[e?e.proxyPassword:null,[]],username:[e?e.username:null,[]],password:[e?e.password:null,[]]})}validatorTriggers(){return["useSystemSmtpSettings","enableProxy"]}updateValidators(e){const t=this.sendEmailConfigForm.get("useSystemSmtpSettings").value,o=this.sendEmailConfigForm.get("enableProxy").value;t?(this.sendEmailConfigForm.get("smtpProtocol").setValidators([]),this.sendEmailConfigForm.get("smtpHost").setValidators([]),this.sendEmailConfigForm.get("smtpPort").setValidators([]),this.sendEmailConfigForm.get("timeout").setValidators([]),this.sendEmailConfigForm.get("proxyHost").setValidators([]),this.sendEmailConfigForm.get("proxyPort").setValidators([])):(this.sendEmailConfigForm.get("smtpProtocol").setValidators([k.required]),this.sendEmailConfigForm.get("smtpHost").setValidators([k.required]),this.sendEmailConfigForm.get("smtpPort").setValidators([k.required,k.min(1),k.max(65535)]),this.sendEmailConfigForm.get("timeout").setValidators([k.required,k.min(0)]),this.sendEmailConfigForm.get("proxyHost").setValidators(o?[k.required]:[]),this.sendEmailConfigForm.get("proxyPort").setValidators(o?[k.required,k.min(1),k.max(65535)]:[])),this.sendEmailConfigForm.get("smtpProtocol").updateValueAndValidity({emitEvent:e}),this.sendEmailConfigForm.get("smtpHost").updateValueAndValidity({emitEvent:e}),this.sendEmailConfigForm.get("smtpPort").updateValueAndValidity({emitEvent:e}),this.sendEmailConfigForm.get("timeout").updateValueAndValidity({emitEvent:e}),this.sendEmailConfigForm.get("proxyHost").updateValueAndValidity({emitEvent:e}),this.sendEmailConfigForm.get("proxyPort").updateValueAndValidity({emitEvent:e})}}e("SendEmailConfigComponent",Ct),Ct.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Ct,deps:[{token:T.Store},{token:q.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),Ct.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"12.0.0",version:"12.2.15",type:Ct,selector:"tb-action-node-send-email-config",usesInheritance:!0,ngImport:t,template:'
\n \n {{ \'tb.rulenode.use-system-smtp-settings\' | translate }}\n \n
\n \n tb.rulenode.smtp-protocol\n \n \n {{ smtpProtocol.toUpperCase() }}\n \n \n \n
\n \n tb.rulenode.smtp-host\n \n \n {{ \'tb.rulenode.smtp-host-required\' | translate }}\n \n \n \n tb.rulenode.smtp-port\n \n \n {{ \'tb.rulenode.smtp-port-required\' | translate }}\n \n \n {{ \'tb.rulenode.smtp-port-range\' | translate }}\n \n \n {{ \'tb.rulenode.smtp-port-range\' | translate }}\n \n \n
\n \n tb.rulenode.timeout-msec\n \n \n {{ \'tb.rulenode.timeout-required\' | translate }}\n \n \n {{ \'tb.rulenode.min-timeout-msec-message\' | translate }}\n \n \n \n {{ \'tb.rulenode.enable-tls\' | translate }}\n \n \n tb.rulenode.tls-version\n \n \n {{ tlsVersion }}\n \n \n \n \n {{ \'tb.rulenode.enable-proxy\' | translate }}\n \n
\n
\n \n tb.rulenode.proxy-host\n \n \n {{ \'tb.rulenode.proxy-host-required\' | translate }}\n \n \n \n tb.rulenode.proxy-port\n \n \n {{ \'tb.rulenode.proxy-port-required\' | translate }}\n \n \n {{ \'tb.rulenode.proxy-port-range\' | translate }}\n \n \n
\n \n tb.rulenode.proxy-user\n \n \n \n tb.rulenode.proxy-password\n \n \n
\n \n tb.rulenode.username\n \n \n \n tb.rulenode.password\n \n \n \n
\n
\n',components:[{type:V.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex","aria-label","aria-labelledby","id","labelPosition","name","required","checked","disabled","indeterminate","aria-describedby","value"],outputs:["change","indeterminateChange"],exportAs:["matCheckbox"]},{type:D.MatFormField,selector:"mat-form-field",inputs:["color","floatLabel","appearance","hideRequiredMarker","hintLabel"],exportAs:["matFormField"]},{type:U.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex"],exportAs:["matSelect"]},{type:K.MatOption,selector:"mat-option",exportAs:["matOption"]},{type:ce.TbCheckboxComponent,selector:"tb-checkbox",inputs:["disabled","trueValue","falseValue"],outputs:["valueChange"]},{type:j.TogglePasswordComponent,selector:"tb-toggle-password"}],directives:[{type:E.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{type:q.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{type:q.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{type:q.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{type:q.FormControlName,selector:"[formControlName]",inputs:["disabled","formControlName","ngModel"],outputs:["ngModelChange"]},{type:w.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{type:D.MatLabel,selector:"mat-label"},{type:P.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{type:w.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{type:E.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{type:E.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{type:R.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["id","disabled","required","type","value","readonly","placeholder","errorStateMatcher","aria-describedby"],exportAs:["matInput"]},{type:q.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{type:q.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{type:D.MatError,selector:"mat-error",inputs:["id"]},{type:q.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{type:q.MaxValidator,selector:"input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]",inputs:["max"]},{type:q.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{type:D.MatSuffix,selector:"[matSuffix]"}],pipes:{translate:P.TranslatePipe}}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Ct,decorators:[{type:o,args:[{selector:"tb-action-node-send-email-config",templateUrl:"./send-email-config.component.html",styleUrls:[]}]}],ctorParameters:function(){return[{type:T.Store},{type:q.FormBuilder}]}});class Ft extends s{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.sendSmsConfigForm}onConfigurationSet(e){this.sendSmsConfigForm=this.fb.group({numbersToTemplate:[e?e.numbersToTemplate:null,[k.required]],smsMessageTemplate:[e?e.smsMessageTemplate:null,[k.required]],useSystemSmsSettings:[!!e&&e.useSystemSmsSettings,[]],smsProviderConfiguration:[e?e.smsProviderConfiguration:null,[]]})}validatorTriggers(){return["useSystemSmsSettings"]}updateValidators(e){this.sendSmsConfigForm.get("useSystemSmsSettings").value?this.sendSmsConfigForm.get("smsProviderConfiguration").setValidators([]):this.sendSmsConfigForm.get("smsProviderConfiguration").setValidators([k.required]),this.sendSmsConfigForm.get("smsProviderConfiguration").updateValueAndValidity({emitEvent:e})}}e("SendSmsConfigComponent",Ft),Ft.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Ft,deps:[{token:T.Store},{token:q.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),Ft.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"12.0.0",version:"12.2.15",type:Ft,selector:"tb-action-node-send-sms-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.numbers-to-template\n \n \n {{ \'tb.rulenode.numbers-to-template-required\' | translate }}\n \n \n \n \n tb.rulenode.sms-message-template\n \n \n {{ \'tb.rulenode.sms-message-template-required\' | translate }}\n \n \n \n \n {{ \'tb.rulenode.use-system-sms-settings\' | translate }}\n \n \n \n
\n',components:[{type:D.MatFormField,selector:"mat-form-field",inputs:["color","floatLabel","appearance","hideRequiredMarker","hintLabel"],exportAs:["matFormField"]},{type:V.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex","aria-label","aria-labelledby","id","labelPosition","name","required","checked","disabled","indeterminate","aria-describedby","value"],outputs:["change","indeterminateChange"],exportAs:["matCheckbox"]},{type:ge.SmsProviderConfigurationComponent,selector:"tb-sms-provider-configuration",inputs:["required","disabled"]}],directives:[{type:E.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{type:q.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{type:q.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{type:D.MatLabel,selector:"mat-label"},{type:P.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{type:R.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["id","disabled","required","type","value","readonly","placeholder","errorStateMatcher","aria-describedby"],exportAs:["matInput"]},{type:q.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{type:q.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{type:q.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{type:q.FormControlName,selector:"[formControlName]",inputs:["disabled","formControlName","ngModel"],outputs:["ngModelChange"]},{type:w.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{type:D.MatError,selector:"mat-error",inputs:["id"]},{type:D.MatHint,selector:"mat-hint",inputs:["align","id"]}],pipes:{translate:P.TranslatePipe,safeHtml:Te}}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Ft,decorators:[{type:o,args:[{selector:"tb-action-node-send-sms-config",templateUrl:"./send-sms-config.component.html",styleUrls:[]}]}],ctorParameters:function(){return[{type:T.Store},{type:q.FormBuilder}]}});class Lt extends s{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.snsConfigForm}onConfigurationSet(e){this.snsConfigForm=this.fb.group({topicArnPattern:[e?e.topicArnPattern:null,[k.required]],accessKeyId:[e?e.accessKeyId:null,[k.required]],secretAccessKey:[e?e.secretAccessKey:null,[k.required]],region:[e?e.region:null,[k.required]]})}}e("SnsConfigComponent",Lt),Lt.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Lt,deps:[{token:T.Store},{token:q.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),Lt.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"12.0.0",version:"12.2.15",type:Lt,selector:"tb-action-node-sns-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.topic-arn-pattern\n \n \n {{ \'tb.rulenode.topic-arn-pattern-required\' | translate }}\n \n \n \n \n tb.rulenode.aws-access-key-id\n \n \n {{ \'tb.rulenode.aws-access-key-id-required\' | translate }}\n \n \n \n tb.rulenode.aws-secret-access-key\n \n \n {{ \'tb.rulenode.aws-secret-access-key-required\' | translate }}\n \n \n \n tb.rulenode.aws-region\n \n \n {{ \'tb.rulenode.aws-region-required\' | translate }}\n \n \n
\n',components:[{type:D.MatFormField,selector:"mat-form-field",inputs:["color","floatLabel","appearance","hideRequiredMarker","hintLabel"],exportAs:["matFormField"]}],directives:[{type:E.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{type:q.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{type:q.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{type:D.MatLabel,selector:"mat-label"},{type:P.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{type:R.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["id","disabled","required","type","value","readonly","placeholder","errorStateMatcher","aria-describedby"],exportAs:["matInput"]},{type:q.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{type:q.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{type:q.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{type:q.FormControlName,selector:"[formControlName]",inputs:["disabled","formControlName","ngModel"],outputs:["ngModelChange"]},{type:w.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{type:D.MatError,selector:"mat-error",inputs:["id"]},{type:D.MatHint,selector:"mat-hint",inputs:["align","id"]}],pipes:{translate:P.TranslatePipe,safeHtml:Te}}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Lt,decorators:[{type:o,args:[{selector:"tb-action-node-sns-config",templateUrl:"./sns-config.component.html",styleUrls:[]}]}],ctorParameters:function(){return[{type:T.Store},{type:q.FormBuilder}]}});class vt extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.sqsQueueType=Ue,this.sqsQueueTypes=Object.keys(Ue),this.sqsQueueTypeTranslationsMap=Ke}configForm(){return this.sqsConfigForm}onConfigurationSet(e){this.sqsConfigForm=this.fb.group({queueType:[e?e.queueType:null,[k.required]],queueUrlPattern:[e?e.queueUrlPattern:null,[k.required]],delaySeconds:[e?e.delaySeconds:null,[k.min(0),k.max(900)]],messageAttributes:[e?e.messageAttributes:null,[]],accessKeyId:[e?e.accessKeyId:null,[k.required]],secretAccessKey:[e?e.secretAccessKey:null,[k.required]],region:[e?e.region:null,[k.required]]})}}e("SqsConfigComponent",vt),vt.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:vt,deps:[{token:T.Store},{token:q.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),vt.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"12.0.0",version:"12.2.15",type:vt,selector:"tb-action-node-sqs-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.queue-type\n \n \n {{ sqsQueueTypeTranslationsMap.get(type) | translate }}\n \n \n \n \n tb.rulenode.queue-url-pattern\n \n \n {{ \'tb.rulenode.queue-url-pattern-required\' | translate }}\n \n \n \n \n tb.rulenode.delay-seconds\n \n \n {{ \'tb.rulenode.min-delay-seconds-message\' | translate }}\n \n \n {{ \'tb.rulenode.max-delay-seconds-message\' | translate }}\n \n \n \n
\n \n \n \n tb.rulenode.aws-access-key-id\n \n \n {{ \'tb.rulenode.aws-access-key-id-required\' | translate }}\n \n \n \n tb.rulenode.aws-secret-access-key\n \n \n {{ \'tb.rulenode.aws-secret-access-key-required\' | translate }}\n \n \n \n tb.rulenode.aws-region\n \n \n {{ \'tb.rulenode.aws-region-required\' | translate }}\n \n \n
\n',components:[{type:D.MatFormField,selector:"mat-form-field",inputs:["color","floatLabel","appearance","hideRequiredMarker","hintLabel"],exportAs:["matFormField"]},{type:U.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex"],exportAs:["matSelect"]},{type:K.MatOption,selector:"mat-option",exportAs:["matOption"]},{type:nt,selector:"tb-kv-map-config",inputs:["disabled","requiredText","keyText","keyRequiredText","valText","valRequiredText","hintText","required"]}],directives:[{type:E.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{type:q.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{type:q.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{type:D.MatLabel,selector:"mat-label"},{type:P.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{type:q.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{type:q.FormControlName,selector:"[formControlName]",inputs:["disabled","formControlName","ngModel"],outputs:["ngModelChange"]},{type:q.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{type:w.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{type:R.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["id","disabled","required","type","value","readonly","placeholder","errorStateMatcher","aria-describedby"],exportAs:["matInput"]},{type:q.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{type:w.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{type:D.MatError,selector:"mat-error",inputs:["id"]},{type:D.MatHint,selector:"mat-hint",inputs:["align","id"]},{type:q.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{type:q.MaxValidator,selector:"input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]",inputs:["max"]},{type:q.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"}],pipes:{translate:P.TranslatePipe,safeHtml:Te}}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:vt,decorators:[{type:o,args:[{selector:"tb-action-node-sqs-config",templateUrl:"./sqs-config.component.html",styleUrls:[]}]}],ctorParameters:function(){return[{type:T.Store},{type:q.FormBuilder}]}});class It extends s{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.timeseriesConfigForm}onConfigurationSet(e){this.timeseriesConfigForm=this.fb.group({defaultTTL:[e?e.defaultTTL:null,[k.required,k.min(0)]],skipLatestPersistence:[!!e&&e.skipLatestPersistence,[]],useServerTs:[!!e&&e.useServerTs,[]]})}}e("TimeseriesConfigComponent",It),It.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:It,deps:[{token:T.Store},{token:q.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),It.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"12.0.0",version:"12.2.15",type:It,selector:"tb-action-node-timeseries-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.default-ttl\n \n \n {{ \'tb.rulenode.default-ttl-required\' | translate }}\n \n \n {{ \'tb.rulenode.min-default-ttl-message\' | translate }}\n \n \n \n {{ \'tb.rulenode.skip-latest-persistence\' | translate }}\n \n \n {{ \'tb.rulenode.use-server-ts\' | translate }}\n \n
tb.rulenode.use-server-ts-hint
\n
\n',components:[{type:D.MatFormField,selector:"mat-form-field",inputs:["color","floatLabel","appearance","hideRequiredMarker","hintLabel"],exportAs:["matFormField"]},{type:V.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex","aria-label","aria-labelledby","id","labelPosition","name","required","checked","disabled","indeterminate","aria-describedby","value"],outputs:["change","indeterminateChange"],exportAs:["matCheckbox"]}],directives:[{type:E.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{type:q.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{type:q.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{type:E.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{type:D.MatLabel,selector:"mat-label"},{type:P.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{type:q.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{type:q.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{type:R.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["id","disabled","required","type","value","readonly","placeholder","errorStateMatcher","aria-describedby"],exportAs:["matInput"]},{type:q.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{type:q.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{type:q.FormControlName,selector:"[formControlName]",inputs:["disabled","formControlName","ngModel"],outputs:["ngModelChange"]},{type:q.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{type:w.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{type:D.MatError,selector:"mat-error",inputs:["id"]}],pipes:{translate:P.TranslatePipe}}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:It,decorators:[{type:o,args:[{selector:"tb-action-node-timeseries-config",templateUrl:"./timeseries-config.component.html",styleUrls:[]}]}],ctorParameters:function(){return[{type:T.Store},{type:q.FormBuilder}]}});class Nt extends s{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.unassignCustomerConfigForm}onConfigurationSet(e){this.unassignCustomerConfigForm=this.fb.group({customerNamePattern:[e?e.customerNamePattern:null,[k.required,k.pattern(/.*\S.*/)]],customerCacheExpiration:[e?e.customerCacheExpiration:null,[k.required,k.min(0)]]})}prepareOutputConfig(e){return e.customerNamePattern=e.customerNamePattern.trim(),e}}e("UnassignCustomerConfigComponent",Nt),Nt.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Nt,deps:[{token:T.Store},{token:q.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),Nt.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"12.0.0",version:"12.2.15",type:Nt,selector:"tb-action-node-un-assign-to-customer-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.customer-name-pattern\n \n \n {{ \'tb.rulenode.customer-name-pattern-required\' | translate }}\n \n \n \n \n tb.rulenode.customer-cache-expiration\n \n \n {{ \'tb.rulenode.customer-cache-expiration-required\' | translate }}\n \n \n {{ \'tb.rulenode.customer-cache-expiration-range\' | translate }}\n \n tb.rulenode.customer-cache-expiration-hint\n \n
\n',components:[{type:D.MatFormField,selector:"mat-form-field",inputs:["color","floatLabel","appearance","hideRequiredMarker","hintLabel"],exportAs:["matFormField"]}],directives:[{type:E.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{type:q.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{type:q.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{type:D.MatLabel,selector:"mat-label"},{type:P.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{type:R.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["id","disabled","required","type","value","readonly","placeholder","errorStateMatcher","aria-describedby"],exportAs:["matInput"]},{type:q.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{type:q.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{type:q.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{type:q.FormControlName,selector:"[formControlName]",inputs:["disabled","formControlName","ngModel"],outputs:["ngModelChange"]},{type:w.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{type:D.MatError,selector:"mat-error",inputs:["id"]},{type:D.MatHint,selector:"mat-hint",inputs:["align","id"]},{type:q.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{type:q.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"}],pipes:{translate:P.TranslatePipe,safeHtml:Te}}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Nt,decorators:[{type:o,args:[{selector:"tb-action-node-un-assign-to-customer-config",templateUrl:"./unassign-customer-config.component.html",styleUrls:[]}]}],ctorParameters:function(){return[{type:T.Store},{type:q.FormBuilder}]}});class Tt extends y{constructor(e,t){super(e),this.store=e,this.fb=t,this.directionTypes=Object.keys(c),this.directionTypeTranslations=g,this.entityType=x,this.propagateChange=null}get required(){return this.requiredValue}set required(e){this.requiredValue=le(e)}ngOnInit(){this.deviceRelationsQueryFormGroup=this.fb.group({fetchLastLevelOnly:[!1,[]],direction:[null,[k.required]],maxLevel:[null,[]],relationType:[null],deviceTypes:[null,[k.required]]}),this.deviceRelationsQueryFormGroup.valueChanges.subscribe((e=>{this.deviceRelationsQueryFormGroup.valid?this.propagateChange(e):this.propagateChange(null)}))}registerOnChange(e){this.propagateChange=e}registerOnTouched(e){}setDisabledState(e){this.disabled=e,this.disabled?this.deviceRelationsQueryFormGroup.disable({emitEvent:!1}):this.deviceRelationsQueryFormGroup.enable({emitEvent:!1})}writeValue(e){this.deviceRelationsQueryFormGroup.reset(e,{emitEvent:!1})}}e("DeviceRelationsQueryConfigComponent",Tt),Tt.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Tt,deps:[{token:T.Store},{token:q.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),Tt.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"12.0.0",version:"12.2.15",type:Tt,selector:"tb-device-relations-query-config",inputs:{disabled:"disabled",required:"required"},providers:[{provide:S,useExisting:n((()=>Tt)),multi:!0}],usesInheritance:!0,ngImport:t,template:'
\n \n {{ \'alias.last-level-relation\' | translate }}\n \n
\n \n relation.direction\n \n \n {{ directionTypeTranslations.get(type) | translate }}\n \n \n \n \n tb.rulenode.max-relation-level\n \n \n
\n
relation.relation-type
\n \n \n
device.device-types
\n \n \n
\n',components:[{type:V.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex","aria-label","aria-labelledby","id","labelPosition","name","required","checked","disabled","indeterminate","aria-describedby","value"],outputs:["change","indeterminateChange"],exportAs:["matCheckbox"]},{type:D.MatFormField,selector:"mat-form-field",inputs:["color","floatLabel","appearance","hideRequiredMarker","hintLabel"],exportAs:["matFormField"]},{type:U.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex"],exportAs:["matSelect"]},{type:K.MatOption,selector:"mat-option",exportAs:["matOption"]},{type:ye.RelationTypeAutocompleteComponent,selector:"tb-relation-type-autocomplete",inputs:["required","disabled"]},{type:be.EntitySubTypeListComponent,selector:"tb-entity-subtype-list",inputs:["required","disabled","entityType"]}],directives:[{type:E.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{type:q.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{type:q.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{type:q.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{type:q.FormControlName,selector:"[formControlName]",inputs:["disabled","formControlName","ngModel"],outputs:["ngModelChange"]},{type:E.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{type:D.MatLabel,selector:"mat-label"},{type:P.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{type:q.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{type:w.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{type:E.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{type:R.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["id","disabled","required","type","value","readonly","placeholder","errorStateMatcher","aria-describedby"],exportAs:["matInput"]},{type:q.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{type:q.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{type:q.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"}],pipes:{translate:P.TranslatePipe}}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Tt,decorators:[{type:o,args:[{selector:"tb-device-relations-query-config",templateUrl:"./device-relations-query-config.component.html",styleUrls:[],providers:[{provide:S,useExisting:n((()=>Tt)),multi:!0}]}]}],ctorParameters:function(){return[{type:T.Store},{type:q.FormBuilder}]},propDecorators:{disabled:[{type:l}],required:[{type:l}]}});class qt extends y{constructor(e,t){super(e),this.store=e,this.fb=t,this.directionTypes=Object.keys(c),this.directionTypeTranslations=g,this.propagateChange=null}get required(){return this.requiredValue}set required(e){this.requiredValue=le(e)}ngOnInit(){this.relationsQueryFormGroup=this.fb.group({fetchLastLevelOnly:[!1,[]],direction:[null,[k.required]],maxLevel:[null,[]],filters:[null]}),this.relationsQueryFormGroup.valueChanges.subscribe((e=>{this.relationsQueryFormGroup.valid?this.propagateChange(e):this.propagateChange(null)}))}registerOnChange(e){this.propagateChange=e}registerOnTouched(e){}setDisabledState(e){this.disabled=e,this.disabled?this.relationsQueryFormGroup.disable({emitEvent:!1}):this.relationsQueryFormGroup.enable({emitEvent:!1})}writeValue(e){this.relationsQueryFormGroup.reset(e||{},{emitEvent:!1})}}e("RelationsQueryConfigComponent",qt),qt.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:qt,deps:[{token:T.Store},{token:q.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),qt.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"12.0.0",version:"12.2.15",type:qt,selector:"tb-relations-query-config",inputs:{disabled:"disabled",required:"required"},providers:[{provide:S,useExisting:n((()=>qt)),multi:!0}],usesInheritance:!0,ngImport:t,template:'
\n \n {{ \'alias.last-level-relation\' | translate }}\n \n
\n \n relation.direction\n \n \n {{ directionTypeTranslations.get(type) | translate }}\n \n \n \n \n tb.rulenode.max-relation-level\n \n \n
\n
relation.relation-filters
\n \n
\n',components:[{type:V.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex","aria-label","aria-labelledby","id","labelPosition","name","required","checked","disabled","indeterminate","aria-describedby","value"],outputs:["change","indeterminateChange"],exportAs:["matCheckbox"]},{type:D.MatFormField,selector:"mat-form-field",inputs:["color","floatLabel","appearance","hideRequiredMarker","hintLabel"],exportAs:["matFormField"]},{type:U.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex"],exportAs:["matSelect"]},{type:K.MatOption,selector:"mat-option",exportAs:["matOption"]},{type:he.RelationFiltersComponent,selector:"tb-relation-filters",inputs:["disabled","allowedEntityTypes"]}],directives:[{type:E.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{type:q.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{type:q.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{type:q.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{type:q.FormControlName,selector:"[formControlName]",inputs:["disabled","formControlName","ngModel"],outputs:["ngModelChange"]},{type:E.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{type:D.MatLabel,selector:"mat-label"},{type:P.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{type:q.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{type:w.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{type:E.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{type:R.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["id","disabled","required","type","value","readonly","placeholder","errorStateMatcher","aria-describedby"],exportAs:["matInput"]},{type:q.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{type:q.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{type:q.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"}],pipes:{translate:P.TranslatePipe}}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:qt,decorators:[{type:o,args:[{selector:"tb-relations-query-config",templateUrl:"./relations-query-config.component.html",styleUrls:[],providers:[{provide:S,useExisting:n((()=>qt)),multi:!0}]}]}],ctorParameters:function(){return[{type:T.Store},{type:q.FormBuilder}]},propDecorators:{disabled:[{type:l}],required:[{type:l}]}});class kt extends y{constructor(e,t,o,r){super(e),this.store=e,this.translate=t,this.truncate=o,this.fb=r,this.placeholder="tb.rulenode.message-type",this.separatorKeysCodes=[Z,X,ee],this.messageTypes=[],this.messageTypesList=[],this.searchText="",this.propagateChange=e=>{},this.messageTypeConfigForm=this.fb.group({messageType:[null]});for(const e of Object.keys(b))this.messageTypesList.push({name:h.get(b[e]),value:e})}get required(){return this.requiredValue}set required(e){this.requiredValue=le(e)}registerOnChange(e){this.propagateChange=e}registerOnTouched(e){}ngOnInit(){this.filteredMessageTypes=this.messageTypeConfigForm.get("messageType").valueChanges.pipe(ue(""),pe((e=>e||"")),de((e=>this.fetchMessageTypes(e))),fe())}ngAfterViewInit(){}setDisabledState(e){this.disabled=e,this.disabled?this.messageTypeConfigForm.disable({emitEvent:!1}):this.messageTypeConfigForm.enable({emitEvent:!1})}writeValue(e){this.searchText="",this.messageTypes.length=0,e&&e.forEach((e=>{const t=this.messageTypesList.find((t=>t.value===e));t?this.messageTypes.push({name:t.name,value:t.value}):this.messageTypes.push({name:e,value:e})}))}displayMessageTypeFn(e){return e?e.name:void 0}textIsNotEmpty(e){return!!(e&&null!=e&&e.length>0)}createMessageType(e,t){e.preventDefault(),this.transformMessageType(t)}add(e){this.transformMessageType(e.value)}fetchMessageTypes(e){if(this.searchText=e,this.searchText&&this.searchText.length){const e=this.searchText.toUpperCase();return Ce(this.messageTypesList.filter((t=>t.name.toUpperCase().includes(e))))}return Ce(this.messageTypesList)}transformMessageType(e){if((e||"").trim()){let t=null;const o=e.trim(),r=this.messageTypesList.find((e=>e.name===o));t=r?{name:r.name,value:r.value}:{name:o,value:o},t&&this.addMessageType(t)}this.clear("")}remove(e){const t=this.messageTypes.indexOf(e);t>=0&&(this.messageTypes.splice(t,1),this.updateModel())}selected(e){this.addMessageType(e.option.value),this.clear("")}addMessageType(e){-1===this.messageTypes.findIndex((t=>t.value===e.value))&&(this.messageTypes.push(e),this.updateModel())}onFocus(){this.messageTypeConfigForm.get("messageType").updateValueAndValidity({onlySelf:!0,emitEvent:!0})}clear(e=""){this.messageTypeInput.nativeElement.value=e,this.messageTypeConfigForm.get("messageType").patchValue(null,{emitEvent:!0}),setTimeout((()=>{this.messageTypeInput.nativeElement.blur(),this.messageTypeInput.nativeElement.focus()}),0)}updateModel(){const e=this.messageTypes.map((e=>e.value));this.required?(this.chipList.errorState=!e.length,this.propagateChange(e.length>0?e:null)):(this.chipList.errorState=!1,this.propagateChange(e))}}e("MessageTypesConfigComponent",kt),kt.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:kt,deps:[{token:T.Store},{token:P.TranslateService},{token:C.TruncatePipe},{token:q.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),kt.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"12.0.0",version:"12.2.15",type:kt,selector:"tb-message-types-config",inputs:{required:"required",label:"label",placeholder:"placeholder",disabled:"disabled"},providers:[{provide:S,useExisting:n((()=>kt)),multi:!0}],viewQueries:[{propertyName:"chipList",first:!0,predicate:["chipList"],descendants:!0},{propertyName:"matAutocomplete",first:!0,predicate:["messageTypeAutocomplete"],descendants:!0},{propertyName:"messageTypeInput",first:!0,predicate:["messageTypeInput"],descendants:!0}],usesInheritance:!0,ngImport:t,template:'\n {{ label }}\n \n \n {{messageType.name}}\n close\n \n \n \n \n \n \n \n \n
\n
\n tb.rulenode.no-message-types-found\n
\n \n \n {{ translate.get(\'tb.rulenode.no-message-type-matching\',\n {messageType: truncate.transform(searchText, true, 6, '...')}) | async }}\n \n \n \n tb.rulenode.create-new-message-type\n \n
\n
\n
\n \n {{ \'tb.rulenode.message-types-required\' | translate }}\n \n
\n',components:[{type:D.MatFormField,selector:"mat-form-field",inputs:["color","floatLabel","appearance","hideRequiredMarker","hintLabel"],exportAs:["matFormField"]},{type:te.MatChipList,selector:"mat-chip-list",inputs:["aria-orientation","multiple","compareWith","value","required","placeholder","disabled","selectable","tabIndex","errorStateMatcher"],outputs:["change","valueChange"],exportAs:["matChipList"]},{type:oe.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{type:Fe.MatAutocomplete,selector:"mat-autocomplete",inputs:["disableRipple"],exportAs:["matAutocomplete"]},{type:K.MatOption,selector:"mat-option",exportAs:["matOption"]}],directives:[{type:q.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{type:q.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{type:w.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{type:D.MatLabel,selector:"mat-label"},{type:P.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{type:w.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{type:te.MatChip,selector:"mat-basic-chip, [mat-basic-chip], mat-chip, [mat-chip]",inputs:["color","disableRipple","tabIndex","selected","value","selectable","disabled","removable"],outputs:["selectionChange","destroyed","removed"],exportAs:["matChip"]},{type:te.MatChipRemove,selector:"[matChipRemove]"},{type:R.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["id","disabled","required","type","value","readonly","placeholder","errorStateMatcher","aria-describedby"],exportAs:["matInput"]},{type:q.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{type:Fe.MatAutocompleteTrigger,selector:"input[matAutocomplete], textarea[matAutocomplete]",exportAs:["matAutocompleteTrigger"]},{type:te.MatChipInput,selector:"input[matChipInputFor]",inputs:["matChipInputSeparatorKeyCodes","placeholder","id","matChipInputFor","matChipInputAddOnBlur","disabled"],outputs:["matChipInputTokenEnd"],exportAs:["matChipInput","matChipInputFor"]},{type:q.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{type:q.FormControlName,selector:"[formControlName]",inputs:["disabled","formControlName","ngModel"],outputs:["ngModelChange"]},{type:Fe.MatAutocompleteOrigin,selector:"[matAutocompleteOrigin]",exportAs:["matAutocompleteOrigin"]},{type:D.MatError,selector:"mat-error",inputs:["id"]}],pipes:{translate:P.TranslatePipe,async:w.AsyncPipe,highlight:Le.HighlightPipe}}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:kt,decorators:[{type:o,args:[{selector:"tb-message-types-config",templateUrl:"./message-types-config.component.html",styleUrls:[],providers:[{provide:S,useExisting:n((()=>kt)),multi:!0}]}]}],ctorParameters:function(){return[{type:T.Store},{type:P.TranslateService},{type:C.TruncatePipe},{type:q.FormBuilder}]},propDecorators:{required:[{type:l}],label:[{type:l}],placeholder:[{type:l}],disabled:[{type:l}],chipList:[{type:a,args:["chipList",{static:!1}]}],matAutocomplete:[{type:a,args:["messageTypeAutocomplete",{static:!1}]}],messageTypeInput:[{type:a,args:["messageTypeInput",{static:!1}]}]}});class Mt{}e("RulenodeCoreConfigCommonModule",Mt),Mt.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Mt,deps:[],target:t.ɵɵFactoryTarget.NgModule}),Mt.ɵmod=t.ɵɵngDeclareNgModule({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Mt,declarations:[nt,Tt,qt,kt,st,Te],imports:[O,F,xe],exports:[nt,Tt,qt,kt,st,Te]}),Mt.ɵinj=t.ɵɵngDeclareInjector({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Mt,imports:[[O,F,xe]]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Mt,decorators:[{type:i,args:[{declarations:[nt,Tt,qt,kt,st,Te],imports:[O,F,xe],exports:[nt,Tt,qt,kt,st,Te]}]}]});class St{}e("RuleNodeCoreConfigActionModule",St),St.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:St,deps:[],target:t.ɵɵFactoryTarget.NgModule}),St.ɵmod=t.ɵɵngDeclareNgModule({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:St,declarations:[ke,It,bt,it,qe,Ze,Xe,et,pt,tt,rt,at,ut,yt,ht,Nt,Lt,vt,dt,lt,mt,gt,xt,Ct,Je,Ye,ot,Ft,ct,ft],imports:[O,F,xe,Mt],exports:[ke,It,bt,it,qe,Ze,Xe,et,pt,tt,rt,at,ut,yt,ht,Nt,Lt,vt,dt,lt,mt,gt,xt,Ct,Je,Ye,ot,Ft,ct,ft]}),St.ɵinj=t.ɵɵngDeclareInjector({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:St,imports:[[O,F,xe,Mt]]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:St,decorators:[{type:i,args:[{declarations:[ke,It,bt,it,qe,Ze,Xe,et,pt,tt,rt,at,ut,yt,ht,Nt,Lt,vt,dt,lt,mt,gt,xt,Ct,Je,Ye,ot,Ft,ct,ft],imports:[O,F,xe,Mt],exports:[ke,It,bt,it,qe,Ze,Xe,et,pt,tt,rt,at,ut,yt,ht,Nt,Lt,vt,dt,lt,mt,gt,xt,Ct,Je,Ye,ot,Ft,ct,ft]}]}]});class At extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.separatorKeysCodes=[Z,X,ee]}configForm(){return this.calculateDeltaConfigForm}onConfigurationSet(e){this.calculateDeltaConfigForm=this.fb.group({inputValueKey:[e?e.inputValueKey:null,[k.required]],outputValueKey:[e?e.outputValueKey:null,[k.required]],useCache:[e?e.useCache:null,[]],addPeriodBetweenMsgs:[!!e&&e.addPeriodBetweenMsgs,[]],periodValueKey:[e?e.periodValueKey:null,[]],round:[e?e.round:null,[k.min(0),k.max(15)]],tellFailureIfDeltaIsNegative:[e?e.tellFailureIfDeltaIsNegative:null,[]]})}updateValidators(e){this.calculateDeltaConfigForm.get("addPeriodBetweenMsgs").value?this.calculateDeltaConfigForm.get("periodValueKey").setValidators([k.required]):this.calculateDeltaConfigForm.get("periodValueKey").setValidators([]),this.calculateDeltaConfigForm.get("periodValueKey").updateValueAndValidity({emitEvent:e})}validatorTriggers(){return["addPeriodBetweenMsgs"]}}e("CalculateDeltaConfigComponent",At),At.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:At,deps:[{token:T.Store},{token:q.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),At.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"12.0.0",version:"12.2.15",type:At,selector:"tb-enrichment-node-calculate-delta-config",usesInheritance:!0,ngImport:t,template:'
\n
\n \n tb.rulenode.input-value-key\n \n \n {{ \'tb.rulenode.input-value-key-required\' | translate }}\n \n \n \n tb.rulenode.output-value-key\n \n \n {{ \'tb.rulenode.output-value-key-required\' | translate }}\n \n \n \n tb.rulenode.round\n \n \n {{ \'tb.rulenode.round-range\' | translate }}\n \n \n {{ \'tb.rulenode.round-range\' | translate }}\n \n \n
\n \n {{ \'tb.rulenode.use-cache\' | translate }}\n \n \n {{ \'tb.rulenode.tell-failure-if-delta-is-negative\' | translate }}\n \n \n {{ \'tb.rulenode.add-period-between-msgs\' | translate }}\n \n \n tb.rulenode.period-value-key\n \n \n {{ \'tb.rulenode.period-value-key-required\' | translate }}\n \n \n
\n',components:[{type:D.MatFormField,selector:"mat-form-field",inputs:["color","floatLabel","appearance","hideRequiredMarker","hintLabel"],exportAs:["matFormField"]},{type:V.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex","aria-label","aria-labelledby","id","labelPosition","name","required","checked","disabled","indeterminate","aria-describedby","value"],outputs:["change","indeterminateChange"],exportAs:["matCheckbox"]}],directives:[{type:E.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{type:q.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{type:q.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{type:E.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{type:E.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{type:D.MatLabel,selector:"mat-label"},{type:P.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{type:R.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["id","disabled","required","type","value","readonly","placeholder","errorStateMatcher","aria-describedby"],exportAs:["matInput"]},{type:q.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{type:q.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{type:q.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{type:q.FormControlName,selector:"[formControlName]",inputs:["disabled","formControlName","ngModel"],outputs:["ngModelChange"]},{type:w.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{type:D.MatError,selector:"mat-error",inputs:["id"]},{type:q.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{type:q.MaxValidator,selector:"input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]",inputs:["max"]},{type:q.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"}],pipes:{translate:P.TranslatePipe}}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:At,decorators:[{type:o,args:[{selector:"tb-enrichment-node-calculate-delta-config",templateUrl:"./calculate-delta-config.component.html",styleUrls:[]}]}],ctorParameters:function(){return[{type:T.Store},{type:q.FormBuilder}]}});class Gt extends s{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.customerAttributesConfigForm}onConfigurationSet(e){this.customerAttributesConfigForm=this.fb.group({telemetry:[!!e&&e.telemetry,[]],attrMapping:[e?e.attrMapping:null,[k.required]]})}}e("CustomerAttributesConfigComponent",Gt),Gt.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Gt,deps:[{token:T.Store},{token:q.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),Gt.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"12.0.0",version:"12.2.15",type:Gt,selector:"tb-enrichment-node-customer-attributes-config",usesInheritance:!0,ngImport:t,template:'
\n \n \n {{ \'tb.rulenode.latest-telemetry\' | translate }}\n \n \n \n
\n',components:[{type:V.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex","aria-label","aria-labelledby","id","labelPosition","name","required","checked","disabled","indeterminate","aria-describedby","value"],outputs:["change","indeterminateChange"],exportAs:["matCheckbox"]},{type:nt,selector:"tb-kv-map-config",inputs:["disabled","requiredText","keyText","keyRequiredText","valText","valRequiredText","hintText","required"]}],directives:[{type:E.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{type:q.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{type:q.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{type:P.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{type:E.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{type:q.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{type:q.FormControlName,selector:"[formControlName]",inputs:["disabled","formControlName","ngModel"],outputs:["ngModelChange"]},{type:q.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]}],pipes:{translate:P.TranslatePipe}}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Gt,decorators:[{type:o,args:[{selector:"tb-enrichment-node-customer-attributes-config",templateUrl:"./customer-attributes-config.component.html",styleUrls:[]}]}],ctorParameters:function(){return[{type:T.Store},{type:q.FormBuilder}]}});class Dt extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.separatorKeysCodes=[Z,X,ee]}configForm(){return this.deviceAttributesConfigForm}onConfigurationSet(e){this.deviceAttributesConfigForm=this.fb.group({deviceRelationsQuery:[e?e.deviceRelationsQuery:null,[k.required]],tellFailureIfAbsent:[!!e&&e.tellFailureIfAbsent,[]],clientAttributeNames:[e?e.clientAttributeNames:null,[]],sharedAttributeNames:[e?e.sharedAttributeNames:null,[]],serverAttributeNames:[e?e.serverAttributeNames:null,[]],latestTsKeyNames:[e?e.latestTsKeyNames:null,[]],getLatestValueWithTs:[!!e&&e.getLatestValueWithTs,[]]})}removeKey(e,t){const o=this.deviceAttributesConfigForm.get(t).value,r=o.indexOf(e);r>=0&&(o.splice(r,1),this.deviceAttributesConfigForm.get(t).setValue(o,{emitEvent:!0}))}addKey(e,t){const o=e.input;let r=e.value;if((r||"").trim()){r=r.trim();let e=this.deviceAttributesConfigForm.get(t).value;e&&-1!==e.indexOf(r)||(e||(e=[]),e.push(r),this.deviceAttributesConfigForm.get(t).setValue(e,{emitEvent:!0}))}o&&(o.value="")}}e("DeviceAttributesConfigComponent",Dt),Dt.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Dt,deps:[{token:T.Store},{token:q.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),Dt.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"12.0.0",version:"12.2.15",type:Dt,selector:"tb-enrichment-node-device-attributes-config",usesInheritance:!0,ngImport:t,template:'
\n \n \n \n \n {{ \'tb.rulenode.tell-failure-if-absent\' | translate }}\n \n
tb.rulenode.tell-failure-if-absent-hint
\n \n \n \n \n \n {{key}}\n close\n \n \n \n \n \n \n \n \n \n {{key}}\n close\n \n \n \n \n \n \n \n \n \n {{key}}\n close\n \n \n \n \n \n \n \n \n \n {{key}}\n close\n \n \n \n \n \n {{ \'tb.rulenode.get-latest-value-with-ts\' | translate }}\n \n
\n
\n',styles:[":host label.tb-title{margin-bottom:-10px}\n"],components:[{type:Tt,selector:"tb-device-relations-query-config",inputs:["disabled","required"]},{type:V.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex","aria-label","aria-labelledby","id","labelPosition","name","required","checked","disabled","indeterminate","aria-describedby","value"],outputs:["change","indeterminateChange"],exportAs:["matCheckbox"]},{type:D.MatFormField,selector:"mat-form-field",inputs:["color","floatLabel","appearance","hideRequiredMarker","hintLabel"],exportAs:["matFormField"]},{type:te.MatChipList,selector:"mat-chip-list",inputs:["aria-orientation","multiple","compareWith","value","required","placeholder","disabled","selectable","tabIndex","errorStateMatcher"],outputs:["change","valueChange"],exportAs:["matChipList"]},{type:oe.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]}],directives:[{type:E.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{type:q.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{type:q.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{type:P.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{type:q.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{type:q.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{type:q.FormControlName,selector:"[formControlName]",inputs:["disabled","formControlName","ngModel"],outputs:["ngModelChange"]},{type:E.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{type:D.MatLabel,selector:"mat-label"},{type:w.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{type:te.MatChip,selector:"mat-basic-chip, [mat-basic-chip], mat-chip, [mat-chip]",inputs:["color","disableRipple","tabIndex","selected","value","selectable","disabled","removable"],outputs:["selectionChange","destroyed","removed"],exportAs:["matChip"]},{type:te.MatChipRemove,selector:"[matChipRemove]"},{type:R.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["id","disabled","required","type","value","readonly","placeholder","errorStateMatcher","aria-describedby"],exportAs:["matInput"]},{type:te.MatChipInput,selector:"input[matChipInputFor]",inputs:["matChipInputSeparatorKeyCodes","placeholder","id","matChipInputFor","matChipInputAddOnBlur","disabled"],outputs:["matChipInputTokenEnd"],exportAs:["matChipInput","matChipInputFor"]}],pipes:{translate:P.TranslatePipe,safeHtml:Te}}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Dt,decorators:[{type:o,args:[{selector:"tb-enrichment-node-device-attributes-config",templateUrl:"./device-attributes-config.component.html",styleUrls:["./device-attributes-config.component.scss"]}]}],ctorParameters:function(){return[{type:T.Store},{type:q.FormBuilder}]}});class Vt extends s{constructor(e,t,o){super(e),this.store=e,this.translate=t,this.fb=o,this.entityDetailsTranslationsMap=we,this.entityDetailsList=[],this.searchText="",this.displayDetailsFn=this.displayDetails.bind(this);for(const e of Object.keys(Re))this.entityDetailsList.push(Re[e]);this.detailsFormControl=new G(""),this.filteredEntityDetails=this.detailsFormControl.valueChanges.pipe(ue(""),pe((e=>e||"")),de((e=>this.fetchEntityDetails(e))),fe())}ngOnInit(){super.ngOnInit()}configForm(){return this.entityDetailsConfigForm}prepareInputConfig(e){return this.searchText="",this.detailsFormControl.patchValue("",{emitEvent:!0}),e}onConfigurationSet(e){this.entityDetailsConfigForm=this.fb.group({detailsList:[e?e.detailsList:null,[k.required]],addToMetadata:[!!e&&e.addToMetadata,[]]})}displayDetails(e){return e?this.translate.instant(we.get(e)):void 0}fetchEntityDetails(e){if(this.searchText=e,this.searchText&&this.searchText.length){const e=this.searchText.toUpperCase();return Ce(this.entityDetailsList.filter((t=>this.translate.instant(we.get(Re[t])).toUpperCase().includes(e))))}return Ce(this.entityDetailsList)}detailsFieldSelected(e){this.addDetailsField(e.option.value),this.clear("")}removeDetailsField(e){const t=this.entityDetailsConfigForm.get("detailsList").value;if(t){const o=t.indexOf(e);o>=0&&(t.splice(o,1),this.entityDetailsConfigForm.get("detailsList").setValue(t))}}addDetailsField(e){let t=this.entityDetailsConfigForm.get("detailsList").value;t||(t=[]);-1===t.indexOf(e)&&(t.push(e),this.entityDetailsConfigForm.get("detailsList").setValue(t))}onEntityDetailsInputFocus(){this.detailsFormControl.updateValueAndValidity({onlySelf:!0,emitEvent:!0})}clear(e=""){this.detailsInput.nativeElement.value=e,this.detailsFormControl.patchValue(null,{emitEvent:!0}),setTimeout((()=>{this.detailsInput.nativeElement.blur(),this.detailsInput.nativeElement.focus()}),0)}}e("EntityDetailsConfigComponent",Vt),Vt.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Vt,deps:[{token:T.Store},{token:P.TranslateService},{token:q.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),Vt.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"12.0.0",version:"12.2.15",type:Vt,selector:"tb-enrichment-node-entity-details-config",viewQueries:[{propertyName:"detailsInput",first:!0,predicate:["detailsInput"],descendants:!0}],usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.entity-details\n \n \n \n {{entityDetailsTranslationsMap.get(details) | translate}}\n \n close\n \n \n \n \n \n \n \n \n
\n
\n tb.rulenode.no-entity-details-matching\n
\n
\n
\n
\n
\n \n \n {{ \'tb.rulenode.add-to-metadata\' | translate }}\n \n
tb.rulenode.add-to-metadata-hint
\n
\n',styles:[":host ::ng-deep mat-form-field.entity-fields-list .mat-form-field-wrapper{margin-bottom:-1.25em}\n"],components:[{type:D.MatFormField,selector:"mat-form-field",inputs:["color","floatLabel","appearance","hideRequiredMarker","hintLabel"],exportAs:["matFormField"]},{type:te.MatChipList,selector:"mat-chip-list",inputs:["aria-orientation","multiple","compareWith","value","required","placeholder","disabled","selectable","tabIndex","errorStateMatcher"],outputs:["change","valueChange"],exportAs:["matChipList"]},{type:oe.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{type:Fe.MatAutocomplete,selector:"mat-autocomplete",inputs:["disableRipple"],exportAs:["matAutocomplete"]},{type:K.MatOption,selector:"mat-option",exportAs:["matOption"]},{type:ie.TbErrorComponent,selector:"tb-error",inputs:["error"]},{type:V.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex","aria-label","aria-labelledby","id","labelPosition","name","required","checked","disabled","indeterminate","aria-describedby","value"],outputs:["change","indeterminateChange"],exportAs:["matCheckbox"]}],directives:[{type:E.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{type:q.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{type:q.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{type:D.MatLabel,selector:"mat-label"},{type:P.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{type:w.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{type:te.MatChip,selector:"mat-basic-chip, [mat-basic-chip], mat-chip, [mat-chip]",inputs:["color","disableRipple","tabIndex","selected","value","selectable","disabled","removable"],outputs:["selectionChange","destroyed","removed"],exportAs:["matChip"]},{type:te.MatChipRemove,selector:"[matChipRemove]"},{type:R.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["id","disabled","required","type","value","readonly","placeholder","errorStateMatcher","aria-describedby"],exportAs:["matInput"]},{type:q.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{type:Fe.MatAutocompleteTrigger,selector:"input[matAutocomplete], textarea[matAutocomplete]",exportAs:["matAutocompleteTrigger"]},{type:te.MatChipInput,selector:"input[matChipInputFor]",inputs:["matChipInputSeparatorKeyCodes","placeholder","id","matChipInputFor","matChipInputAddOnBlur","disabled"],outputs:["matChipInputTokenEnd"],exportAs:["matChipInput","matChipInputFor"]},{type:Fe.MatAutocompleteOrigin,selector:"[matAutocompleteOrigin]",exportAs:["matAutocompleteOrigin"]},{type:q.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{type:q.FormControlDirective,selector:"[formControl]",inputs:["disabled","formControl","ngModel"],outputs:["ngModelChange"],exportAs:["ngForm"]},{type:w.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{type:E.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{type:q.FormControlName,selector:"[formControlName]",inputs:["disabled","formControlName","ngModel"],outputs:["ngModelChange"]}],pipes:{translate:P.TranslatePipe,async:w.AsyncPipe,highlight:Le.HighlightPipe}}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Vt,decorators:[{type:o,args:[{selector:"tb-enrichment-node-entity-details-config",templateUrl:"./entity-details-config.component.html",styleUrls:["./entity-details-config.component.scss"]}]}],ctorParameters:function(){return[{type:T.Store},{type:P.TranslateService},{type:q.FormBuilder}]},propDecorators:{detailsInput:[{type:a,args:["detailsInput",{static:!1}]}]}});class Et extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.separatorKeysCodes=[Z,X,ee],this.aggregationTypes=L,this.aggregations=Object.keys(L),this.aggregationTypesTranslations=v,this.fetchMode=Oe,this.fetchModes=Object.keys(Oe),this.samplingOrders=Object.keys(He),this.timeUnits=Object.values(De),this.timeUnitsTranslationMap=Ve}configForm(){return this.getTelemetryFromDatabaseConfigForm}onConfigurationSet(e){this.getTelemetryFromDatabaseConfigForm=this.fb.group({latestTsKeyNames:[e?e.latestTsKeyNames:null,[]],aggregation:[e?e.aggregation:null,[k.required]],fetchMode:[e?e.fetchMode:null,[k.required]],orderBy:[e?e.orderBy:null,[]],limit:[e?e.limit:null,[]],useMetadataIntervalPatterns:[!!e&&e.useMetadataIntervalPatterns,[]],startInterval:[e?e.startInterval:null,[]],startIntervalTimeUnit:[e?e.startIntervalTimeUnit:null,[]],endInterval:[e?e.endInterval:null,[]],endIntervalTimeUnit:[e?e.endIntervalTimeUnit:null,[]],startIntervalPattern:[e?e.startIntervalPattern:null,[]],endIntervalPattern:[e?e.endIntervalPattern:null,[]]})}validatorTriggers(){return["fetchMode","useMetadataIntervalPatterns"]}updateValidators(e){const t=this.getTelemetryFromDatabaseConfigForm.get("fetchMode").value,o=this.getTelemetryFromDatabaseConfigForm.get("useMetadataIntervalPatterns").value;t&&t===Oe.ALL?(this.getTelemetryFromDatabaseConfigForm.get("aggregation").setValidators([k.required]),this.getTelemetryFromDatabaseConfigForm.get("orderBy").setValidators([k.required]),this.getTelemetryFromDatabaseConfigForm.get("limit").setValidators([k.required,k.min(2),k.max(1e3)])):(this.getTelemetryFromDatabaseConfigForm.get("aggregation").setValidators([]),this.getTelemetryFromDatabaseConfigForm.get("orderBy").setValidators([]),this.getTelemetryFromDatabaseConfigForm.get("limit").setValidators([])),o?(this.getTelemetryFromDatabaseConfigForm.get("startInterval").setValidators([]),this.getTelemetryFromDatabaseConfigForm.get("startIntervalTimeUnit").setValidators([]),this.getTelemetryFromDatabaseConfigForm.get("endInterval").setValidators([]),this.getTelemetryFromDatabaseConfigForm.get("endIntervalTimeUnit").setValidators([]),this.getTelemetryFromDatabaseConfigForm.get("startIntervalPattern").setValidators([k.required]),this.getTelemetryFromDatabaseConfigForm.get("endIntervalPattern").setValidators([k.required])):(this.getTelemetryFromDatabaseConfigForm.get("startInterval").setValidators([k.required,k.min(1),k.max(2147483647)]),this.getTelemetryFromDatabaseConfigForm.get("startIntervalTimeUnit").setValidators([k.required]),this.getTelemetryFromDatabaseConfigForm.get("endInterval").setValidators([k.required,k.min(1),k.max(2147483647)]),this.getTelemetryFromDatabaseConfigForm.get("endIntervalTimeUnit").setValidators([k.required]),this.getTelemetryFromDatabaseConfigForm.get("startIntervalPattern").setValidators([]),this.getTelemetryFromDatabaseConfigForm.get("endIntervalPattern").setValidators([])),this.getTelemetryFromDatabaseConfigForm.get("aggregation").updateValueAndValidity({emitEvent:e}),this.getTelemetryFromDatabaseConfigForm.get("orderBy").updateValueAndValidity({emitEvent:e}),this.getTelemetryFromDatabaseConfigForm.get("limit").updateValueAndValidity({emitEvent:e}),this.getTelemetryFromDatabaseConfigForm.get("startInterval").updateValueAndValidity({emitEvent:e}),this.getTelemetryFromDatabaseConfigForm.get("startIntervalTimeUnit").updateValueAndValidity({emitEvent:e}),this.getTelemetryFromDatabaseConfigForm.get("endInterval").updateValueAndValidity({emitEvent:e}),this.getTelemetryFromDatabaseConfigForm.get("endIntervalTimeUnit").updateValueAndValidity({emitEvent:e}),this.getTelemetryFromDatabaseConfigForm.get("startIntervalPattern").updateValueAndValidity({emitEvent:e}),this.getTelemetryFromDatabaseConfigForm.get("endIntervalPattern").updateValueAndValidity({emitEvent:e})}removeKey(e,t){const o=this.getTelemetryFromDatabaseConfigForm.get(t).value,r=o.indexOf(e);r>=0&&(o.splice(r,1),this.getTelemetryFromDatabaseConfigForm.get(t).setValue(o,{emitEvent:!0}))}addKey(e,t){const o=e.input;let r=e.value;if((r||"").trim()){r=r.trim();let e=this.getTelemetryFromDatabaseConfigForm.get(t).value;e&&-1!==e.indexOf(r)||(e||(e=[]),e.push(r),this.getTelemetryFromDatabaseConfigForm.get(t).setValue(e,{emitEvent:!0}))}o&&(o.value="")}}e("GetTelemetryFromDatabaseConfigComponent",Et),Et.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Et,deps:[{token:T.Store},{token:q.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),Et.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"12.0.0",version:"12.2.15",type:Et,selector:"tb-enrichment-node-get-telemetry-from-database",usesInheritance:!0,ngImport:t,template:'
\n \n \n \n \n \n {{key}}\n close\n \n \n \n \n \n \n tb.rulenode.fetch-mode\n \n \n {{ mode }}\n \n \n tb.rulenode.fetch-mode-hint\n \n
\n \n aggregation.function\n \n \n {{ aggregationTypesTranslations.get(aggregationTypes[aggregation]) | translate }}\n \n \n \n \n tb.rulenode.order-by\n \n \n {{ order }}\n \n \n tb.rulenode.order-by-hint\n \n \n tb.rulenode.limit\n \n tb.rulenode.limit-hint\n \n
\n \n {{ \'tb.rulenode.use-metadata-interval-patterns\' | translate }}\n \n
tb.rulenode.use-metadata-interval-patterns-hint
\n
\n
\n \n tb.rulenode.start-interval\n \n \n {{ \'tb.rulenode.start-interval-value-required\' | translate }}\n \n \n {{ \'tb.rulenode.time-value-range\' | translate }}\n \n \n {{ \'tb.rulenode.time-value-range\' | translate }}\n \n \n \n tb.rulenode.start-interval-time-unit\n \n \n {{ timeUnitsTranslationMap.get(timeUnit) | translate }}\n \n \n \n
\n
\n \n tb.rulenode.end-interval\n \n \n {{ \'tb.rulenode.end-interval-value-required\' | translate }}\n \n \n {{ \'tb.rulenode.time-value-range\' | translate }}\n \n \n {{ \'tb.rulenode.time-value-range\' | translate }}\n \n \n \n tb.rulenode.end-interval-time-unit\n \n \n {{ timeUnitsTranslationMap.get(timeUnit) | translate }}\n \n \n \n
\n
\n \n \n tb.rulenode.start-interval-pattern\n \n \n {{ \'tb.rulenode.start-interval-pattern-required\' | translate }}\n \n \n \n \n tb.rulenode.end-interval-pattern\n \n \n {{ \'tb.rulenode.end-interval-pattern-required\' | translate }}\n \n \n \n \n
\n',styles:[":host label.tb-title{margin-bottom:-10px}\n"],components:[{type:D.MatFormField,selector:"mat-form-field",inputs:["color","floatLabel","appearance","hideRequiredMarker","hintLabel"],exportAs:["matFormField"]},{type:te.MatChipList,selector:"mat-chip-list",inputs:["aria-orientation","multiple","compareWith","value","required","placeholder","disabled","selectable","tabIndex","errorStateMatcher"],outputs:["change","valueChange"],exportAs:["matChipList"]},{type:oe.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{type:U.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex"],exportAs:["matSelect"]},{type:K.MatOption,selector:"mat-option",exportAs:["matOption"]},{type:V.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex","aria-label","aria-labelledby","id","labelPosition","name","required","checked","disabled","indeterminate","aria-describedby","value"],outputs:["change","indeterminateChange"],exportAs:["matCheckbox"]}],directives:[{type:E.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{type:q.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{type:q.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{type:P.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{type:D.MatLabel,selector:"mat-label"},{type:w.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{type:te.MatChip,selector:"mat-basic-chip, [mat-basic-chip], mat-chip, [mat-chip]",inputs:["color","disableRipple","tabIndex","selected","value","selectable","disabled","removable"],outputs:["selectionChange","destroyed","removed"],exportAs:["matChip"]},{type:te.MatChipRemove,selector:"[matChipRemove]"},{type:R.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["id","disabled","required","type","value","readonly","placeholder","errorStateMatcher","aria-describedby"],exportAs:["matInput"]},{type:te.MatChipInput,selector:"input[matChipInputFor]",inputs:["matChipInputSeparatorKeyCodes","placeholder","id","matChipInputFor","matChipInputAddOnBlur","disabled"],outputs:["matChipInputTokenEnd"],exportAs:["matChipInput","matChipInputFor"]},{type:D.MatHint,selector:"mat-hint",inputs:["align","id"]},{type:q.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{type:q.FormControlName,selector:"[formControlName]",inputs:["disabled","formControlName","ngModel"],outputs:["ngModelChange"]},{type:q.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{type:w.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{type:q.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{type:q.MaxValidator,selector:"input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]",inputs:["max"]},{type:q.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{type:q.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{type:E.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{type:E.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{type:D.MatError,selector:"mat-error",inputs:["id"]}],pipes:{translate:P.TranslatePipe,safeHtml:Te}}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Et,decorators:[{type:o,args:[{selector:"tb-enrichment-node-get-telemetry-from-database",templateUrl:"./get-telemetry-from-database-config.component.html",styleUrls:["./get-telemetry-from-database-config.component.scss"]}]}],ctorParameters:function(){return[{type:T.Store},{type:q.FormBuilder}]}});class Pt extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.separatorKeysCodes=[Z,X,ee]}configForm(){return this.originatorAttributesConfigForm}onConfigurationSet(e){this.originatorAttributesConfigForm=this.fb.group({tellFailureIfAbsent:[!!e&&e.tellFailureIfAbsent,[]],clientAttributeNames:[e?e.clientAttributeNames:null,[]],sharedAttributeNames:[e?e.sharedAttributeNames:null,[]],serverAttributeNames:[e?e.serverAttributeNames:null,[]],latestTsKeyNames:[e?e.latestTsKeyNames:null,[]],getLatestValueWithTs:[!!e&&e.getLatestValueWithTs,[]]})}removeKey(e,t){const o=this.originatorAttributesConfigForm.get(t).value,r=o.indexOf(e);r>=0&&(o.splice(r,1),this.originatorAttributesConfigForm.get(t).setValue(o,{emitEvent:!0}))}addKey(e,t){const o=e.input;let r=e.value;if((r||"").trim()){r=r.trim();let e=this.originatorAttributesConfigForm.get(t).value;e&&-1!==e.indexOf(r)||(e||(e=[]),e.push(r),this.originatorAttributesConfigForm.get(t).setValue(e,{emitEvent:!0}))}o&&(o.value="")}}e("OriginatorAttributesConfigComponent",Pt),Pt.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Pt,deps:[{token:T.Store},{token:q.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),Pt.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"12.0.0",version:"12.2.15",type:Pt,selector:"tb-enrichment-node-originator-attributes-config",usesInheritance:!0,ngImport:t,template:'
\n \n {{ \'tb.rulenode.tell-failure-if-absent\' | translate }}\n \n
tb.rulenode.tell-failure-if-absent-hint
\n \n \n \n \n \n {{key}}\n close\n \n \n \n \n \n \n \n \n \n \n {{key}}\n close\n \n \n \n \n \n \n \n \n \n \n {{key}}\n close\n \n \n \n \n \n \n \n \n \n \n {{key}}\n close\n \n \n \n \n \n \n {{ \'tb.rulenode.get-latest-value-with-ts\' | translate }}\n \n
\n
\n',styles:[":host label.tb-title{margin-bottom:-10px}\n"],components:[{type:V.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex","aria-label","aria-labelledby","id","labelPosition","name","required","checked","disabled","indeterminate","aria-describedby","value"],outputs:["change","indeterminateChange"],exportAs:["matCheckbox"]},{type:D.MatFormField,selector:"mat-form-field",inputs:["color","floatLabel","appearance","hideRequiredMarker","hintLabel"],exportAs:["matFormField"]},{type:te.MatChipList,selector:"mat-chip-list",inputs:["aria-orientation","multiple","compareWith","value","required","placeholder","disabled","selectable","tabIndex","errorStateMatcher"],outputs:["change","valueChange"],exportAs:["matChipList"]},{type:oe.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]}],directives:[{type:E.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{type:q.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{type:q.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{type:E.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{type:q.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{type:q.FormControlName,selector:"[formControlName]",inputs:["disabled","formControlName","ngModel"],outputs:["ngModelChange"]},{type:P.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{type:D.MatLabel,selector:"mat-label"},{type:w.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{type:te.MatChip,selector:"mat-basic-chip, [mat-basic-chip], mat-chip, [mat-chip]",inputs:["color","disableRipple","tabIndex","selected","value","selectable","disabled","removable"],outputs:["selectionChange","destroyed","removed"],exportAs:["matChip"]},{type:te.MatChipRemove,selector:"[matChipRemove]"},{type:R.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["id","disabled","required","type","value","readonly","placeholder","errorStateMatcher","aria-describedby"],exportAs:["matInput"]},{type:te.MatChipInput,selector:"input[matChipInputFor]",inputs:["matChipInputSeparatorKeyCodes","placeholder","id","matChipInputFor","matChipInputAddOnBlur","disabled"],outputs:["matChipInputTokenEnd"],exportAs:["matChipInput","matChipInputFor"]},{type:D.MatHint,selector:"mat-hint",inputs:["align","id"]}],pipes:{translate:P.TranslatePipe,safeHtml:Te}}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Pt,decorators:[{type:o,args:[{selector:"tb-enrichment-node-originator-attributes-config",templateUrl:"./originator-attributes-config.component.html",styleUrls:["./originator-attributes-config.component.scss"]}]}],ctorParameters:function(){return[{type:T.Store},{type:q.FormBuilder}]}});class Rt extends s{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.originatorFieldsConfigForm}onConfigurationSet(e){this.originatorFieldsConfigForm=this.fb.group({fieldsMapping:[e?e.fieldsMapping:null,[k.required]]})}}e("OriginatorFieldsConfigComponent",Rt),Rt.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Rt,deps:[{token:T.Store},{token:q.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),Rt.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"12.0.0",version:"12.2.15",type:Rt,selector:"tb-enrichment-node-originator-fields-config",usesInheritance:!0,ngImport:t,template:'
\n \n \n \n
\n',components:[{type:nt,selector:"tb-kv-map-config",inputs:["disabled","requiredText","keyText","keyRequiredText","valText","valRequiredText","hintText","required"]}],directives:[{type:E.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{type:q.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{type:q.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{type:P.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{type:q.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{type:q.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{type:q.FormControlName,selector:"[formControlName]",inputs:["disabled","formControlName","ngModel"],outputs:["ngModelChange"]}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Rt,decorators:[{type:o,args:[{selector:"tb-enrichment-node-originator-fields-config",templateUrl:"./originator-fields-config.component.html",styleUrls:[]}]}],ctorParameters:function(){return[{type:T.Store},{type:q.FormBuilder}]}});class wt extends s{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.relatedAttributesConfigForm}onConfigurationSet(e){this.relatedAttributesConfigForm=this.fb.group({relationsQuery:[e?e.relationsQuery:null,[k.required]],telemetry:[!!e&&e.telemetry,[]],attrMapping:[e?e.attrMapping:null,[k.required]]})}}e("RelatedAttributesConfigComponent",wt),wt.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:wt,deps:[{token:T.Store},{token:q.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),wt.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"12.0.0",version:"12.2.15",type:wt,selector:"tb-enrichment-node-related-attributes-config",usesInheritance:!0,ngImport:t,template:'
\n \n \n \n \n \n {{ \'tb.rulenode.latest-telemetry\' | translate }}\n \n \n \n
\n',components:[{type:qt,selector:"tb-relations-query-config",inputs:["disabled","required"]},{type:V.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex","aria-label","aria-labelledby","id","labelPosition","name","required","checked","disabled","indeterminate","aria-describedby","value"],outputs:["change","indeterminateChange"],exportAs:["matCheckbox"]},{type:nt,selector:"tb-kv-map-config",inputs:["disabled","requiredText","keyText","keyRequiredText","valText","valRequiredText","hintText","required"]}],directives:[{type:E.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{type:q.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{type:q.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{type:P.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{type:q.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{type:q.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{type:q.FormControlName,selector:"[formControlName]",inputs:["disabled","formControlName","ngModel"],outputs:["ngModelChange"]},{type:E.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]}],pipes:{translate:P.TranslatePipe}}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:wt,decorators:[{type:o,args:[{selector:"tb-enrichment-node-related-attributes-config",templateUrl:"./related-attributes-config.component.html",styleUrls:[]}]}],ctorParameters:function(){return[{type:T.Store},{type:q.FormBuilder}]}});class Ot extends s{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.tenantAttributesConfigForm}onConfigurationSet(e){this.tenantAttributesConfigForm=this.fb.group({telemetry:[!!e&&e.telemetry,[]],attrMapping:[e?e.attrMapping:null,[k.required]]})}}e("TenantAttributesConfigComponent",Ot),Ot.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Ot,deps:[{token:T.Store},{token:q.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),Ot.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"12.0.0",version:"12.2.15",type:Ot,selector:"tb-enrichment-node-tenant-attributes-config",usesInheritance:!0,ngImport:t,template:'
\n \n \n {{ \'tb.rulenode.latest-telemetry\' | translate }}\n \n \n \n
\n',components:[{type:V.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex","aria-label","aria-labelledby","id","labelPosition","name","required","checked","disabled","indeterminate","aria-describedby","value"],outputs:["change","indeterminateChange"],exportAs:["matCheckbox"]},{type:nt,selector:"tb-kv-map-config",inputs:["disabled","requiredText","keyText","keyRequiredText","valText","valRequiredText","hintText","required"]}],directives:[{type:E.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{type:q.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{type:q.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{type:P.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{type:E.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{type:q.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{type:q.FormControlName,selector:"[formControlName]",inputs:["disabled","formControlName","ngModel"],outputs:["ngModelChange"]},{type:q.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]}],pipes:{translate:P.TranslatePipe}}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Ot,decorators:[{type:o,args:[{selector:"tb-enrichment-node-tenant-attributes-config",templateUrl:"./tenant-attributes-config.component.html",styleUrls:[]}]}],ctorParameters:function(){return[{type:T.Store},{type:q.FormBuilder}]}});class Ht{}e("RulenodeCoreConfigEnrichmentModule",Ht),Ht.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Ht,deps:[],target:t.ɵɵFactoryTarget.NgModule}),Ht.ɵmod=t.ɵɵngDeclareNgModule({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Ht,declarations:[Gt,Vt,Dt,Pt,Rt,Et,wt,Ot,At],imports:[O,F,Mt],exports:[Gt,Vt,Dt,Pt,Rt,Et,wt,Ot,At]}),Ht.ɵinj=t.ɵɵngDeclareInjector({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Ht,imports:[[O,F,Mt]]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Ht,decorators:[{type:i,args:[{declarations:[Gt,Vt,Dt,Pt,Rt,Et,wt,Ot,At],imports:[O,F,Mt],exports:[Gt,Vt,Dt,Pt,Rt,Et,wt,Ot,At]}]}]});class Ut extends s{constructor(e,t,o){super(e),this.store=e,this.translate=t,this.fb=o,this.alarmStatusTranslationsMap=I,this.alarmStatusList=[],this.searchText="",this.displayStatusFn=this.displayStatus.bind(this);for(const e of Object.keys(N))this.alarmStatusList.push(N[e]);this.statusFormControl=new G(""),this.filteredAlarmStatus=this.statusFormControl.valueChanges.pipe(ue(""),pe((e=>e||"")),de((e=>this.fetchAlarmStatus(e))),fe())}ngOnInit(){super.ngOnInit()}configForm(){return this.alarmStatusConfigForm}prepareInputConfig(e){return this.searchText="",this.statusFormControl.patchValue("",{emitEvent:!0}),e}onConfigurationSet(e){this.alarmStatusConfigForm=this.fb.group({alarmStatusList:[e?e.alarmStatusList:null,[k.required]]})}displayStatus(e){return e?this.translate.instant(I.get(e)):void 0}fetchAlarmStatus(e){const t=this.getAlarmStatusList();if(this.searchText=e,this.searchText&&this.searchText.length){const e=this.searchText.toUpperCase();return Ce(t.filter((t=>this.translate.instant(I.get(N[t])).toUpperCase().includes(e))))}return Ce(t)}alarmStatusSelected(e){this.addAlarmStatus(e.option.value),this.clear("")}removeAlarmStatus(e){const t=this.alarmStatusConfigForm.get("alarmStatusList").value;if(t){const o=t.indexOf(e);o>=0&&(t.splice(o,1),this.alarmStatusConfigForm.get("alarmStatusList").setValue(t))}}addAlarmStatus(e){let t=this.alarmStatusConfigForm.get("alarmStatusList").value;t||(t=[]);-1===t.indexOf(e)&&(t.push(e),this.alarmStatusConfigForm.get("alarmStatusList").setValue(t))}getAlarmStatusList(){return this.alarmStatusList.filter((e=>-1===this.alarmStatusConfigForm.get("alarmStatusList").value.indexOf(e)))}onAlarmStatusInputFocus(){this.statusFormControl.updateValueAndValidity({onlySelf:!0,emitEvent:!0})}clear(e=""){this.alarmStatusInput.nativeElement.value=e,this.statusFormControl.patchValue(null,{emitEvent:!0}),setTimeout((()=>{this.alarmStatusInput.nativeElement.blur(),this.alarmStatusInput.nativeElement.focus()}),0)}}e("CheckAlarmStatusComponent",Ut),Ut.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Ut,deps:[{token:T.Store},{token:P.TranslateService},{token:q.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),Ut.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"12.0.0",version:"12.2.15",type:Ut,selector:"tb-filter-node-check-alarm-status-config",viewQueries:[{propertyName:"alarmStatusInput",first:!0,predicate:["alarmStatusInput"],descendants:!0}],usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.alarm-status-filter\n \n \n \n {{alarmStatusTranslationsMap.get(alarmStatus) | translate}}\n \n close\n \n \n \n \n \n \n \n \n
\n
\n tb.rulenode.no-alarm-status-matching\n
\n
\n
\n
\n
\n \n
\n\n\n\n',components:[{type:D.MatFormField,selector:"mat-form-field",inputs:["color","floatLabel","appearance","hideRequiredMarker","hintLabel"],exportAs:["matFormField"]},{type:te.MatChipList,selector:"mat-chip-list",inputs:["aria-orientation","multiple","compareWith","value","required","placeholder","disabled","selectable","tabIndex","errorStateMatcher"],outputs:["change","valueChange"],exportAs:["matChipList"]},{type:oe.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{type:Fe.MatAutocomplete,selector:"mat-autocomplete",inputs:["disableRipple"],exportAs:["matAutocomplete"]},{type:K.MatOption,selector:"mat-option",exportAs:["matOption"]},{type:ie.TbErrorComponent,selector:"tb-error",inputs:["error"]}],directives:[{type:E.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{type:q.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{type:q.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{type:D.MatLabel,selector:"mat-label"},{type:P.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{type:w.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{type:te.MatChip,selector:"mat-basic-chip, [mat-basic-chip], mat-chip, [mat-chip]",inputs:["color","disableRipple","tabIndex","selected","value","selectable","disabled","removable"],outputs:["selectionChange","destroyed","removed"],exportAs:["matChip"]},{type:te.MatChipRemove,selector:"[matChipRemove]"},{type:R.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["id","disabled","required","type","value","readonly","placeholder","errorStateMatcher","aria-describedby"],exportAs:["matInput"]},{type:q.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{type:Fe.MatAutocompleteTrigger,selector:"input[matAutocomplete], textarea[matAutocomplete]",exportAs:["matAutocompleteTrigger"]},{type:te.MatChipInput,selector:"input[matChipInputFor]",inputs:["matChipInputSeparatorKeyCodes","placeholder","id","matChipInputFor","matChipInputAddOnBlur","disabled"],outputs:["matChipInputTokenEnd"],exportAs:["matChipInput","matChipInputFor"]},{type:Fe.MatAutocompleteOrigin,selector:"[matAutocompleteOrigin]",exportAs:["matAutocompleteOrigin"]},{type:q.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{type:q.FormControlDirective,selector:"[formControl]",inputs:["disabled","formControl","ngModel"],outputs:["ngModelChange"],exportAs:["ngForm"]},{type:w.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]}],pipes:{translate:P.TranslatePipe,async:w.AsyncPipe,highlight:Le.HighlightPipe}}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Ut,decorators:[{type:o,args:[{selector:"tb-filter-node-check-alarm-status-config",templateUrl:"./check-alarm-status.component.html",styleUrls:[]}]}],ctorParameters:function(){return[{type:T.Store},{type:P.TranslateService},{type:q.FormBuilder}]},propDecorators:{alarmStatusInput:[{type:a,args:["alarmStatusInput",{static:!1}]}]}});class Kt extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.separatorKeysCodes=[Z,X,ee]}configForm(){return this.checkMessageConfigForm}onConfigurationSet(e){this.checkMessageConfigForm=this.fb.group({messageNames:[e?e.messageNames:null,[]],metadataNames:[e?e.metadataNames:null,[]],checkAllKeys:[!!e&&e.checkAllKeys,[]]})}validateConfig(){const e=this.checkMessageConfigForm.get("messageNames").value,t=this.checkMessageConfigForm.get("metadataNames").value;return e.length>0||t.length>0}removeMessageName(e){const t=this.checkMessageConfigForm.get("messageNames").value,o=t.indexOf(e);o>=0&&(t.splice(o,1),this.checkMessageConfigForm.get("messageNames").setValue(t,{emitEvent:!0}))}removeMetadataName(e){const t=this.checkMessageConfigForm.get("metadataNames").value,o=t.indexOf(e);o>=0&&(t.splice(o,1),this.checkMessageConfigForm.get("metadataNames").setValue(t,{emitEvent:!0}))}addMessageName(e){const t=e.input;let o=e.value;if((o||"").trim()){o=o.trim();let e=this.checkMessageConfigForm.get("messageNames").value;e&&-1!==e.indexOf(o)||(e||(e=[]),e.push(o),this.checkMessageConfigForm.get("messageNames").setValue(e,{emitEvent:!0}))}t&&(t.value="")}addMetadataName(e){const t=e.input;let o=e.value;if((o||"").trim()){o=o.trim();let e=this.checkMessageConfigForm.get("metadataNames").value;e&&-1!==e.indexOf(o)||(e||(e=[]),e.push(o),this.checkMessageConfigForm.get("metadataNames").setValue(e,{emitEvent:!0}))}t&&(t.value="")}}e("CheckMessageConfigComponent",Kt),Kt.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Kt,deps:[{token:T.Store},{token:q.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),Kt.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"12.0.0",version:"12.2.15",type:Kt,selector:"tb-filter-node-check-message-config",usesInheritance:!0,ngImport:t,template:'
\n \n \n \n \n \n {{messageName}}\n close\n \n \n \n \n
tb.rulenode.separator-hint
\n \n \n \n \n \n {{metadataName}}\n close\n \n \n \n \n
tb.rulenode.separator-hint
\n \n {{ \'tb.rulenode.check-all-keys\' | translate }}\n \n
tb.rulenode.check-all-keys-hint
\n
\n',styles:[":host label.tb-title{margin-bottom:-10px}\n"],components:[{type:D.MatFormField,selector:"mat-form-field",inputs:["color","floatLabel","appearance","hideRequiredMarker","hintLabel"],exportAs:["matFormField"]},{type:te.MatChipList,selector:"mat-chip-list",inputs:["aria-orientation","multiple","compareWith","value","required","placeholder","disabled","selectable","tabIndex","errorStateMatcher"],outputs:["change","valueChange"],exportAs:["matChipList"]},{type:oe.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{type:V.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex","aria-label","aria-labelledby","id","labelPosition","name","required","checked","disabled","indeterminate","aria-describedby","value"],outputs:["change","indeterminateChange"],exportAs:["matCheckbox"]}],directives:[{type:E.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{type:q.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{type:q.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{type:P.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{type:D.MatLabel,selector:"mat-label"},{type:w.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{type:te.MatChip,selector:"mat-basic-chip, [mat-basic-chip], mat-chip, [mat-chip]",inputs:["color","disableRipple","tabIndex","selected","value","selectable","disabled","removable"],outputs:["selectionChange","destroyed","removed"],exportAs:["matChip"]},{type:te.MatChipRemove,selector:"[matChipRemove]"},{type:R.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["id","disabled","required","type","value","readonly","placeholder","errorStateMatcher","aria-describedby"],exportAs:["matInput"]},{type:te.MatChipInput,selector:"input[matChipInputFor]",inputs:["matChipInputSeparatorKeyCodes","placeholder","id","matChipInputFor","matChipInputAddOnBlur","disabled"],outputs:["matChipInputTokenEnd"],exportAs:["matChipInput","matChipInputFor"]},{type:E.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{type:q.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{type:q.FormControlName,selector:"[formControlName]",inputs:["disabled","formControlName","ngModel"],outputs:["ngModelChange"]}],pipes:{translate:P.TranslatePipe}}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Kt,decorators:[{type:o,args:[{selector:"tb-filter-node-check-message-config",templateUrl:"./check-message-config.component.html",styleUrls:["./check-message-config.component.scss"]}]}],ctorParameters:function(){return[{type:T.Store},{type:q.FormBuilder}]}});class Bt extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.entitySearchDirection=Object.keys(c),this.entitySearchDirectionTranslationsMap=g}configForm(){return this.checkRelationConfigForm}onConfigurationSet(e){this.checkRelationConfigForm=this.fb.group({checkForSingleEntity:[!!e&&e.checkForSingleEntity,[]],direction:[e?e.direction:null,[]],entityType:[e?e.entityType:null,e&&e.checkForSingleEntity?[k.required]:[]],entityId:[e?e.entityId:null,e&&e.checkForSingleEntity?[k.required]:[]],relationType:[e?e.relationType:null,[k.required]]})}validatorTriggers(){return["checkForSingleEntity"]}updateValidators(e){const t=this.checkRelationConfigForm.get("checkForSingleEntity").value;this.checkRelationConfigForm.get("entityType").setValidators(t?[k.required]:[]),this.checkRelationConfigForm.get("entityType").updateValueAndValidity({emitEvent:e}),this.checkRelationConfigForm.get("entityId").setValidators(t?[k.required]:[]),this.checkRelationConfigForm.get("entityId").updateValueAndValidity({emitEvent:e})}}e("CheckRelationConfigComponent",Bt),Bt.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Bt,deps:[{token:T.Store},{token:q.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),Bt.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"12.0.0",version:"12.2.15",type:Bt,selector:"tb-filter-node-check-relation-config",usesInheritance:!0,ngImport:t,template:'
\n \n {{ \'tb.rulenode.check-relation-to-specific-entity\' | translate }}\n \n
tb.rulenode.check-relation-hint
\n \n relation.direction\n \n \n {{ entitySearchDirectionTranslationsMap.get(direction) | translate }}\n \n \n \n
\n \n \n \n \n
\n \n \n
\n',components:[{type:V.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex","aria-label","aria-labelledby","id","labelPosition","name","required","checked","disabled","indeterminate","aria-describedby","value"],outputs:["change","indeterminateChange"],exportAs:["matCheckbox"]},{type:D.MatFormField,selector:"mat-form-field",inputs:["color","floatLabel","appearance","hideRequiredMarker","hintLabel"],exportAs:["matFormField"]},{type:U.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex"],exportAs:["matSelect"]},{type:K.MatOption,selector:"mat-option",exportAs:["matOption"]},{type:ae.EntityTypeSelectComponent,selector:"tb-entity-type-select",inputs:["allowedEntityTypes","useAliasEntityTypes","showLabel","required","disabled"]},{type:ve.EntityAutocompleteComponent,selector:"tb-entity-autocomplete",inputs:["entityType","entitySubtype","excludeEntityIds","labelText","requiredText","required","disabled"],outputs:["entityChanged"]},{type:ye.RelationTypeAutocompleteComponent,selector:"tb-relation-type-autocomplete",inputs:["required","disabled"]}],directives:[{type:E.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{type:q.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{type:q.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{type:E.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{type:q.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{type:q.FormControlName,selector:"[formControlName]",inputs:["disabled","formControlName","ngModel"],outputs:["ngModelChange"]},{type:P.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{type:D.MatLabel,selector:"mat-label"},{type:q.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{type:w.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{type:w.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]}],pipes:{translate:P.TranslatePipe}}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Bt,decorators:[{type:o,args:[{selector:"tb-filter-node-check-relation-config",templateUrl:"./check-relation-config.component.html",styleUrls:[]}]}],ctorParameters:function(){return[{type:T.Store},{type:q.FormBuilder}]}});class jt extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.perimeterType=Ae,this.perimeterTypes=Object.keys(Ae),this.perimeterTypeTranslationMap=Ge,this.rangeUnits=Object.keys(Ee),this.rangeUnitTranslationMap=Pe}configForm(){return this.geoFilterConfigForm}onConfigurationSet(e){this.geoFilterConfigForm=this.fb.group({latitudeKeyName:[e?e.latitudeKeyName:null,[k.required]],longitudeKeyName:[e?e.longitudeKeyName:null,[k.required]],perimeterType:[e?e.perimeterType:null,[k.required]],fetchPerimeterInfoFromMessageMetadata:[!!e&&e.fetchPerimeterInfoFromMessageMetadata,[]],perimeterKeyName:[e?e.perimeterKeyName:null,[]],centerLatitude:[e?e.centerLatitude:null,[]],centerLongitude:[e?e.centerLatitude:null,[]],range:[e?e.range:null,[]],rangeUnit:[e?e.rangeUnit:null,[]],polygonsDefinition:[e?e.polygonsDefinition:null,[]]})}validatorTriggers(){return["fetchPerimeterInfoFromMessageMetadata","perimeterType"]}updateValidators(e){const t=this.geoFilterConfigForm.get("fetchPerimeterInfoFromMessageMetadata").value,o=this.geoFilterConfigForm.get("perimeterType").value;t?this.geoFilterConfigForm.get("perimeterKeyName").setValidators([k.required]):this.geoFilterConfigForm.get("perimeterKeyName").setValidators([]),t||o!==Ae.CIRCLE?(this.geoFilterConfigForm.get("centerLatitude").setValidators([]),this.geoFilterConfigForm.get("centerLongitude").setValidators([]),this.geoFilterConfigForm.get("range").setValidators([]),this.geoFilterConfigForm.get("rangeUnit").setValidators([])):(this.geoFilterConfigForm.get("centerLatitude").setValidators([k.required,k.min(-90),k.max(90)]),this.geoFilterConfigForm.get("centerLongitude").setValidators([k.required,k.min(-180),k.max(180)]),this.geoFilterConfigForm.get("range").setValidators([k.required,k.min(0)]),this.geoFilterConfigForm.get("rangeUnit").setValidators([k.required])),t||o!==Ae.POLYGON?this.geoFilterConfigForm.get("polygonsDefinition").setValidators([]):this.geoFilterConfigForm.get("polygonsDefinition").setValidators([k.required]),this.geoFilterConfigForm.get("perimeterKeyName").updateValueAndValidity({emitEvent:e}),this.geoFilterConfigForm.get("centerLatitude").updateValueAndValidity({emitEvent:e}),this.geoFilterConfigForm.get("centerLongitude").updateValueAndValidity({emitEvent:e}),this.geoFilterConfigForm.get("range").updateValueAndValidity({emitEvent:e}),this.geoFilterConfigForm.get("rangeUnit").updateValueAndValidity({emitEvent:e}),this.geoFilterConfigForm.get("polygonsDefinition").updateValueAndValidity({emitEvent:e})}}e("GpsGeoFilterConfigComponent",jt),jt.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:jt,deps:[{token:T.Store},{token:q.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),jt.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"12.0.0",version:"12.2.15",type:jt,selector:"tb-filter-node-gps-geofencing-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.latitude-key-name\n \n \n {{ \'tb.rulenode.latitude-key-name-required\' | translate }}\n \n \n \n tb.rulenode.longitude-key-name\n \n \n {{ \'tb.rulenode.longitude-key-name-required\' | translate }}\n \n \n \n tb.rulenode.perimeter-type\n \n \n {{ perimeterTypeTranslationMap.get(type) | translate }}\n \n \n \n \n {{ \'tb.rulenode.fetch-perimeter-info-from-message-metadata\' | translate }}\n \n \n tb.rulenode.perimeter-key-name\n \n \n {{ \'tb.rulenode.perimeter-key-name-required\' | translate }}\n \n \n
\n
\n \n tb.rulenode.circle-center-latitude\n \n \n {{ \'tb.rulenode.circle-center-latitude-required\' | translate }}\n \n \n \n tb.rulenode.circle-center-longitude\n \n \n {{ \'tb.rulenode.circle-center-longitude-required\' | translate }}\n \n \n
\n
\n \n tb.rulenode.range\n \n \n {{ \'tb.rulenode.range-required\' | translate }}\n \n \n \n tb.rulenode.range-units\n \n \n {{ rangeUnitTranslationMap.get(type) | translate }}\n \n \n \n
\n
\n
\n
\n \n tb.rulenode.polygon-definition\n \n \n {{ \'tb.rulenode.polygon-definition-required\' | translate }}\n \n \n
\n
\n
\n',components:[{type:D.MatFormField,selector:"mat-form-field",inputs:["color","floatLabel","appearance","hideRequiredMarker","hintLabel"],exportAs:["matFormField"]},{type:U.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex"],exportAs:["matSelect"]},{type:K.MatOption,selector:"mat-option",exportAs:["matOption"]},{type:V.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex","aria-label","aria-labelledby","id","labelPosition","name","required","checked","disabled","indeterminate","aria-describedby","value"],outputs:["change","indeterminateChange"],exportAs:["matCheckbox"]}],directives:[{type:E.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{type:q.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{type:q.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{type:D.MatLabel,selector:"mat-label"},{type:P.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{type:R.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["id","disabled","required","type","value","readonly","placeholder","errorStateMatcher","aria-describedby"],exportAs:["matInput"]},{type:q.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{type:q.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{type:q.FormControlName,selector:"[formControlName]",inputs:["disabled","formControlName","ngModel"],outputs:["ngModelChange"]},{type:q.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{type:w.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{type:D.MatError,selector:"mat-error",inputs:["id"]},{type:E.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{type:w.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{type:E.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{type:q.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{type:q.MaxValidator,selector:"input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]",inputs:["max"]},{type:q.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"}],pipes:{translate:P.TranslatePipe}}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:jt,decorators:[{type:o,args:[{selector:"tb-filter-node-gps-geofencing-config",templateUrl:"./gps-geo-filter-config.component.html",styleUrls:[]}]}],ctorParameters:function(){return[{type:T.Store},{type:q.FormBuilder}]}});class zt extends s{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.messageTypeConfigForm}onConfigurationSet(e){this.messageTypeConfigForm=this.fb.group({messageTypes:[e?e.messageTypes:null,[k.required]]})}}e("MessageTypeConfigComponent",zt),zt.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:zt,deps:[{token:T.Store},{token:q.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),zt.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"12.0.0",version:"12.2.15",type:zt,selector:"tb-filter-node-message-type-config",usesInheritance:!0,ngImport:t,template:'
\n \n
\n',components:[{type:kt,selector:"tb-message-types-config",inputs:["required","label","placeholder","disabled"]}],directives:[{type:E.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{type:q.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{type:q.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{type:q.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{type:q.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{type:q.FormControlName,selector:"[formControlName]",inputs:["disabled","formControlName","ngModel"],outputs:["ngModelChange"]}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:zt,decorators:[{type:o,args:[{selector:"tb-filter-node-message-type-config",templateUrl:"./message-type-config.component.html",styleUrls:[]}]}],ctorParameters:function(){return[{type:T.Store},{type:q.FormBuilder}]}});class _t extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.allowedEntityTypes=[x.DEVICE,x.ASSET,x.ENTITY_VIEW,x.TENANT,x.CUSTOMER,x.USER,x.DASHBOARD,x.RULE_CHAIN,x.RULE_NODE]}configForm(){return this.originatorTypeConfigForm}onConfigurationSet(e){this.originatorTypeConfigForm=this.fb.group({originatorTypes:[e?e.originatorTypes:null,[k.required]]})}}e("OriginatorTypeConfigComponent",_t),_t.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:_t,deps:[{token:T.Store},{token:q.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),_t.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"12.0.0",version:"12.2.15",type:_t,selector:"tb-filter-node-originator-type-config",usesInheritance:!0,ngImport:t,template:'
\n \n \n \n
\n',styles:[":host ::ng-deep tb-entity-type-list .mat-form-field-flex{padding-top:0}:host ::ng-deep tb-entity-type-list .mat-form-field-infix{border-top:0}\n"],components:[{type:Ie.EntityTypeListComponent,selector:"tb-entity-type-list",inputs:["required","disabled","allowedEntityTypes","ignoreAuthorityFilter"]}],directives:[{type:E.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{type:q.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{type:q.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{type:P.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{type:E.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{type:q.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{type:q.FormControlName,selector:"[formControlName]",inputs:["disabled","formControlName","ngModel"],outputs:["ngModelChange"]},{type:q.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:_t,decorators:[{type:o,args:[{selector:"tb-filter-node-originator-type-config",templateUrl:"./originator-type-config.component.html",styleUrls:["./originator-type-config.component.scss"]}]}],ctorParameters:function(){return[{type:T.Store},{type:q.FormBuilder}]}});class Qt extends s{constructor(e,t,o,r){super(e),this.store=e,this.fb=t,this.nodeScriptTestService=o,this.translate=r}configForm(){return this.scriptConfigForm}onConfigurationSet(e){this.scriptConfigForm=this.fb.group({jsScript:[e?e.jsScript:null,[k.required]]})}testScript(){const e=this.scriptConfigForm.get("jsScript").value;this.nodeScriptTestService.testNodeScript(e,"filter",this.translate.instant("tb.rulenode.filter"),"Filter",["msg","metadata","msgType"],this.ruleNodeId,"rulenode/filter_node_script_fn").subscribe((e=>{e&&this.scriptConfigForm.get("jsScript").setValue(e)}))}onValidate(){this.jsFuncComponent.validateOnSubmit()}}e("ScriptConfigComponent",Qt),Qt.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Qt,deps:[{token:T.Store},{token:q.FormBuilder},{token:Q.NodeScriptTestService},{token:P.TranslateService}],target:t.ɵɵFactoryTarget.Component}),Qt.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"12.0.0",version:"12.2.15",type:Qt,selector:"tb-filter-node-script-config",viewQueries:[{propertyName:"jsFuncComponent",first:!0,predicate:["jsFuncComponent"],descendants:!0,static:!0}],usesInheritance:!0,ngImport:t,template:'
\n \n \n \n
\n \n
\n
\n',components:[{type:Y.JsFuncComponent,selector:"tb-js-func",inputs:["functionTitle","functionName","functionArgs","validationArgs","resultType","disabled","fillHeight","minHeight","editorCompleter","globalVariables","disableUndefinedCheck","helpId","noValidate","required"]},{type:J.MatButton,selector:"button[mat-button], button[mat-raised-button], button[mat-icon-button], button[mat-fab], button[mat-mini-fab], button[mat-stroked-button], button[mat-flat-button]",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]}],directives:[{type:E.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{type:q.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{type:q.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{type:P.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{type:q.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{type:q.FormControlName,selector:"[formControlName]",inputs:["disabled","formControlName","ngModel"],outputs:["ngModelChange"]}],pipes:{translate:P.TranslatePipe}}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Qt,decorators:[{type:o,args:[{selector:"tb-filter-node-script-config",templateUrl:"./script-config.component.html",styleUrls:[]}]}],ctorParameters:function(){return[{type:T.Store},{type:q.FormBuilder},{type:Q.NodeScriptTestService},{type:P.TranslateService}]},propDecorators:{jsFuncComponent:[{type:a,args:["jsFuncComponent",{static:!0}]}]}});class $t extends s{constructor(e,t,o,r){super(e),this.store=e,this.fb=t,this.nodeScriptTestService=o,this.translate=r}configForm(){return this.switchConfigForm}onConfigurationSet(e){this.switchConfigForm=this.fb.group({jsScript:[e?e.jsScript:null,[k.required]]})}testScript(){const e=this.switchConfigForm.get("jsScript").value;this.nodeScriptTestService.testNodeScript(e,"switch",this.translate.instant("tb.rulenode.switch"),"Switch",["msg","metadata","msgType"],this.ruleNodeId,"rulenode/switch_node_script_fn").subscribe((e=>{e&&this.switchConfigForm.get("jsScript").setValue(e)}))}onValidate(){this.jsFuncComponent.validateOnSubmit()}}e("SwitchConfigComponent",$t),$t.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:$t,deps:[{token:T.Store},{token:q.FormBuilder},{token:Q.NodeScriptTestService},{token:P.TranslateService}],target:t.ɵɵFactoryTarget.Component}),$t.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"12.0.0",version:"12.2.15",type:$t,selector:"tb-filter-node-switch-config",viewQueries:[{propertyName:"jsFuncComponent",first:!0,predicate:["jsFuncComponent"],descendants:!0,static:!0}],usesInheritance:!0,ngImport:t,template:'
\n \n \n \n
\n \n
\n
\n',components:[{type:Y.JsFuncComponent,selector:"tb-js-func",inputs:["functionTitle","functionName","functionArgs","validationArgs","resultType","disabled","fillHeight","minHeight","editorCompleter","globalVariables","disableUndefinedCheck","helpId","noValidate","required"]},{type:J.MatButton,selector:"button[mat-button], button[mat-raised-button], button[mat-icon-button], button[mat-fab], button[mat-mini-fab], button[mat-stroked-button], button[mat-flat-button]",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]}],directives:[{type:E.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{type:q.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{type:q.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{type:P.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{type:q.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{type:q.FormControlName,selector:"[formControlName]",inputs:["disabled","formControlName","ngModel"],outputs:["ngModelChange"]}],pipes:{translate:P.TranslatePipe}}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:$t,decorators:[{type:o,args:[{selector:"tb-filter-node-switch-config",templateUrl:"./switch-config.component.html",styleUrls:[]}]}],ctorParameters:function(){return[{type:T.Store},{type:q.FormBuilder},{type:Q.NodeScriptTestService},{type:P.TranslateService}]},propDecorators:{jsFuncComponent:[{type:a,args:["jsFuncComponent",{static:!0}]}]}});class Wt{}e("RuleNodeCoreConfigFilterModule",Wt),Wt.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Wt,deps:[],target:t.ɵɵFactoryTarget.NgModule}),Wt.ɵmod=t.ɵɵngDeclareNgModule({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Wt,declarations:[Kt,Bt,jt,zt,_t,Qt,$t,Ut],imports:[O,F,Mt],exports:[Kt,Bt,jt,zt,_t,Qt,$t,Ut]}),Wt.ɵinj=t.ɵɵngDeclareInjector({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Wt,imports:[[O,F,Mt]]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Wt,decorators:[{type:i,args:[{declarations:[Kt,Bt,jt,zt,_t,Qt,$t,Ut],imports:[O,F,Mt],exports:[Kt,Bt,jt,zt,_t,Qt,$t,Ut]}]}]});class Yt extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.originatorSource=Me,this.originatorSources=Object.keys(Me),this.originatorSourceTranslationMap=Se}configForm(){return this.changeOriginatorConfigForm}onConfigurationSet(e){this.changeOriginatorConfigForm=this.fb.group({originatorSource:[e?e.originatorSource:null,[k.required]],relationsQuery:[e?e.relationsQuery:null,[]]})}validatorTriggers(){return["originatorSource"]}updateValidators(e){const t=this.changeOriginatorConfigForm.get("originatorSource").value;t&&t===Me.RELATED?this.changeOriginatorConfigForm.get("relationsQuery").setValidators([k.required]):this.changeOriginatorConfigForm.get("relationsQuery").setValidators([]),this.changeOriginatorConfigForm.get("relationsQuery").updateValueAndValidity({emitEvent:e})}}e("ChangeOriginatorConfigComponent",Yt),Yt.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Yt,deps:[{token:T.Store},{token:q.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),Yt.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"12.0.0",version:"12.2.15",type:Yt,selector:"tb-transformation-node-change-originator-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.originator-source\n \n \n {{ originatorSourceTranslationMap.get(source) | translate }}\n \n \n \n
\n \n \n \n
\n
\n',components:[{type:D.MatFormField,selector:"mat-form-field",inputs:["color","floatLabel","appearance","hideRequiredMarker","hintLabel"],exportAs:["matFormField"]},{type:U.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex"],exportAs:["matSelect"]},{type:K.MatOption,selector:"mat-option",exportAs:["matOption"]},{type:qt,selector:"tb-relations-query-config",inputs:["disabled","required"]}],directives:[{type:E.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{type:q.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{type:q.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{type:D.MatLabel,selector:"mat-label"},{type:P.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{type:q.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{type:q.FormControlName,selector:"[formControlName]",inputs:["disabled","formControlName","ngModel"],outputs:["ngModelChange"]},{type:q.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{type:w.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{type:w.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]}],pipes:{translate:P.TranslatePipe}}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Yt,decorators:[{type:o,args:[{selector:"tb-transformation-node-change-originator-config",templateUrl:"./change-originator-config.component.html",styleUrls:[]}]}],ctorParameters:function(){return[{type:T.Store},{type:q.FormBuilder}]}});class Jt extends s{constructor(e,t,o,r){super(e),this.store=e,this.fb=t,this.nodeScriptTestService=o,this.translate=r}configForm(){return this.scriptConfigForm}onConfigurationSet(e){this.scriptConfigForm=this.fb.group({jsScript:[e?e.jsScript:null,[k.required]]})}testScript(){const e=this.scriptConfigForm.get("jsScript").value;this.nodeScriptTestService.testNodeScript(e,"update",this.translate.instant("tb.rulenode.transformer"),"Transform",["msg","metadata","msgType"],this.ruleNodeId,"rulenode/transformation_node_script_fn").subscribe((e=>{e&&this.scriptConfigForm.get("jsScript").setValue(e)}))}onValidate(){this.jsFuncComponent.validateOnSubmit()}}e("TransformScriptConfigComponent",Jt),Jt.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Jt,deps:[{token:T.Store},{token:q.FormBuilder},{token:Q.NodeScriptTestService},{token:P.TranslateService}],target:t.ɵɵFactoryTarget.Component}),Jt.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"12.0.0",version:"12.2.15",type:Jt,selector:"tb-transformation-node-script-config",viewQueries:[{propertyName:"jsFuncComponent",first:!0,predicate:["jsFuncComponent"],descendants:!0,static:!0}],usesInheritance:!0,ngImport:t,template:'
\n \n \n \n
\n \n
\n
\n',components:[{type:Y.JsFuncComponent,selector:"tb-js-func",inputs:["functionTitle","functionName","functionArgs","validationArgs","resultType","disabled","fillHeight","minHeight","editorCompleter","globalVariables","disableUndefinedCheck","helpId","noValidate","required"]},{type:J.MatButton,selector:"button[mat-button], button[mat-raised-button], button[mat-icon-button], button[mat-fab], button[mat-mini-fab], button[mat-stroked-button], button[mat-flat-button]",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]}],directives:[{type:E.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{type:q.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{type:q.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{type:P.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{type:q.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{type:q.FormControlName,selector:"[formControlName]",inputs:["disabled","formControlName","ngModel"],outputs:["ngModelChange"]}],pipes:{translate:P.TranslatePipe}}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Jt,decorators:[{type:o,args:[{selector:"tb-transformation-node-script-config",templateUrl:"./script-config.component.html",styleUrls:[]}]}],ctorParameters:function(){return[{type:T.Store},{type:q.FormBuilder},{type:Q.NodeScriptTestService},{type:P.TranslateService}]},propDecorators:{jsFuncComponent:[{type:a,args:["jsFuncComponent",{static:!0}]}]}});class Zt extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.mailBodyTypes=[{name:"tb.mail-body-type.plain-text",value:"false"},{name:"tb.mail-body-type.html",value:"true"},{name:"tb.mail-body-type.dynamic",value:"dynamic"}]}configForm(){return this.toEmailConfigForm}onConfigurationSet(e){this.toEmailConfigForm=this.fb.group({fromTemplate:[e?e.fromTemplate:null,[k.required]],toTemplate:[e?e.toTemplate:null,[k.required]],ccTemplate:[e?e.ccTemplate:null,[]],bccTemplate:[e?e.bccTemplate:null,[]],subjectTemplate:[e?e.subjectTemplate:null,[k.required]],mailBodyType:[e?e.mailBodyType:null],isHtmlTemplate:[e?e.isHtmlTemplate:null],bodyTemplate:[e?e.bodyTemplate:null,[k.required]]}),this.toEmailConfigForm.get("mailBodyType").valueChanges.pipe(ue([null==e?void 0:e.subjectTemplate])).subscribe((e=>{"dynamic"===e?(this.toEmailConfigForm.get("isHtmlTemplate").patchValue("",{emitEvent:!1}),this.toEmailConfigForm.get("isHtmlTemplate").setValidators(k.required)):this.toEmailConfigForm.get("isHtmlTemplate").clearValidators(),this.toEmailConfigForm.get("isHtmlTemplate").updateValueAndValidity()}))}}e("ToEmailConfigComponent",Zt),Zt.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Zt,deps:[{token:T.Store},{token:q.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),Zt.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"12.0.0",version:"12.2.15",type:Zt,selector:"tb-transformation-node-to-email-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.from-template\n \n \n {{ \'tb.rulenode.from-template-required\' | translate }}\n \n \n \n \n tb.rulenode.to-template\n \n \n {{ \'tb.rulenode.to-template-required\' | translate }}\n \n \n \n \n tb.rulenode.cc-template\n \n \n \n \n tb.rulenode.bcc-template\n \n \n \n \n tb.rulenode.subject-template\n \n \n {{ \'tb.rulenode.subject-template-required\' | translate }}\n \n \n \n \n tb.rulenode.mail-body-type\n \n \n {{ type.name | translate }}\n \n \n \n \n tb.rulenode.dynamic-mail-body-type\n \n \n \n \n tb.rulenode.body-template\n \n \n {{ \'tb.rulenode.body-template-required\' | translate }}\n \n \n \n
\n',components:[{type:D.MatFormField,selector:"mat-form-field",inputs:["color","floatLabel","appearance","hideRequiredMarker","hintLabel"],exportAs:["matFormField"]},{type:U.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex"],exportAs:["matSelect"]},{type:K.MatOption,selector:"mat-option",exportAs:["matOption"]}],directives:[{type:E.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{type:q.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{type:q.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{type:D.MatLabel,selector:"mat-label"},{type:P.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{type:R.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["id","disabled","required","type","value","readonly","placeholder","errorStateMatcher","aria-describedby"],exportAs:["matInput"]},{type:q.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{type:q.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{type:q.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{type:q.FormControlName,selector:"[formControlName]",inputs:["disabled","formControlName","ngModel"],outputs:["ngModelChange"]},{type:w.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{type:D.MatError,selector:"mat-error",inputs:["id"]},{type:D.MatHint,selector:"mat-hint",inputs:["align","id"]},{type:w.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]}],pipes:{translate:P.TranslatePipe,safeHtml:Te}}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Zt,decorators:[{type:o,args:[{selector:"tb-transformation-node-to-email-config",templateUrl:"./to-email-config.component.html",styleUrls:[]}]}],ctorParameters:function(){return[{type:T.Store},{type:q.FormBuilder}]}});class Xt{}e("RulenodeCoreConfigTransformModule",Xt),Xt.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Xt,deps:[],target:t.ɵɵFactoryTarget.NgModule}),Xt.ɵmod=t.ɵɵngDeclareNgModule({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Xt,declarations:[Yt,Jt,Zt],imports:[O,F,Mt],exports:[Yt,Jt,Zt]}),Xt.ɵinj=t.ɵɵngDeclareInjector({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Xt,imports:[[O,F,Mt]]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:Xt,decorators:[{type:i,args:[{declarations:[Yt,Jt,Zt],imports:[O,F,Mt],exports:[Yt,Jt,Zt]}]}]});class eo extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.entityType=x}configForm(){return this.ruleChainInputConfigForm}onConfigurationSet(e){this.ruleChainInputConfigForm=this.fb.group({ruleChainId:[e?e.ruleChainId:null,[k.required]]})}}e("RuleChainInputComponent",eo),eo.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:eo,deps:[{token:T.Store},{token:q.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),eo.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"12.0.0",version:"12.2.15",type:eo,selector:"tb-flow-node-rule-chain-input-config",usesInheritance:!0,ngImport:t,template:'
\n \n \n
\n',components:[{type:ve.EntityAutocompleteComponent,selector:"tb-entity-autocomplete",inputs:["entityType","entitySubtype","excludeEntityIds","labelText","requiredText","required","disabled"],outputs:["entityChanged"]}],directives:[{type:E.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{type:q.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{type:q.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{type:q.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{type:q.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{type:q.FormControlName,selector:"[formControlName]",inputs:["disabled","formControlName","ngModel"],outputs:["ngModelChange"]}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:eo,decorators:[{type:o,args:[{selector:"tb-flow-node-rule-chain-input-config",templateUrl:"./rule-chain-input.component.html",styleUrls:[]}]}],ctorParameters:function(){return[{type:T.Store},{type:q.FormBuilder}]}});class to extends s{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.ruleChainOutputConfigForm}onConfigurationSet(e){this.ruleChainOutputConfigForm=this.fb.group({})}}e("RuleChainOutputComponent",to),to.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:to,deps:[{token:T.Store},{token:q.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),to.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"12.0.0",version:"12.2.15",type:to,selector:"tb-flow-node-rule-chain-output-config",usesInheritance:!0,ngImport:t,template:'
\n
\n
\n',directives:[{type:E.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{type:q.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{type:q.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]}],pipes:{translate:P.TranslatePipe}}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:to,decorators:[{type:o,args:[{selector:"tb-flow-node-rule-chain-output-config",templateUrl:"./rule-chain-output.component.html",styleUrls:[]}]}],ctorParameters:function(){return[{type:T.Store},{type:q.FormBuilder}]}});class oo{}e("RuleNodeCoreConfigFlowModule",oo),oo.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:oo,deps:[],target:t.ɵɵFactoryTarget.NgModule}),oo.ɵmod=t.ɵɵngDeclareNgModule({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:oo,declarations:[eo,to],imports:[O,F,Mt],exports:[eo,to]}),oo.ɵinj=t.ɵɵngDeclareInjector({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:oo,imports:[[O,F,Mt]]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:oo,decorators:[{type:i,args:[{declarations:[eo,to],imports:[O,F,Mt],exports:[eo,to]}]}]});class ro{constructor(e){!function(e){e.setTranslation("en_US",{tb:{rulenode:{"create-entity-if-not-exists":"Create new entity if not exists","create-entity-if-not-exists-hint":"Create a new entity set above if it does not exist.","entity-name-pattern":"Name pattern","entity-name-pattern-required":"Name pattern is required","entity-type-pattern":"Type pattern","entity-type-pattern-required":"Type pattern is required","entity-cache-expiration":"Entities cache expiration time (sec)","entity-cache-expiration-hint":"Specifies maximum time interval allowed to store found entity records. 0 value means that records will never expire.","entity-cache-expiration-required":"Entities cache expiration time is required.","entity-cache-expiration-range":"Entities cache expiration time should be greater than or equal to 0.","customer-name-pattern":"Customer name pattern","customer-name-pattern-required":"Customer name pattern is required","create-customer-if-not-exists":"Create new customer if not exists","customer-cache-expiration":"Customers cache expiration time (sec)","customer-cache-expiration-hint":"Specifies maximum time interval allowed to store found customer records. 0 value means that records will never expire.","customer-cache-expiration-required":"Customers cache expiration time is required.","customer-cache-expiration-range":"Customers cache expiration time should be greater than or equal to 0.","start-interval":"Start Interval","end-interval":"End Interval","start-interval-time-unit":"Start Interval Time Unit","end-interval-time-unit":"End Interval Time Unit","fetch-mode":"Fetch mode","fetch-mode-hint":"If selected fetch mode 'ALL' you able to choose telemetry sampling order.","order-by":"Order by","order-by-hint":"Select to choose telemetry sampling order.",limit:"Limit","limit-hint":"Min limit value is 2, max - 1000. In case you want to fetch a single entry, select fetch mode 'FIRST' or 'LAST'.","time-unit-milliseconds":"Milliseconds","time-unit-seconds":"Seconds","time-unit-minutes":"Minutes","time-unit-hours":"Hours","time-unit-days":"Days","time-value-range":"Time value should be in a range from 1 to 2147483647.","start-interval-value-required":"Start interval value is required.","end-interval-value-required":"End interval value is required.",filter:"Filter",switch:"Switch","message-type":"Message type","message-type-required":"Message type is required.","message-types-filter":"Message types filter","no-message-types-found":"No message types found","no-message-type-matching":"'{{messageType}}' not found.","create-new-message-type":"Create a new one!","message-types-required":"Message types are required.","client-attributes":"Client attributes","shared-attributes":"Shared attributes","server-attributes":"Server attributes","notify-device":"Notify Device","notify-device-hint":"If the message arrives from the device, we will push it back to the device by default.","latest-timeseries":"Latest timeseries","timeseries-key":"Timeseries key","data-keys":"Message data","metadata-keys":"Message metadata","relations-query":"Relations query","device-relations-query":"Device relations query","max-relation-level":"Max relation level","relation-type-pattern":"Relation type pattern","relation-type-pattern-required":"Relation type pattern is required","relation-types-list":"Relation types to propagate","relation-types-list-hint":"If Propagate relation types are not selected, alarms will be propagated without filtering by relation type.","unlimited-level":"Unlimited level","latest-telemetry":"Latest telemetry","attr-mapping":"Attributes mapping","source-attribute":"Source attribute","source-attribute-required":"Source attribute is required.","source-telemetry":"Source telemetry","source-telemetry-required":"Source telemetry is required.","target-attribute":"Target attribute","target-attribute-required":"Target attribute is required.","attr-mapping-required":"At least one attribute mapping should be specified.","fields-mapping":"Fields mapping","fields-mapping-required":"At least one field mapping should be specified.","source-field":"Source field","source-field-required":"Source field is required.","originator-source":"Originator source","originator-customer":"Customer","originator-tenant":"Tenant","originator-related":"Related","originator-alarm-originator":"Alarm Originator","clone-message":"Clone message",transform:"Transform","default-ttl":"Default TTL in seconds","default-ttl-required":"Default TTL is required.","min-default-ttl-message":"Only 0 minimum TTL is allowed.","message-count":"Message count (0 - unlimited)","message-count-required":"Message count is required.","min-message-count-message":"Only 0 minimum message count is allowed.","period-seconds":"Period in seconds","period-seconds-required":"Period is required.","use-metadata-period-in-seconds-patterns":"Use period in seconds pattern","use-metadata-period-in-seconds-patterns-hint":"If selected, rule node use period in seconds interval pattern from message metadata or data assuming that intervals are in the seconds.","period-in-seconds-pattern":"Period in seconds pattern","period-in-seconds-pattern-required":"Period in seconds pattern is required","min-period-seconds-message":"Only 1 second minimum period is allowed.",originator:"Originator","message-body":"Message body","message-metadata":"Message metadata",generate:"Generate","test-generator-function":"Test generator function",generator:"Generator","test-filter-function":"Test filter function","test-switch-function":"Test switch function","test-transformer-function":"Test transformer function",transformer:"Transformer","alarm-create-condition":"Alarm create condition","test-condition-function":"Test condition function","alarm-clear-condition":"Alarm clear condition","alarm-details-builder":"Alarm details builder","test-details-function":"Test details function","alarm-type":"Alarm type","alarm-type-required":"Alarm type is required.","alarm-severity":"Alarm severity","alarm-severity-required":"Alarm severity is required","alarm-severity-pattern":"Alarm severity pattern","alarm-status-filter":"Alarm status filter","alarm-status-list-empty":"Alarm status list is empty","no-alarm-status-matching":"No alarm status matching were found.",propagate:"Propagate alarm to related entities","propagate-to-owner":"Propagate alarm to entity owner (Customer or Tenant)","propagate-to-tenant":"Propagate alarm to Tenant",condition:"Condition",details:"Details","to-string":"To string","test-to-string-function":"Test to string function","from-template":"From Template","from-template-required":"From Template is required","to-template":"To Template","to-template-required":"To Template is required","mail-address-list-template-hint":'Comma separated address list, use ${metadataKey} for value from metadata, $[messageKey] for value from message body',"cc-template":"Cc Template","bcc-template":"Bcc Template","subject-template":"Subject Template","subject-template-required":"Subject Template is required","body-template":"Body Template","body-template-required":"Body Template is required","dynamic-mail-body-type":"Dynamic mail body type","mail-body-type":"Mail body type","request-id-metadata-attribute":"Request Id Metadata attribute name","timeout-sec":"Timeout in seconds","timeout-required":"Timeout is required","min-timeout-message":"Only 0 minimum timeout value is allowed.","endpoint-url-pattern":"Endpoint URL pattern","endpoint-url-pattern-required":"Endpoint URL pattern is required","request-method":"Request method","use-simple-client-http-factory":"Use simple client HTTP factory","ignore-request-body":"Without request body","read-timeout":"Read timeout in millis","read-timeout-hint":"The value of 0 means an infinite timeout","max-parallel-requests-count":"Max number of parallel requests","max-parallel-requests-count-hint":"The value of 0 specifies no limit in parallel processing",headers:"Headers","headers-hint":'Use ${metadataKey} for value from metadata, $[messageKey] for value from message body in header/value fields',header:"Header","header-required":"Header is required",value:"Value","value-required":"Value is required","topic-pattern":"Topic pattern","topic-pattern-required":"Topic pattern is required",topic:"Topic","topic-required":"Topic is required","bootstrap-servers":"Bootstrap servers","bootstrap-servers-required":"Bootstrap servers value is required","other-properties":"Other properties",key:"Key","key-required":"Key is required",retries:"Automatically retry times if fails","min-retries-message":"Only 0 minimum retries is allowed.","batch-size-bytes":"Produces batch size in bytes","min-batch-size-bytes-message":"Only 0 minimum batch size is allowed.","linger-ms":"Time to buffer locally (ms)","min-linger-ms-message":"Only 0 ms minimum value is allowed.","buffer-memory-bytes":"Client buffer max size in bytes","min-buffer-memory-message":"Only 0 minimum buffer size is allowed.",acks:"Number of acknowledgments","key-serializer":"Key serializer","key-serializer-required":"Key serializer is required","value-serializer":"Value serializer","value-serializer-required":"Value serializer is required","topic-arn-pattern":"Topic ARN pattern","topic-arn-pattern-required":"Topic ARN pattern is required","aws-access-key-id":"AWS Access Key ID","aws-access-key-id-required":"AWS Access Key ID is required","aws-secret-access-key":"AWS Secret Access Key","aws-secret-access-key-required":"AWS Secret Access Key is required","aws-region":"AWS Region","aws-region-required":"AWS Region is required","exchange-name-pattern":"Exchange name pattern","routing-key-pattern":"Routing key pattern","message-properties":"Message properties",host:"Host","host-required":"Host is required",port:"Port","port-required":"Port is required","port-range":"Port should be in a range from 1 to 65535.","virtual-host":"Virtual host",username:"Username",password:"Password","automatic-recovery":"Automatic recovery","connection-timeout-ms":"Connection timeout (ms)","min-connection-timeout-ms-message":"Only 0 ms minimum value is allowed.","handshake-timeout-ms":"Handshake timeout (ms)","min-handshake-timeout-ms-message":"Only 0 ms minimum value is allowed.","client-properties":"Client properties","queue-url-pattern":"Queue URL pattern","queue-url-pattern-required":"Queue URL pattern is required","delay-seconds":"Delay (seconds)","min-delay-seconds-message":"Only 0 seconds minimum value is allowed.","max-delay-seconds-message":"Only 900 seconds maximum value is allowed.",name:"Name","name-required":"Name is required","queue-type":"Queue type","sqs-queue-standard":"Standard","sqs-queue-fifo":"FIFO","gcp-project-id":"GCP project ID","gcp-project-id-required":"GCP project ID is required","gcp-service-account-key":"GCP service account key file","gcp-service-account-key-required":"GCP service account key file is required","pubsub-topic-name":"Topic name","pubsub-topic-name-required":"Topic name is required","message-attributes":"Message attributes","message-attributes-hint":'Use ${metadataKey} for value from metadata, $[messageKey] for value from message body in name/value fields',"connect-timeout":"Connection timeout (sec)","connect-timeout-required":"Connection timeout is required.","connect-timeout-range":"Connection timeout should be in a range from 1 to 200.","client-id":"Client ID","client-id-hint":'Hint: Optional. Leave empty for auto-generated Client ID. Be careful when specifying the Client ID. Majority of the MQTT brokers will not allow multiple connections with the same Client ID. To connect to such brokers, your mqtt Client ID must be unique. When platform is running in a micro-services mode, the copy of rule node is launched in each micro-service. This will automatically lead to multiple mqtt clients with the same ID and may cause failures of the rule node. To avoid such failures enable "Add Service ID as suffix to Client ID" option below.',"append-client-id-suffix":"Add Service ID as suffix to Client ID","client-id-suffix-hint":'Hint: Optional. Applied when "Client ID" specified explicitly. If selected then Service ID will be added to Client ID as a suffix. Helps to avoid failures when platform is running in a micro-services mode.',"device-id":"Device ID","device-id-required":"Device ID is required.","clean-session":"Clean session","enable-ssl":"Enable SSL",credentials:"Credentials","credentials-type":"Credentials type","credentials-type-required":"Credentials type is required.","credentials-anonymous":"Anonymous","credentials-basic":"Basic","credentials-pem":"PEM","credentials-pem-hint":"At least Server CA certificate file or a pair of Client certificate and Client private key files are required","credentials-sas":"Shared Access Signature","sas-key":"SAS Key","sas-key-required":"SAS Key is required.",hostname:"Hostname","hostname-required":"Hostname is required.","azure-ca-cert":"CA certificate file","username-required":"Username is required.","password-required":"Password is required.","ca-cert":"Server CA certificate file *","private-key":"Client private key file *",cert:"Client certificate file *","no-file":"No file selected.","drop-file":"Drop a file or click to select a file to upload.","private-key-password":"Private key password","use-system-smtp-settings":"Use system SMTP settings","use-metadata-interval-patterns":"Use interval patterns","use-metadata-interval-patterns-hint":"If selected, rule node use start and end interval patterns from message metadata or data assuming that intervals are in the milliseconds.","use-message-alarm-data":"Use message alarm data","overwrite-alarm-details":"Overwrite alarm details","use-alarm-severity-pattern":"Use alarm severity pattern","check-all-keys":"Check that all selected keys are present","check-all-keys-hint":"If selected, checks that all specified keys are present in the message data and metadata.","check-relation-to-specific-entity":"Check relation to specific entity","check-relation-hint":"Checks existence of relation to specific entity or to any entity based on direction and relation type.","delete-relation-to-specific-entity":"Delete relation to specific entity","delete-relation-hint":"Deletes relation from the originator of the incoming message to the specified entity or list of entities based on direction and type.","remove-current-relations":"Remove current relations","remove-current-relations-hint":"Removes current relations from the originator of the incoming message based on direction and type.","change-originator-to-related-entity":"Change originator to related entity","change-originator-to-related-entity-hint":"Used to process submitted message as a message from another entity.","start-interval-pattern":"Start interval pattern","end-interval-pattern":"End interval pattern","start-interval-pattern-required":"Start interval pattern is required","end-interval-pattern-required":"End interval pattern is required","smtp-protocol":"Protocol","smtp-host":"SMTP host","smtp-host-required":"SMTP host is required.","smtp-port":"SMTP port","smtp-port-required":"You must supply a smtp port.","smtp-port-range":"SMTP port should be in a range from 1 to 65535.","timeout-msec":"Timeout ms","min-timeout-msec-message":"Only 0 ms minimum value is allowed.","enter-username":"Enter username","enter-password":"Enter password","enable-tls":"Enable TLS","tls-version":"TLS version","enable-proxy":"Enable proxy","use-system-proxy-properties":"Use system proxy properties","proxy-host":"Proxy host","proxy-host-required":"Proxy host is required.","proxy-port":"Proxy port","proxy-port-required":"Proxy port is required.","proxy-port-range":"Proxy port should be in a range from 1 to 65535.","proxy-user":"Proxy user","proxy-password":"Proxy password","proxy-scheme":"Proxy scheme","numbers-to-template":"Phone Numbers To Template","numbers-to-template-required":"Phone Numbers To Template is required","numbers-to-template-hint":'Comma separated Phone Numbers, use ${metadataKey} for value from metadata, $[messageKey] for value from message body',"sms-message-template":"SMS message Template","sms-message-template-required":"SMS message Template is required","use-system-sms-settings":"Use system SMS provider settings","min-period-0-seconds-message":"Only 0 second minimum period is allowed.","max-pending-messages":"Maximum pending messages","max-pending-messages-required":"Maximum pending messages is required.","max-pending-messages-range":"Maximum pending messages should be in a range from 1 to 100000.","originator-types-filter":"Originator types filter","interval-seconds":"Interval in seconds","interval-seconds-required":"Interval is required.","min-interval-seconds-message":"Only 1 second minimum interval is allowed.","output-timeseries-key-prefix":"Output timeseries key prefix","output-timeseries-key-prefix-required":"Output timeseries key prefix required.","separator-hint":'You should press "enter" to complete field input.',"entity-details":"Select entity details:","entity-details-title":"Title","entity-details-country":"Country","entity-details-state":"State","entity-details-city":"City","entity-details-zip":"Zip","entity-details-address":"Address","entity-details-address2":"Address2","entity-details-additional_info":"Additional Info","entity-details-phone":"Phone","entity-details-email":"Email","add-to-metadata":"Add selected details to message metadata","add-to-metadata-hint":"If selected, adds the selected details keys to the message metadata instead of message data.","entity-details-list-empty":"No entity details selected.","no-entity-details-matching":"No entity details matching were found.","custom-table-name":"Custom table name","custom-table-name-required":"Table Name is required","custom-table-hint":"You should enter the table name without prefix 'cs_tb_'.","message-field":"Message field","message-field-required":"Message field is required.","table-col":"Table column","table-col-required":"Table column is required.","latitude-key-name":"Latitude key name","longitude-key-name":"Longitude key name","latitude-key-name-required":"Latitude key name is required.","longitude-key-name-required":"Longitude key name is required.","fetch-perimeter-info-from-message-metadata":"Fetch perimeter information from message metadata","perimeter-key-name":"Perimeter key name","perimeter-key-name-required":"Perimeter key name is required.","perimeter-circle":"Circle","perimeter-polygon":"Polygon","perimeter-type":"Perimeter type","circle-center-latitude":"Center latitude","circle-center-latitude-required":"Center latitude is required.","circle-center-longitude":"Center longitude","circle-center-longitude-required":"Center longitude is required.","range-unit-meter":"Meter","range-unit-kilometer":"Kilometer","range-unit-foot":"Foot","range-unit-mile":"Mile","range-unit-nautical-mile":"Nautical mile","range-units":"Range units",range:"Range","range-required":"Range is required.","polygon-definition":"Polygon definition","polygon-definition-required":"Polygon definition is required.","polygon-definition-hint":"Please, use the following format for manual definition of polygon: [[lat1,lon1],[lat2,lon2], ... ,[latN,lonN]].","min-inside-duration":"Minimal inside duration","min-inside-duration-value-required":"Minimal inside duration is required","min-inside-duration-time-unit":"Minimal inside duration time unit","min-outside-duration":"Minimal outside duration","min-outside-duration-value-required":"Minimal outside duration is required","min-outside-duration-time-unit":"Minimal outside duration time unit","tell-failure-if-absent":"Tell Failure","tell-failure-if-absent-hint":'If at least one selected key doesn\'t exist the outbound message will report "Failure".',"get-latest-value-with-ts":"Fetch Latest telemetry with Timestamp","get-latest-value-with-ts-hint":'If selected, latest telemetry values will be added to the outbound message metadata with timestamp, e.g: "temp": "{"ts":1574329385897, "value":42}"',"use-redis-queue":"Use redis queue for message persistence","trim-redis-queue":"Trim redis queue","redis-queue-max-size":"Redis queue max size","add-metadata-key-values-as-kafka-headers":"Add Message metadata key-value pairs to Kafka record headers","add-metadata-key-values-as-kafka-headers-hint":"If selected, key-value pairs from message metadata will be added to the outgoing records headers as byte arrays with predefined charset encoding.","charset-encoding":"Charset encoding","charset-encoding-required":"Charset encoding is required.","charset-us-ascii":"US-ASCII","charset-iso-8859-1":"ISO-8859-1","charset-utf-8":"UTF-8","charset-utf-16be":"UTF-16BE","charset-utf-16le":"UTF-16LE","charset-utf-16":"UTF-16","select-queue-hint":"The queue name can be selected from a drop-down list or add a custom name.","persist-alarm-rules":"Persist state of alarm rules","fetch-alarm-rules":"Fetch state of alarm rules","input-value-key":"Input value key","input-value-key-required":"Input value key is required.","output-value-key":"Output value key","output-value-key-required":"Output value key is required.",round:"Decimals","round-range":"Decimals should be in a range from 0 to 15.","use-cache":"Use cache for latest value","tell-failure-if-delta-is-negative":"Tell Failure if delta is negative","add-period-between-msgs":"Add period between messages","period-value-key":"Period value key","period-value-key-required":"Period value key is required.","general-pattern-hint":'Hint: use ${metadataKey} for value from metadata, $[messageKey] for value from message body',"alarm-severity-pattern-hint":'Hint: use ${metadataKey} for value from metadata, $[messageKey] for value from message body. Alarm severity should be system (CRITICAL, MAJOR etc.)',"output-node-name-hint":"The rule node name corresponds to the relation type of the output message, and it is used to forward messages to other rule nodes in the caller rule chain.","skip-latest-persistence":"Skip latest persistence","use-server-ts":"Use server ts","use-server-ts-hint":"Enable this setting to use the timestamp of the message processing instead of the timestamp from the message. Useful for all sorts of sequential processing if you merge messages from multiple sources (devices, assets, etc).","kv-map-pattern-hint":'Hint: use ${metadataKey} for value from metadata, $[messageKey] for value from message body to substitute "Source" and "Target" key names'},"key-val":{key:"Key",value:"Value","remove-entry":"Remove entry","add-entry":"Add entry"},"mail-body-type":{"plain-text":"Plain Text",html:"HTML",dynamic:"Dynamic"}}},!0)}(e)}}e("RuleNodeCoreConfigModule",ro),ro.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:ro,deps:[{token:P.TranslateService}],target:t.ɵɵFactoryTarget.NgModule}),ro.ɵmod=t.ɵɵngDeclareNgModule({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:ro,declarations:[Ne],imports:[O,F],exports:[St,Wt,Ht,Xt,oo,Ne]}),ro.ɵinj=t.ɵɵngDeclareInjector({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:ro,imports:[[O,F],St,Wt,Ht,Xt,oo]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"12.2.15",ngImport:t,type:ro,decorators:[{type:i,args:[{declarations:[Ne],imports:[O,F],exports:[St,Wt,Ht,Xt,oo,Ne]}]}],ctorParameters:function(){return[{type:P.TranslateService}]}})}}}));//# sourceMappingURL=rulenode-core-config.js.map From 979603b733ba3e7cc74a26b4d45526ab62fb61fb Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Thu, 2 Jun 2022 12:42:13 +0300 Subject: [PATCH 749/798] refactoring: comments8 (ruleChain) --- .../server/controller/RuleChainController.java | 2 +- .../entitiy/DefaultTbNotificationEntityService.java | 2 +- .../server/service/rule/DefaultTbRuleChainService.java | 10 +++++----- .../server/service/rule/TbRuleChainService.java | 4 ++-- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java b/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java index b4ca069382..190d68e79e 100644 --- a/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java +++ b/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java @@ -553,7 +553,7 @@ public class RuleChainController extends BaseController { RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId)); RuleChain ruleChain = checkRuleChain(ruleChainId, Operation.READ); - return tbRuleChainService.unassignRuleChainToEdge(getTenantId(), ruleChain, edge, getCurrentUser()); + return tbRuleChainService.unassignRuleChainFromEdge(getTenantId(), ruleChain, edge, getCurrentUser()); } @ApiOperation(value = "Get Edge Rule Chains (getEdgeRuleChains)", diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/DefaultTbNotificationEntityService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/DefaultTbNotificationEntityService.java index d61459a294..3361b75d23 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/DefaultTbNotificationEntityService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/DefaultTbNotificationEntityService.java @@ -101,7 +101,7 @@ public class DefaultTbNotificationEntityService implements TbNotificationEntityS @Override public void notifySendMsgToEdgeService(TenantId tenantId, I entityId, EdgeEventActionType edgeEventActionType) { - sendEntityNotificationMsg(tenantId, entityId, edgeEventActionType); + sendEntityNotificationMsg(tenantId, entityId, edgeEventActionType); } @Override diff --git a/application/src/main/java/org/thingsboard/server/service/rule/DefaultTbRuleChainService.java b/application/src/main/java/org/thingsboard/server/service/rule/DefaultTbRuleChainService.java index b54578107f..ac82f78ccb 100644 --- a/application/src/main/java/org/thingsboard/server/service/rule/DefaultTbRuleChainService.java +++ b/application/src/main/java/org/thingsboard/server/service/rule/DefaultTbRuleChainService.java @@ -174,9 +174,9 @@ public class DefaultTbRuleChainService extends AbstractTbEntityService implement tbClusterService.broadcastEntityStateChangeEvent(tenantId, savedRuleChain.getId(), actionType.equals(ActionType.ADDED) ? ComponentLifecycleEvent.CREATED : ComponentLifecycleEvent.UPDATED); } - boolean isSendMsg = RuleChainType.EDGE.equals(savedRuleChain.getType()) && actionType.equals(ActionType.UPDATED); + boolean sendMsgToEdge = RuleChainType.EDGE.equals(savedRuleChain.getType()) && actionType.equals(ActionType.UPDATED); notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, null, savedRuleChain.getId(), - savedRuleChain, user, actionType, isSendMsg, null); + savedRuleChain, user, actionType, sendMsgToEdge, null); return savedRuleChain; } catch (Exception e) { notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, null, emptyId(EntityType.RULE_CHAIN), @@ -237,12 +237,12 @@ public class DefaultTbRuleChainService extends AbstractTbEntityService implement @Override public RuleChain setRootRuleChain(TenantId tenantId, RuleChain ruleChain, SecurityUser user) throws ThingsboardException { - RuleChain previousRootRuleChain = ruleChainService.getRootTenantRuleChain(tenantId); - RuleChainId previousRootRuleChainId = previousRootRuleChain.getId(); RuleChainId ruleChainId = ruleChain.getId(); try { + RuleChain previousRootRuleChain = ruleChainService.getRootTenantRuleChain(tenantId); if (ruleChainService.setRootRuleChain(tenantId, ruleChainId)) { if (previousRootRuleChain != null) { + RuleChainId previousRootRuleChainId = previousRootRuleChain.getId(); previousRootRuleChain = ruleChainService.findRuleChainById(tenantId, previousRootRuleChainId); tbClusterService.broadcastEntityStateChangeEvent(tenantId, previousRootRuleChainId, @@ -332,7 +332,7 @@ public class DefaultTbRuleChainService extends AbstractTbEntityService implement } @Override - public RuleChain unassignRuleChainToEdge(TenantId tenantId, RuleChain ruleChain, Edge edge, SecurityUser user) throws ThingsboardException { + public RuleChain unassignRuleChainFromEdge(TenantId tenantId, RuleChain ruleChain, Edge edge, SecurityUser user) throws ThingsboardException { RuleChainId ruleChainId = ruleChain.getId(); try { RuleChain savedRuleChain = checkNotNull(ruleChainService.unassignRuleChainFromEdge(tenantId, ruleChainId, edge.getId(), false)); diff --git a/application/src/main/java/org/thingsboard/server/service/rule/TbRuleChainService.java b/application/src/main/java/org/thingsboard/server/service/rule/TbRuleChainService.java index c4c1030d8b..dbdba7d4ef 100644 --- a/application/src/main/java/org/thingsboard/server/service/rule/TbRuleChainService.java +++ b/application/src/main/java/org/thingsboard/server/service/rule/TbRuleChainService.java @@ -47,8 +47,8 @@ public interface TbRuleChainService extends SimpleTbEntityService { RuleChain assignRuleChainToEdge(TenantId tenantId, RuleChain ruleChain, Edge edge, SecurityUser user) throws ThingsboardException; - RuleChain unassignRuleChainToEdge(TenantId tenantId, RuleChain ruleChain, Edge edge, - SecurityUser user) throws ThingsboardException; + RuleChain unassignRuleChainFromEdge(TenantId tenantId, RuleChain ruleChain, Edge edge, + SecurityUser user) throws ThingsboardException; RuleChain setEdgeTemplateRootRuleChain(TenantId tenantId, RuleChain ruleChain, SecurityUser user) throws ThingsboardException; From d4bf9dd5d954f6020f9a323ae5231ba114545d97 Mon Sep 17 00:00:00 2001 From: Andrii Shvaika Date: Thu, 2 Jun 2022 13:05:17 +0300 Subject: [PATCH 750/798] Adding @TbCoreComponent to the 2FA services to fix rule-engine msa --- .../server/config/ThingsboardSecurityConfiguration.java | 6 ++---- .../server/install/ThingsboardInstallConfiguration.java | 5 ++++- .../security/auth/oauth2/AppleOAuth2ClientMapper.java | 2 ++ .../security/auth/oauth2/BasicOAuth2ClientMapper.java | 2 ++ .../security/auth/oauth2/CustomOAuth2ClientMapper.java | 2 ++ .../security/auth/oauth2/GithubOAuth2ClientMapper.java | 2 ++ .../security/auth/oauth2/OAuth2ClientMapperProvider.java | 2 ++ .../auth/oauth2/Oauth2AuthenticationFailureHandler.java | 2 ++ .../auth/oauth2/Oauth2AuthenticationSuccessHandler.java | 2 ++ .../security/auth/rest/RestAuthenticationProvider.java | 2 ++ .../security/system/DefaultSystemSecurityService.java | 2 ++ .../thingsboard/server/dao/audit/AuditLogLevelFilter.java | 7 ++++++- .../server/dao/audit}/AuditLogLevelProperties.java | 2 +- .../thingsboard/server/dao/audit/AuditLogServiceImpl.java | 1 + .../server/dao/service/AbstractServiceTest.java | 5 ++++- docker/.gitignore | 1 + 16 files changed, 37 insertions(+), 8 deletions(-) rename {application/src/main/java/org/thingsboard/server/config => dao/src/main/java/org/thingsboard/server/dao/audit}/AuditLogLevelProperties.java (96%) diff --git a/application/src/main/java/org/thingsboard/server/config/ThingsboardSecurityConfiguration.java b/application/src/main/java/org/thingsboard/server/config/ThingsboardSecurityConfiguration.java index 3e9597a72d..823bbf35e3 100644 --- a/application/src/main/java/org/thingsboard/server/config/ThingsboardSecurityConfiguration.java +++ b/application/src/main/java/org/thingsboard/server/config/ThingsboardSecurityConfiguration.java @@ -42,6 +42,7 @@ import org.springframework.web.filter.CorsFilter; import org.thingsboard.server.dao.audit.AuditLogLevelFilter; import org.thingsboard.server.dao.oauth2.OAuth2Configuration; import org.thingsboard.server.exception.ThingsboardErrorResponseHandler; +import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.security.auth.jwt.JwtAuthenticationProvider; import org.thingsboard.server.service.security.auth.jwt.JwtTokenAuthenticationProcessingFilter; import org.thingsboard.server.service.security.auth.jwt.RefreshTokenAuthenticationProvider; @@ -61,6 +62,7 @@ import java.util.List; @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled=true) @Order(SecurityProperties.BASIC_AUTH_ORDER) +@TbCoreComponent public class ThingsboardSecurityConfiguration extends WebSecurityConfigurerAdapter { public static final String JWT_TOKEN_HEADER_PARAM = "X-Authorization"; @@ -241,8 +243,4 @@ public class ThingsboardSecurityConfiguration extends WebSecurityConfigurerAdapt } } - @Bean - public AuditLogLevelFilter auditLogLevelFilter(@Autowired AuditLogLevelProperties auditLogLevelProperties) { - return new AuditLogLevelFilter(auditLogLevelProperties.getMask()); - } } diff --git a/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallConfiguration.java b/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallConfiguration.java index a1f1fcf0e3..4338f4aa44 100644 --- a/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallConfiguration.java +++ b/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallConfiguration.java @@ -19,6 +19,7 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Profile; import org.thingsboard.server.dao.audit.AuditLogLevelFilter; +import org.thingsboard.server.dao.audit.AuditLogLevelProperties; import java.util.HashMap; @@ -28,6 +29,8 @@ public class ThingsboardInstallConfiguration { @Bean public AuditLogLevelFilter emptyAuditLogLevelFilter() { - return new AuditLogLevelFilter(new HashMap<>()); + var props = new AuditLogLevelProperties(); + props.setMask(new HashMap<>()); + return new AuditLogLevelFilter(props); } } diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/AppleOAuth2ClientMapper.java b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/AppleOAuth2ClientMapper.java index a02d8a4441..77b9a94aaa 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/AppleOAuth2ClientMapper.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/AppleOAuth2ClientMapper.java @@ -26,6 +26,7 @@ import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.oauth2.OAuth2MapperConfig; import org.thingsboard.server.common.data.oauth2.OAuth2Registration; import org.thingsboard.server.dao.oauth2.OAuth2User; +import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.security.model.SecurityUser; import javax.servlet.http.HttpServletRequest; @@ -34,6 +35,7 @@ import java.util.Map; @Service(value = "appleOAuth2ClientMapper") @Slf4j +@TbCoreComponent public class AppleOAuth2ClientMapper extends AbstractOAuth2ClientMapper implements OAuth2ClientMapper { private static final String USER = "user"; diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/BasicOAuth2ClientMapper.java b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/BasicOAuth2ClientMapper.java index bfab031de8..42f44a712d 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/BasicOAuth2ClientMapper.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/BasicOAuth2ClientMapper.java @@ -21,6 +21,7 @@ import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.oauth2.OAuth2MapperConfig; import org.thingsboard.server.common.data.oauth2.OAuth2Registration; import org.thingsboard.server.dao.oauth2.OAuth2User; +import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.security.model.SecurityUser; import javax.servlet.http.HttpServletRequest; @@ -28,6 +29,7 @@ import java.util.Map; @Service(value = "basicOAuth2ClientMapper") @Slf4j +@TbCoreComponent public class BasicOAuth2ClientMapper extends AbstractOAuth2ClientMapper implements OAuth2ClientMapper { @Override diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/CustomOAuth2ClientMapper.java b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/CustomOAuth2ClientMapper.java index 1f075f4b44..39438b997d 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/CustomOAuth2ClientMapper.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/CustomOAuth2ClientMapper.java @@ -28,6 +28,7 @@ import org.thingsboard.server.common.data.oauth2.OAuth2CustomMapperConfig; import org.thingsboard.server.common.data.oauth2.OAuth2MapperConfig; import org.thingsboard.server.common.data.oauth2.OAuth2Registration; import org.thingsboard.server.dao.oauth2.OAuth2User; +import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.security.model.SecurityUser; import javax.annotation.PostConstruct; @@ -35,6 +36,7 @@ import javax.servlet.http.HttpServletRequest; @Service(value = "customOAuth2ClientMapper") @Slf4j +@TbCoreComponent public class CustomOAuth2ClientMapper extends AbstractOAuth2ClientMapper implements OAuth2ClientMapper { private static final String PROVIDER_ACCESS_TOKEN = "provider-access-token"; diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/GithubOAuth2ClientMapper.java b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/GithubOAuth2ClientMapper.java index 45d75f164e..d4d4d99005 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/GithubOAuth2ClientMapper.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/GithubOAuth2ClientMapper.java @@ -27,6 +27,7 @@ import org.thingsboard.server.common.data.oauth2.OAuth2MapperConfig; import org.thingsboard.server.common.data.oauth2.OAuth2Registration; import org.thingsboard.server.dao.oauth2.OAuth2Configuration; import org.thingsboard.server.dao.oauth2.OAuth2User; +import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.security.model.SecurityUser; import javax.servlet.http.HttpServletRequest; @@ -36,6 +37,7 @@ import java.util.Optional; @Service(value = "githubOAuth2ClientMapper") @Slf4j +@TbCoreComponent public class GithubOAuth2ClientMapper extends AbstractOAuth2ClientMapper implements OAuth2ClientMapper { private static final String EMAIL_URL_KEY = "emailUrl"; diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/OAuth2ClientMapperProvider.java b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/OAuth2ClientMapperProvider.java index e843d6d907..3c1ac4a5c6 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/OAuth2ClientMapperProvider.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/OAuth2ClientMapperProvider.java @@ -20,9 +20,11 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.oauth2.MapperType; +import org.thingsboard.server.queue.util.TbCoreComponent; @Component @Slf4j +@TbCoreComponent public class OAuth2ClientMapperProvider { @Autowired diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/Oauth2AuthenticationFailureHandler.java b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/Oauth2AuthenticationFailureHandler.java index c8b4dad02d..c12c6081f6 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/Oauth2AuthenticationFailureHandler.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/Oauth2AuthenticationFailureHandler.java @@ -25,6 +25,7 @@ import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.security.system.SystemSecurityService; import org.thingsboard.server.utils.MiscUtils; @@ -35,6 +36,7 @@ import java.io.IOException; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; +@TbCoreComponent @Component(value = "oauth2AuthenticationFailureHandler") public class Oauth2AuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler { diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/Oauth2AuthenticationSuccessHandler.java b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/Oauth2AuthenticationSuccessHandler.java index eab858ef3e..e2a78eb605 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/Oauth2AuthenticationSuccessHandler.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/Oauth2AuthenticationSuccessHandler.java @@ -31,6 +31,7 @@ import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.oauth2.OAuth2Registration; import org.thingsboard.server.common.data.security.model.JwtToken; import org.thingsboard.server.dao.oauth2.OAuth2Service; +import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.security.auth.jwt.RefreshTokenRepository; import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.security.model.token.JwtTokenFactory; @@ -45,6 +46,7 @@ import java.util.UUID; @Slf4j @Component(value = "oauth2AuthenticationSuccessHandler") +@TbCoreComponent public class Oauth2AuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler { private final JwtTokenFactory tokenFactory; diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/rest/RestAuthenticationProvider.java b/application/src/main/java/org/thingsboard/server/service/security/auth/rest/RestAuthenticationProvider.java index cb12ada559..b3ef88dbff 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/rest/RestAuthenticationProvider.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/rest/RestAuthenticationProvider.java @@ -38,6 +38,7 @@ import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.common.data.security.UserCredentials; import org.thingsboard.server.dao.customer.CustomerService; import org.thingsboard.server.dao.user.UserService; +import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.security.auth.MfaAuthenticationToken; import org.thingsboard.server.service.security.auth.mfa.TwoFactorAuthService; import org.thingsboard.server.service.security.model.SecurityUser; @@ -49,6 +50,7 @@ import java.util.UUID; @Component @Slf4j +@TbCoreComponent public class RestAuthenticationProvider implements AuthenticationProvider { private final SystemSecurityService systemSecurityService; diff --git a/application/src/main/java/org/thingsboard/server/service/security/system/DefaultSystemSecurityService.java b/application/src/main/java/org/thingsboard/server/service/security/system/DefaultSystemSecurityService.java index 65b89c85b8..32a0ff9e70 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/system/DefaultSystemSecurityService.java +++ b/application/src/main/java/org/thingsboard/server/service/security/system/DefaultSystemSecurityService.java @@ -55,6 +55,7 @@ import org.thingsboard.server.dao.settings.AdminSettingsService; import org.thingsboard.server.dao.user.UserService; import org.thingsboard.server.dao.user.UserServiceImpl; import org.thingsboard.server.common.data.security.model.mfa.PlatformTwoFaSettings; +import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.security.auth.rest.RestAuthenticationDetails; import org.thingsboard.server.service.security.exception.UserPasswordExpiredException; import org.thingsboard.server.service.security.model.SecurityUser; @@ -72,6 +73,7 @@ import static org.thingsboard.server.common.data.CacheConstants.SECURITY_SETTING @Service @Slf4j +@TbCoreComponent public class DefaultSystemSecurityService implements SystemSecurityService { @Autowired diff --git a/dao/src/main/java/org/thingsboard/server/dao/audit/AuditLogLevelFilter.java b/dao/src/main/java/org/thingsboard/server/dao/audit/AuditLogLevelFilter.java index 8ecda6fe42..5bdf856223 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/audit/AuditLogLevelFilter.java +++ b/dao/src/main/java/org/thingsboard/server/dao/audit/AuditLogLevelFilter.java @@ -15,6 +15,8 @@ */ package org.thingsboard.server.dao.audit; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.audit.ActionType; @@ -22,11 +24,14 @@ import java.util.HashMap; import java.util.Locale; import java.util.Map; +@Component +@ConditionalOnProperty(prefix = "audit-log", value = "enabled", havingValue = "true") public class AuditLogLevelFilter { private Map entityTypeMask = new HashMap<>(); - public AuditLogLevelFilter(Map mask) { + public AuditLogLevelFilter(AuditLogLevelProperties auditLogLevelProperties) { + Map mask = auditLogLevelProperties.getMask(); entityTypeMask.clear(); mask.forEach((entityTypeStr, logLevelMaskStr) -> { EntityType entityType = EntityType.valueOf(entityTypeStr.toUpperCase(Locale.ENGLISH)); diff --git a/application/src/main/java/org/thingsboard/server/config/AuditLogLevelProperties.java b/dao/src/main/java/org/thingsboard/server/dao/audit/AuditLogLevelProperties.java similarity index 96% rename from application/src/main/java/org/thingsboard/server/config/AuditLogLevelProperties.java rename to dao/src/main/java/org/thingsboard/server/dao/audit/AuditLogLevelProperties.java index d492fb365e..82556759f2 100644 --- a/application/src/main/java/org/thingsboard/server/config/AuditLogLevelProperties.java +++ b/dao/src/main/java/org/thingsboard/server/dao/audit/AuditLogLevelProperties.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.config; +package org.thingsboard.server.dao.audit; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Configuration; diff --git a/dao/src/main/java/org/thingsboard/server/dao/audit/AuditLogServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/audit/AuditLogServiceImpl.java index 6e26f4343d..e88c37d213 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/audit/AuditLogServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/audit/AuditLogServiceImpl.java @@ -25,6 +25,7 @@ import com.google.common.util.concurrent.ListenableFuture; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.annotation.Bean; import org.springframework.stereotype.Service; import org.springframework.util.StringUtils; import org.thingsboard.common.util.JacksonUtil; diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/AbstractServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/AbstractServiceTest.java index c1ecafda6e..b06e354321 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/AbstractServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/AbstractServiceTest.java @@ -49,6 +49,7 @@ import org.thingsboard.server.dao.alarm.AlarmService; import org.thingsboard.server.dao.asset.AssetService; import org.thingsboard.server.dao.audit.AuditLogLevelFilter; import org.thingsboard.server.dao.audit.AuditLogLevelMask; +import org.thingsboard.server.dao.audit.AuditLogLevelProperties; import org.thingsboard.server.dao.component.ComponentDescriptorService; import org.thingsboard.server.dao.customer.CustomerService; import org.thingsboard.server.dao.dashboard.DashboardService; @@ -218,7 +219,9 @@ public abstract class AbstractServiceTest { for (EntityType entityType : EntityType.values()) { mask.put(entityType.name().toLowerCase(), AuditLogLevelMask.RW.name()); } - return new AuditLogLevelFilter(mask); + var props = new AuditLogLevelProperties(); + props.setMask(mask); + return new AuditLogLevelFilter(props); } protected DeviceProfile createDeviceProfile(TenantId tenantId, String name) { diff --git a/docker/.gitignore b/docker/.gitignore index eee422d573..e5313d17cf 100644 --- a/docker/.gitignore +++ b/docker/.gitignore @@ -5,4 +5,5 @@ tb-node/db/** tb-node/postgres/** tb-node/cassandra/** tb-transports/*/log +docker/tb-vc-executor/log/** !.env From 0e9acbec7ad9ee4a3c71ab4e5f8c28ee788d881b Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Thu, 2 Jun 2022 17:39:19 +0300 Subject: [PATCH 751/798] Improve upgrade scripts --- .../install/ThingsboardInstallService.java | 1 + .../DefaultSystemDataLoaderService.java | 133 +++++++------ .../install/SqlDatabaseUpgradeService.java | 107 ----------- .../update/DefaultDataUpdateService.java | 176 +++++++++++++++++- .../server/dao/rule/RuleChainService.java | 2 + .../server/dao/rule/BaseRuleChainService.java | 11 +- .../server/dao/rule/RuleNodeDao.java | 4 + .../server/dao/sql/rule/JpaRuleNodeDao.java | 13 ++ .../dao/sql/rule/RuleNodeRepository.java | 7 + 9 files changed, 279 insertions(+), 175 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java b/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java index 5ed8f588e2..40e15c7b0e 100644 --- a/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java +++ b/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java @@ -220,6 +220,7 @@ public class ThingsboardInstallService { case "3.3.4": log.info("Upgrading ThingsBoard from version 3.3.4 to 3.4.0 ..."); databaseEntitiesUpgradeService.upgradeDatabase("3.3.4"); + dataUpdateService.updateData("3.3.4"); log.info("Updating system data..."); systemDataLoaderService.updateSystemWidgets(); break; diff --git a/application/src/main/java/org/thingsboard/server/service/install/DefaultSystemDataLoaderService.java b/application/src/main/java/org/thingsboard/server/service/install/DefaultSystemDataLoaderService.java index d22660969d..cece3377b0 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/DefaultSystemDataLoaderService.java +++ b/application/src/main/java/org/thingsboard/server/service/install/DefaultSystemDataLoaderService.java @@ -611,67 +611,76 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { @Override public void createQueues() { - Queue mainQueue = new Queue(); - mainQueue.setTenantId(TenantId.SYS_TENANT_ID); - mainQueue.setName("Main"); - mainQueue.setTopic("tb_rule_engine.main"); - mainQueue.setPollInterval(25); - mainQueue.setPartitions(10); - mainQueue.setConsumerPerPartition(true); - mainQueue.setPackProcessingTimeout(2000); - SubmitStrategy mainQueueSubmitStrategy = new SubmitStrategy(); - mainQueueSubmitStrategy.setType(SubmitStrategyType.BURST); - mainQueueSubmitStrategy.setBatchSize(1000); - mainQueue.setSubmitStrategy(mainQueueSubmitStrategy); - ProcessingStrategy mainQueueProcessingStrategy = new ProcessingStrategy(); - mainQueueProcessingStrategy.setType(ProcessingStrategyType.SKIP_ALL_FAILURES); - mainQueueProcessingStrategy.setRetries(3); - mainQueueProcessingStrategy.setFailurePercentage(0); - mainQueueProcessingStrategy.setPauseBetweenRetries(3); - mainQueueProcessingStrategy.setMaxPauseBetweenRetries(3); - mainQueue.setProcessingStrategy(mainQueueProcessingStrategy); - queueService.saveQueue(mainQueue); - - Queue highPriorityQueue = new Queue(); - highPriorityQueue.setTenantId(TenantId.SYS_TENANT_ID); - highPriorityQueue.setName("HighPriority"); - highPriorityQueue.setTopic("tb_rule_engine.hp"); - highPriorityQueue.setPollInterval(25); - highPriorityQueue.setPartitions(10); - highPriorityQueue.setConsumerPerPartition(true); - highPriorityQueue.setPackProcessingTimeout(2000); - SubmitStrategy highPriorityQueueSubmitStrategy = new SubmitStrategy(); - highPriorityQueueSubmitStrategy.setType(SubmitStrategyType.BURST); - highPriorityQueueSubmitStrategy.setBatchSize(100); - highPriorityQueue.setSubmitStrategy(highPriorityQueueSubmitStrategy); - ProcessingStrategy highPriorityQueueProcessingStrategy = new ProcessingStrategy(); - highPriorityQueueProcessingStrategy.setType(ProcessingStrategyType.RETRY_FAILED_AND_TIMED_OUT); - highPriorityQueueProcessingStrategy.setRetries(0); - highPriorityQueueProcessingStrategy.setFailurePercentage(0); - highPriorityQueueProcessingStrategy.setPauseBetweenRetries(5); - highPriorityQueueProcessingStrategy.setMaxPauseBetweenRetries(5); - highPriorityQueue.setProcessingStrategy(highPriorityQueueProcessingStrategy); - queueService.saveQueue(highPriorityQueue); - - Queue sequentialByOriginatorQueue = new Queue(); - sequentialByOriginatorQueue.setTenantId(TenantId.SYS_TENANT_ID); - sequentialByOriginatorQueue.setName("SequentialByOriginator"); - sequentialByOriginatorQueue.setTopic("tb_rule_engine.sq"); - sequentialByOriginatorQueue.setPollInterval(25); - sequentialByOriginatorQueue.setPartitions(10); - sequentialByOriginatorQueue.setPackProcessingTimeout(2000); - sequentialByOriginatorQueue.setConsumerPerPartition(true); - SubmitStrategy sequentialByOriginatorQueueSubmitStrategy = new SubmitStrategy(); - sequentialByOriginatorQueueSubmitStrategy.setType(SubmitStrategyType.SEQUENTIAL_BY_ORIGINATOR); - sequentialByOriginatorQueueSubmitStrategy.setBatchSize(100); - sequentialByOriginatorQueue.setSubmitStrategy(sequentialByOriginatorQueueSubmitStrategy); - ProcessingStrategy sequentialByOriginatorQueueProcessingStrategy = new ProcessingStrategy(); - sequentialByOriginatorQueueProcessingStrategy.setType(ProcessingStrategyType.RETRY_FAILED_AND_TIMED_OUT); - sequentialByOriginatorQueueProcessingStrategy.setRetries(3); - sequentialByOriginatorQueueProcessingStrategy.setFailurePercentage(0); - sequentialByOriginatorQueueProcessingStrategy.setPauseBetweenRetries(5); - sequentialByOriginatorQueueProcessingStrategy.setMaxPauseBetweenRetries(5); - sequentialByOriginatorQueue.setProcessingStrategy(sequentialByOriginatorQueueProcessingStrategy); - queueService.saveQueue(sequentialByOriginatorQueue); + Queue mainQueue = queueService.findQueueByTenantIdAndName(TenantId.SYS_TENANT_ID, "Main"); + if (mainQueue == null) { + mainQueue = new Queue(); + mainQueue.setTenantId(TenantId.SYS_TENANT_ID); + mainQueue.setName("Main"); + mainQueue.setTopic("tb_rule_engine.main"); + mainQueue.setPollInterval(25); + mainQueue.setPartitions(10); + mainQueue.setConsumerPerPartition(true); + mainQueue.setPackProcessingTimeout(2000); + SubmitStrategy mainQueueSubmitStrategy = new SubmitStrategy(); + mainQueueSubmitStrategy.setType(SubmitStrategyType.BURST); + mainQueueSubmitStrategy.setBatchSize(1000); + mainQueue.setSubmitStrategy(mainQueueSubmitStrategy); + ProcessingStrategy mainQueueProcessingStrategy = new ProcessingStrategy(); + mainQueueProcessingStrategy.setType(ProcessingStrategyType.SKIP_ALL_FAILURES); + mainQueueProcessingStrategy.setRetries(3); + mainQueueProcessingStrategy.setFailurePercentage(0); + mainQueueProcessingStrategy.setPauseBetweenRetries(3); + mainQueueProcessingStrategy.setMaxPauseBetweenRetries(3); + mainQueue.setProcessingStrategy(mainQueueProcessingStrategy); + queueService.saveQueue(mainQueue); + } + + Queue highPriorityQueue = queueService.findQueueByTenantIdAndName(TenantId.SYS_TENANT_ID, "HighPriority"); + if (highPriorityQueue == null) { + highPriorityQueue = new Queue(); + highPriorityQueue.setTenantId(TenantId.SYS_TENANT_ID); + highPriorityQueue.setName("HighPriority"); + highPriorityQueue.setTopic("tb_rule_engine.hp"); + highPriorityQueue.setPollInterval(25); + highPriorityQueue.setPartitions(10); + highPriorityQueue.setConsumerPerPartition(true); + highPriorityQueue.setPackProcessingTimeout(2000); + SubmitStrategy highPriorityQueueSubmitStrategy = new SubmitStrategy(); + highPriorityQueueSubmitStrategy.setType(SubmitStrategyType.BURST); + highPriorityQueueSubmitStrategy.setBatchSize(100); + highPriorityQueue.setSubmitStrategy(highPriorityQueueSubmitStrategy); + ProcessingStrategy highPriorityQueueProcessingStrategy = new ProcessingStrategy(); + highPriorityQueueProcessingStrategy.setType(ProcessingStrategyType.RETRY_FAILED_AND_TIMED_OUT); + highPriorityQueueProcessingStrategy.setRetries(0); + highPriorityQueueProcessingStrategy.setFailurePercentage(0); + highPriorityQueueProcessingStrategy.setPauseBetweenRetries(5); + highPriorityQueueProcessingStrategy.setMaxPauseBetweenRetries(5); + highPriorityQueue.setProcessingStrategy(highPriorityQueueProcessingStrategy); + queueService.saveQueue(highPriorityQueue); + } + + Queue sequentialByOriginatorQueue = queueService.findQueueByTenantIdAndName(TenantId.SYS_TENANT_ID, "SequentialByOriginator"); + if (sequentialByOriginatorQueue == null) { + sequentialByOriginatorQueue = new Queue(); + sequentialByOriginatorQueue.setTenantId(TenantId.SYS_TENANT_ID); + sequentialByOriginatorQueue.setName("SequentialByOriginator"); + sequentialByOriginatorQueue.setTopic("tb_rule_engine.sq"); + sequentialByOriginatorQueue.setPollInterval(25); + sequentialByOriginatorQueue.setPartitions(10); + sequentialByOriginatorQueue.setPackProcessingTimeout(2000); + sequentialByOriginatorQueue.setConsumerPerPartition(true); + SubmitStrategy sequentialByOriginatorQueueSubmitStrategy = new SubmitStrategy(); + sequentialByOriginatorQueueSubmitStrategy.setType(SubmitStrategyType.SEQUENTIAL_BY_ORIGINATOR); + sequentialByOriginatorQueueSubmitStrategy.setBatchSize(100); + sequentialByOriginatorQueue.setSubmitStrategy(sequentialByOriginatorQueueSubmitStrategy); + ProcessingStrategy sequentialByOriginatorQueueProcessingStrategy = new ProcessingStrategy(); + sequentialByOriginatorQueueProcessingStrategy.setType(ProcessingStrategyType.RETRY_FAILED_AND_TIMED_OUT); + sequentialByOriginatorQueueProcessingStrategy.setRetries(3); + sequentialByOriginatorQueueProcessingStrategy.setFailurePercentage(0); + sequentialByOriginatorQueueProcessingStrategy.setPauseBetweenRetries(5); + sequentialByOriginatorQueueProcessingStrategy.setMaxPauseBetweenRetries(5); + sequentialByOriginatorQueue.setProcessingStrategy(sequentialByOriginatorQueueProcessingStrategy); + queueService.saveQueue(sequentialByOriginatorQueue); + } } } diff --git a/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java b/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java index a86adeb578..d0d5f37e00 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java +++ b/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java @@ -571,73 +571,10 @@ public class SqlDatabaseUpgradeService implements DatabaseEntitiesUpgradeService schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "3.3.4", SCHEMA_UPDATE_SQL); loadSql(schemaUpdateFile, conn); - log.info("Loading queues..."); - try { - if (!CollectionUtils.isEmpty(queueConfig.getQueues())) { - queueConfig.getQueues().forEach(queueSettings -> { - queueService.saveQueue(queueConfigToQueue(queueSettings)); - }); - } else { - systemDataLoaderService.createQueues(); - } - } catch (Exception e) { - } - log.info("Updating device profiles..."); schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "3.3.4", "schema_update_device_profile.sql"); loadSql(schemaUpdateFile, conn); - log.info("Updating checkpoint rule nodes..."); - PageLink pageLink = new PageLink(100); - PageData pageData; - do { - pageData = tenantService.findTenants(pageLink); - for (Tenant tenant : pageData.getData()) { - TenantId tenantId = tenant.getId(); - Map queues = - queueService.findQueuesByTenantId(tenantId).stream().collect(Collectors.toMap(Queue::getName, Queue::getId)); - try { - List checkpointNodes = - ruleChainService.findRuleNodesByTenantIdAndType(tenantId, "org.thingsboard.rule.engine.flow.TbCheckpointNode"); - checkpointNodes.forEach(node -> { - ObjectNode configuration = (ObjectNode) node.getConfiguration(); - JsonNode queueNameNode = configuration.remove("queueName"); - if (queueNameNode != null) { - String queueName = queueNameNode.asText(); - configuration.put("queueId", queues.get(queueName).toString()); - ruleChainService.saveRuleNode(tenantId, node); - } - }); - } catch (Exception e) { - } - } - pageLink = pageLink.nextPageLink(); - } while (pageData.hasNext()); - - log.info("Updating tenant profiles..."); - PageLink profilePageLink = new PageLink(100); - PageData profilePageData; - do { - profilePageData = tenantProfileService.findTenantProfiles(TenantId.SYS_TENANT_ID, profilePageLink); - - profilePageData.getData().forEach(profile -> { - try { - List queueConfiguration = profile.getProfileData().getQueueConfiguration(); - if (profile.isIsolatedTbRuleEngine() && (queueConfiguration == null || queueConfiguration.isEmpty())) { - TenantProfileQueueConfiguration mainQueueConfig = getMainQueueConfiguration(); - profile.getProfileData().setQueueConfiguration(Collections.singletonList((mainQueueConfig))); - tenantProfileService.saveTenantProfile(TenantId.SYS_TENANT_ID, profile); - List isolatedTenants = tenantService.findTenantIdsByTenantProfileId(profile.getId()); - isolatedTenants.forEach(tenantId -> { - queueService.saveQueue(new Queue(tenantId, mainQueueConfig)); - }); - } - } catch (Exception e) { - } - - }); - profilePageLink = profilePageLink.nextPageLink(); - } while (profilePageData.hasNext()); log.info("Updating schema settings..."); conn.createStatement().execute("UPDATE tb_schema_settings SET schema_version = 3004000;"); log.info("Schema updated."); @@ -691,48 +628,4 @@ public class SqlDatabaseUpgradeService implements DatabaseEntitiesUpgradeService return isOldSchema; } - private Queue queueConfigToQueue(TbRuleEngineQueueConfiguration queueSettings) { - Queue queue = new Queue(); - queue.setTenantId(TenantId.SYS_TENANT_ID); - queue.setName(queueSettings.getName()); - queue.setTopic(queueSettings.getTopic()); - queue.setPollInterval(queueSettings.getPollInterval()); - queue.setPartitions(queueSettings.getPartitions()); - queue.setPackProcessingTimeout(queueSettings.getPackProcessingTimeout()); - SubmitStrategy submitStrategy = new SubmitStrategy(); - submitStrategy.setBatchSize(queueSettings.getSubmitStrategy().getBatchSize()); - submitStrategy.setType(SubmitStrategyType.valueOf(queueSettings.getSubmitStrategy().getType())); - queue.setSubmitStrategy(submitStrategy); - ProcessingStrategy processingStrategy = new ProcessingStrategy(); - processingStrategy.setType(ProcessingStrategyType.valueOf(queueSettings.getProcessingStrategy().getType())); - processingStrategy.setRetries(queueSettings.getProcessingStrategy().getRetries()); - processingStrategy.setFailurePercentage(queueSettings.getProcessingStrategy().getFailurePercentage()); - processingStrategy.setPauseBetweenRetries(queueSettings.getProcessingStrategy().getPauseBetweenRetries()); - processingStrategy.setMaxPauseBetweenRetries(queueSettings.getProcessingStrategy().getMaxPauseBetweenRetries()); - queue.setProcessingStrategy(processingStrategy); - queue.setConsumerPerPartition(queueSettings.isConsumerPerPartition()); - return queue; - } - - private TenantProfileQueueConfiguration getMainQueueConfiguration() { - TenantProfileQueueConfiguration mainQueueConfiguration = new TenantProfileQueueConfiguration(); - mainQueueConfiguration.setName("Main"); - mainQueueConfiguration.setTopic("tb_rule_engine.main"); - mainQueueConfiguration.setPollInterval(25); - mainQueueConfiguration.setPartitions(10); - mainQueueConfiguration.setConsumerPerPartition(true); - mainQueueConfiguration.setPackProcessingTimeout(2000); - SubmitStrategy mainQueueSubmitStrategy = new SubmitStrategy(); - mainQueueSubmitStrategy.setType(SubmitStrategyType.BURST); - mainQueueSubmitStrategy.setBatchSize(1000); - mainQueueConfiguration.setSubmitStrategy(mainQueueSubmitStrategy); - ProcessingStrategy mainQueueProcessingStrategy = new ProcessingStrategy(); - mainQueueProcessingStrategy.setType(ProcessingStrategyType.SKIP_ALL_FAILURES); - mainQueueProcessingStrategy.setRetries(3); - mainQueueProcessingStrategy.setFailurePercentage(0); - mainQueueProcessingStrategy.setPauseBetweenRetries(3); - mainQueueProcessingStrategy.setMaxPauseBetweenRetries(3); - mainQueueConfiguration.setProcessingStrategy(mainQueueProcessingStrategy); - return mainQueueConfiguration; - } } diff --git a/application/src/main/java/org/thingsboard/server/service/install/update/DefaultDataUpdateService.java b/application/src/main/java/org/thingsboard/server/service/install/update/DefaultDataUpdateService.java index 93c4bb835e..7b87668319 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/update/DefaultDataUpdateService.java +++ b/application/src/main/java/org/thingsboard/server/service/install/update/DefaultDataUpdateService.java @@ -21,6 +21,7 @@ import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.MoreExecutors; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections.CollectionUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Profile; import org.springframework.stereotype.Service; @@ -31,14 +32,12 @@ import org.thingsboard.rule.engine.profile.TbDeviceProfileNode; import org.thingsboard.rule.engine.profile.TbDeviceProfileNodeConfiguration; import org.thingsboard.server.common.data.EntityView; import org.thingsboard.server.common.data.Tenant; +import org.thingsboard.server.common.data.TenantProfile; import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.alarm.AlarmInfo; import org.thingsboard.server.common.data.alarm.AlarmQuery; import org.thingsboard.server.common.data.alarm.AlarmSeverity; -import org.thingsboard.server.common.data.id.EntityViewId; -import org.thingsboard.server.common.data.id.RuleChainId; -import org.thingsboard.server.common.data.id.RuleNodeId; -import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.id.*; import org.thingsboard.server.common.data.kv.BaseReadTsKvQuery; import org.thingsboard.server.common.data.kv.ReadTsKvQuery; import org.thingsboard.server.common.data.kv.TsKvEntry; @@ -47,12 +46,14 @@ import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.page.TimePageLink; import org.thingsboard.server.common.data.query.DynamicValue; import org.thingsboard.server.common.data.query.FilterPredicateValue; +import org.thingsboard.server.common.data.queue.*; import org.thingsboard.server.common.data.relation.EntityRelation; import org.thingsboard.server.common.data.relation.RelationTypeGroup; import org.thingsboard.server.common.data.rule.RuleChain; import org.thingsboard.server.common.data.rule.RuleChainMetaData; import org.thingsboard.server.common.data.rule.RuleChainType; import org.thingsboard.server.common.data.rule.RuleNode; +import org.thingsboard.server.common.data.tenant.profile.TenantProfileQueueConfiguration; import org.thingsboard.server.dao.DaoUtil; import org.thingsboard.server.dao.alarm.AlarmDao; import org.thingsboard.server.dao.alarm.AlarmService; @@ -61,16 +62,22 @@ import org.thingsboard.server.dao.entityview.EntityViewService; import org.thingsboard.server.dao.model.sql.DeviceProfileEntity; import org.thingsboard.server.dao.model.sql.RelationEntity; import org.thingsboard.server.dao.oauth2.OAuth2Service; +import org.thingsboard.server.dao.queue.QueueService; import org.thingsboard.server.dao.relation.RelationService; import org.thingsboard.server.dao.rule.RuleChainService; import org.thingsboard.server.dao.sql.device.DeviceProfileRepository; +import org.thingsboard.server.dao.tenant.TenantProfileService; import org.thingsboard.server.dao.tenant.TenantService; import org.thingsboard.server.dao.timeseries.TimeseriesService; +import org.thingsboard.server.queue.settings.TbRuleEngineQueueConfiguration; import org.thingsboard.server.service.install.InstallScripts; +import org.thingsboard.server.service.install.SystemDataLoaderService; +import org.thingsboard.server.service.install.TbRuleEngineQueueConfigService; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Map; import java.util.concurrent.ExecutionException; import java.util.concurrent.atomic.AtomicLong; import java.util.stream.Collectors; @@ -115,6 +122,18 @@ public class DefaultDataUpdateService implements DataUpdateService { @Autowired private OAuth2Service oAuth2Service; + @Autowired + private TenantProfileService tenantProfileService; + + @Autowired + private QueueService queueService; + + @Autowired + private TbRuleEngineQueueConfigService queueConfig; + + @Autowired + private SystemDataLoaderService systemDataLoaderService; + @Override public void updateData(String fromVersion) throws Exception { switch (fromVersion) { @@ -141,6 +160,26 @@ public class DefaultDataUpdateService implements DataUpdateService { log.info("Updating data from version 3.3.2 to 3.3.3 ..."); updateNestedRuleChains(); break; + case "3.3.4": + log.info("Updating data from version 3.3.4 to 3.4.0 ..."); + log.info("Loading queues..."); + try { + if (!CollectionUtils.isEmpty(queueConfig.getQueues())) { + queueConfig.getQueues().forEach(queueSettings -> { + Queue queue = queueConfigToQueue(queueSettings); + Queue existing = queueService.findQueueByTenantIdAndName(queue.getTenantId(), queue.getName()); + if (existing == null) { + queueService.saveQueue(queue); + } + }); + } else { + systemDataLoaderService.createQueues(); + } + } catch (Exception e) { + } + tenantsProfileQueueConfigurationUpdater.updateEntities(null); + checkPointRuleNodesUpdater.updateEntities(null); + break; default: throw new RuntimeException("Unable to update data, unsupported fromVersion: " + fromVersion); } @@ -547,4 +586,133 @@ public class DefaultDataUpdateService implements DataUpdateService { log.warn("CAUTION: Update of Oauth2 parameters from 3.2.2 to 3.3.0 available only in ThingsBoard versions 3.3.0/3.3.1"); } + private final PaginatedUpdater tenantsProfileQueueConfigurationUpdater = + new PaginatedUpdater<>() { + + @Override + protected String getName() { + return "Tenant profiles queue configuration updater"; + } + + @Override + protected boolean forceReportTotal() { + return true; + } + + @Override + protected PageData findEntities(String id, PageLink pageLink) { + return tenantProfileService.findTenantProfiles(TenantId.SYS_TENANT_ID, pageLink); + } + + @Override + protected void updateEntity(TenantProfile tenantProfile) { + updateTenantProfileQueueConfiguration(tenantProfile); + } + }; + + private void updateTenantProfileQueueConfiguration(TenantProfile profile) { + try { + List queueConfiguration = profile.getProfileData().getQueueConfiguration(); + if (profile.isIsolatedTbRuleEngine() && (queueConfiguration == null || queueConfiguration.isEmpty())) { + TenantProfileQueueConfiguration mainQueueConfig = getMainQueueConfiguration(); + profile.getProfileData().setQueueConfiguration(Collections.singletonList((mainQueueConfig))); + tenantProfileService.saveTenantProfile(TenantId.SYS_TENANT_ID, profile); + List isolatedTenants = tenantService.findTenantIdsByTenantProfileId(profile.getId()); + isolatedTenants.forEach(tenantId -> { + queueService.saveQueue(new Queue(tenantId, mainQueueConfig)); + }); + } + } catch (Exception e) { + log.error("Failed to update tenant profile queue configuration name=["+profile.getName()+"], id=["+ profile.getId().getId() +"]", e); + } + } + + private TenantProfileQueueConfiguration getMainQueueConfiguration() { + TenantProfileQueueConfiguration mainQueueConfiguration = new TenantProfileQueueConfiguration(); + mainQueueConfiguration.setName("Main"); + mainQueueConfiguration.setTopic("tb_rule_engine.main"); + mainQueueConfiguration.setPollInterval(25); + mainQueueConfiguration.setPartitions(10); + mainQueueConfiguration.setConsumerPerPartition(true); + mainQueueConfiguration.setPackProcessingTimeout(2000); + SubmitStrategy mainQueueSubmitStrategy = new SubmitStrategy(); + mainQueueSubmitStrategy.setType(SubmitStrategyType.BURST); + mainQueueSubmitStrategy.setBatchSize(1000); + mainQueueConfiguration.setSubmitStrategy(mainQueueSubmitStrategy); + ProcessingStrategy mainQueueProcessingStrategy = new ProcessingStrategy(); + mainQueueProcessingStrategy.setType(ProcessingStrategyType.SKIP_ALL_FAILURES); + mainQueueProcessingStrategy.setRetries(3); + mainQueueProcessingStrategy.setFailurePercentage(0); + mainQueueProcessingStrategy.setPauseBetweenRetries(3); + mainQueueProcessingStrategy.setMaxPauseBetweenRetries(3); + mainQueueConfiguration.setProcessingStrategy(mainQueueProcessingStrategy); + return mainQueueConfiguration; + } + + private Queue queueConfigToQueue(TbRuleEngineQueueConfiguration queueSettings) { + Queue queue = new Queue(); + queue.setTenantId(TenantId.SYS_TENANT_ID); + queue.setName(queueSettings.getName()); + queue.setTopic(queueSettings.getTopic()); + queue.setPollInterval(queueSettings.getPollInterval()); + queue.setPartitions(queueSettings.getPartitions()); + queue.setPackProcessingTimeout(queueSettings.getPackProcessingTimeout()); + SubmitStrategy submitStrategy = new SubmitStrategy(); + submitStrategy.setBatchSize(queueSettings.getSubmitStrategy().getBatchSize()); + submitStrategy.setType(SubmitStrategyType.valueOf(queueSettings.getSubmitStrategy().getType())); + queue.setSubmitStrategy(submitStrategy); + ProcessingStrategy processingStrategy = new ProcessingStrategy(); + processingStrategy.setType(ProcessingStrategyType.valueOf(queueSettings.getProcessingStrategy().getType())); + processingStrategy.setRetries(queueSettings.getProcessingStrategy().getRetries()); + processingStrategy.setFailurePercentage(queueSettings.getProcessingStrategy().getFailurePercentage()); + processingStrategy.setPauseBetweenRetries(queueSettings.getProcessingStrategy().getPauseBetweenRetries()); + processingStrategy.setMaxPauseBetweenRetries(queueSettings.getProcessingStrategy().getMaxPauseBetweenRetries()); + queue.setProcessingStrategy(processingStrategy); + queue.setConsumerPerPartition(queueSettings.isConsumerPerPartition()); + return queue; + } + + private final PaginatedUpdater checkPointRuleNodesUpdater = + new PaginatedUpdater<>() { + + @Override + protected String getName() { + return "Checkpoint rule nodes updater"; + } + + @Override + protected boolean forceReportTotal() { + return true; + } + + @Override + protected PageData findEntities(String id, PageLink pageLink) { + return ruleChainService.findAllRuleNodesByType("org.thingsboard.rule.engine.flow.TbCheckpointNode", pageLink); + } + + @Override + protected void updateEntity(RuleNode ruleNode) { + updateCheckPointRuleNodeConfiguration(ruleNode); + } + }; + + private void updateCheckPointRuleNodeConfiguration(RuleNode node) { + try { + ObjectNode configuration = (ObjectNode) node.getConfiguration(); + JsonNode queueNameNode = configuration.remove("queueName"); + if (queueNameNode != null) { + RuleChain ruleChain = this.ruleChainService.findRuleChainById(TenantId.SYS_TENANT_ID, node.getRuleChainId()); + TenantId tenantId = ruleChain.getTenantId(); + Map queues = + queueService.findQueuesByTenantId(tenantId).stream().collect(Collectors.toMap(Queue::getName, Queue::getId)); + String queueName = queueNameNode.asText(); + QueueId queueId = queues.get(queueName); + configuration.put("queueId", queueId != null ? queueId.toString() : ""); + ruleChainService.saveRuleNode(tenantId, node); + } + } catch (Exception e) { + log.error("Failed to update checkpoint rule node configuration name=["+node.getName()+"], id=["+ node.getId().getId() +"]", e); + } + } + } diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/rule/RuleChainService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/rule/RuleChainService.java index 1d99534636..3328372f2e 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/rule/RuleChainService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/rule/RuleChainService.java @@ -94,5 +94,7 @@ public interface RuleChainService { List findRuleNodesByTenantIdAndType(TenantId tenantId, String type); + PageData findAllRuleNodesByType(String type, PageLink pageLink); + RuleNode saveRuleNode(TenantId tenantId, RuleNode ruleNode); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java b/dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java index 166d142baf..4be7227214 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java @@ -68,8 +68,7 @@ import java.util.Set; import java.util.stream.Collectors; import static org.thingsboard.server.common.data.DataConstants.TENANT; -import static org.thingsboard.server.dao.service.Validator.validateId; -import static org.thingsboard.server.dao.service.Validator.validateString; +import static org.thingsboard.server.dao.service.Validator.*; /** * Created by igor on 3/12/18. @@ -676,6 +675,14 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC return ruleNodeDao.findRuleNodesByTenantIdAndType(tenantId, type, ""); } + @Override + public PageData findAllRuleNodesByType(String type, PageLink pageLink) { + log.trace("Executing findAllRuleNodesByType, type {}, pageLink {}", type, pageLink); + validateString(type, "Incorrect type of the rule node"); + validatePageLink(pageLink); + return ruleNodeDao.findAllRuleNodesByType(type, pageLink); + } + @Override public RuleNode saveRuleNode(TenantId tenantId, RuleNode ruleNode) { return ruleNodeDao.save(tenantId, ruleNode); diff --git a/dao/src/main/java/org/thingsboard/server/dao/rule/RuleNodeDao.java b/dao/src/main/java/org/thingsboard/server/dao/rule/RuleNodeDao.java index 3984622d89..b4f087b98a 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/rule/RuleNodeDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/rule/RuleNodeDao.java @@ -16,6 +16,8 @@ package org.thingsboard.server.dao.rule; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.rule.RuleNode; import org.thingsboard.server.dao.Dao; @@ -27,4 +29,6 @@ import java.util.List; public interface RuleNodeDao extends Dao { List findRuleNodesByTenantIdAndType(TenantId tenantId, String type, String search); + + PageData findAllRuleNodesByType(String type, PageLink pageLink); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/rule/JpaRuleNodeDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/rule/JpaRuleNodeDao.java index a499e68da8..a0f84758c6 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/rule/JpaRuleNodeDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/rule/JpaRuleNodeDao.java @@ -20,6 +20,8 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.rule.RuleNode; import org.thingsboard.server.dao.DaoUtil; import org.thingsboard.server.dao.model.sql.RuleNodeEntity; @@ -27,6 +29,7 @@ import org.thingsboard.server.dao.rule.RuleNodeDao; import org.thingsboard.server.dao.sql.JpaAbstractSearchTextDao; import java.util.List; +import java.util.Objects; import java.util.UUID; @Slf4j @@ -50,4 +53,14 @@ public class JpaRuleNodeDao extends JpaAbstractSearchTextDao findRuleNodesByTenantIdAndType(TenantId tenantId, String type, String search) { return DaoUtil.convertDataList(ruleNodeRepository.findRuleNodesByTenantIdAndType(tenantId.getId(), type, search)); } + + @Override + public PageData findAllRuleNodesByType(String type, PageLink pageLink) { + return DaoUtil.toPageData(ruleNodeRepository + .findAllRuleNodesByType( + type, + Objects.toString(pageLink.getTextSearch(), ""), + DaoUtil.toPageable(pageLink))); + } + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/rule/RuleNodeRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/rule/RuleNodeRepository.java index b11923a50f..f584ff2b02 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/rule/RuleNodeRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/rule/RuleNodeRepository.java @@ -15,6 +15,8 @@ */ package org.thingsboard.server.dao.sql.rule; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; @@ -32,4 +34,9 @@ public interface RuleNodeRepository extends JpaRepository @Param("ruleType") String ruleType, @Param("searchText") String searchText); + @Query("SELECT r FROM RuleNodeEntity r WHERE r.type = :ruleType AND LOWER(r.configuration) LIKE LOWER(CONCAT('%', :searchText, '%')) ") + Page findAllRuleNodesByType(@Param("ruleType") String ruleType, + @Param("searchText") String searchText, + Pageable pageable); + } From 4301927b7f1cccd818d576c1a2c0822f5d899fda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20N=C3=BA=C3=B1ez?= Date: Thu, 2 Jun 2022 20:40:01 +0200 Subject: [PATCH 752/798] [WIP] Add spanish feature to 3.4 --- .../assets/locale/locale.constant-es_ES.json | 2094 +++++++++++++++-- 1 file changed, 1934 insertions(+), 160 deletions(-) diff --git a/ui-ngx/src/assets/locale/locale.constant-es_ES.json b/ui-ngx/src/assets/locale/locale.constant-es_ES.json index f8e16aca0e..578153c62f 100644 --- a/ui-ngx/src/assets/locale/locale.constant-es_ES.json +++ b/ui-ngx/src/assets/locale/locale.constant-es_ES.json @@ -58,7 +58,8 @@ "next-with-label": "Siguiente: {{label}}", "read-more": "Leer más", "hide": "Ocultar", - "done": "Hecho" + "done": "Terminado", + "print": "Imprimir" }, "aggregation": { "aggregation": "Agrupación", @@ -75,6 +76,7 @@ "admin": { "general": "General", "general-settings": "Configuración general", + "home-settings": "Ajustes Home", "outgoing-mail": "Servidor de correo", "outgoing-mail-settings": "Configuración del servidor de correo de salida", "system-settings": "Sistema", @@ -104,6 +106,7 @@ "proxy-port-range": "El puerto proxy debe estar en un rango de 1 a 65535.", "proxy-user": "Usuario proxy", "proxy-password": "Contraseña proxy", + "change-password": "Cambiar contraseña", "send-test-mail": "Enviar correo de prueba", "sms-provider": "Proveedor SMS", "sms-provider-settings": "Ajustes proveedor SMS", @@ -111,18 +114,21 @@ "sms-provider-type-required": "Se requiere proveedor SMS.", "sms-provider-type-aws-sns": "Amazon SNS", "sms-provider-type-twilio": "Twilio", + "sms-provider-type-smpp": "SMPP", "aws-access-key-id": "AWS Access Key ID", - "aws-access-key-id-required": "Se requiere AWS Access Key ID", + "aws-access-key-id-required": "AWS Access Key ID requerido", "aws-secret-access-key": "AWS Secret Access Key", - "aws-secret-access-key-required": "Se requere AWS Secret Access Key", + "aws-secret-access-key-required": "AWS Secret Access Key requerido", "aws-region": "Región AWS", "aws-region-required": "Se requere región AWS", "number-from": "Nº de teléfono Origen", "number-from-required": "Se requere Nº de teléfono origen.", "number-to": "Nº de teléfono de destino", "number-to-required": "Se requere Nº de teléfono de destino.", - "phone-number-hint": "Nº de teléfono en formato E.164, ex. +19995550123", - "phone-number-pattern": "Nº Inválido. Debe estar en formato E.164, ex. +19995550123.", + "phone-number-hint": "Nº de teléfono en formato E.164, ej. +19995550123", + "phone-number-hint-twilio": "Nº de teléfono en formato E.164 SID/SID de servicio de mensajería, ej. +19995550123/PNXXX/MGXXX", + "phone-number-pattern": "Nº Inválido. Debe estar en formato E.164, ej. +19995550123.", + "phone-number-pattern-twilio": "Nº Inválido. Debe estar en formato E.164 , ej. +19995550123/PNXXX/MGXXX.", "sms-message": "Mensaje SMS", "sms-message-required": "Se requeriere mensaje SMS.", "sms-message-max-length": "Los SMS no pueden ser más largos de 1600 caracteres", @@ -149,12 +155,14 @@ "password-expiration-period-days-range": "El período de caducidad de la contraseña en días no puede ser negativo", "password-reuse-frequency-days": "Frecuencia de reutilización de contraseña en días", "password-reuse-frequency-days-range": "La frecuencia de reutilización de contraseña en días no puede ser negativa", + "allow-whitespace": "Permitir espacios en blanco", "general-policy": "Política general", "max-failed-login-attempts": "Número máximo de intentos fallidos de inicio de sesión, antes de que la cuenta esté bloqueada", "minimum-max-failed-login-attempts-range": "El número máximo de intentos fallidos de inicio de sesión no puede ser negativo", "user-lockout-notification-email": "En caso de bloqueo de la cuenta del usuario, envíe una notificación por correo electrónico", "domain-name": "Nombre de dominio", "domain-name-unique": "El nombre de dominio y protocolo debe ser único.", + "domain-name-max-length": "El nombre de dominio debe ser menor que 256", "error-verification-url": "Un nombre de dominio no debe contener símbolos '/' y ':'. Ejemplo: thingsboard.io", "oauth2": { "access-token-uri": "URI Access token", @@ -171,21 +179,28 @@ "client-authentication-method": "Método de autenticación", "client-id": "ID Cliente", "client-id-required": "Se requere ID Cliente.", + "client-id-max-length": "El ID Cliente debe ser menor de 256", "client-secret": "Secreto de Cliente", "client-secret-required": "Se requiere Secreto de Cliente.", + "client-secret-max-length": "El secreto de cliente debe ser menor de 2049", "custom-setting": "Ajustes personalizados", "customer-name-pattern": "Patrón nombre de cliente", + "customer-name-pattern-max-length": "El patrón del nombre de cliente debe ser menor de 256", "default-dashboard-name": "Nombre de panel por defecto", + "default-dashboard-name-max-length": "El nombre del panel debe ser menor de 256", "delete-domain-text": "Atención, tras la confirmación el dominio y todos los datos del proveedor no estarán disponibles.", "delete-domain-title": "Eliminar los ajustes del dominio '{{domainName}}'?", "delete-registration-text": "Atención, tras la confirmación los datos del proveedor no estarán disponibles.", "delete-registration-title": "Eliminar el proveedor '{{name}}'?", "email-attribute-key": "Clave de atributos email", "email-attribute-key-required": "Se requiere clave de atributos de email.", + "email-attribute-key-max-length": "La clave de atributos de email debe ser menor de 32", "first-name-attribute-key": "Clave de atributos de nombre", + "first-name-attribute-key-max-length": "La clave de atributos de nombre debe ser menor de 32", "general": "General", "jwk-set-uri": "URI web key JSON", "last-name-attribute-key": "Clave de atributos de apellido", + "last-name-attribute-key-max-length": "La clave de atributos de apellido debe ser menor de 32", "login-button-icon": "Icono de botón login", "login-button-label": "Etiqueta de proveedor", "login-button-label-placeholder": "Login con $(Provider label)", @@ -194,6 +209,7 @@ "mapper": "Mapeador", "new-domain": "Nuevo dominio", "oauth2": "OAuth2", + "password-max-length": "La contraseña debe ser menor de 256", "redirect-uri-template": "Plantilla de redirección URI", "copy-redirect-uri": "Copiar URI de redirección", "registration-id": "ID de registro", @@ -203,21 +219,141 @@ "scope-required": "Se requiere alcance.", "tenant-name-pattern": "Patrón de nombre de propietario", "tenant-name-pattern-required": "Se requiere patrón de nombre de propietario.", + "tenant-name-pattern-max-length": "EL patrón de nombre de propietario debe ser menor de 256", "tenant-name-strategy": "Estrategia de Nombre de Propietario", "type": "Tipo de mapeador", "uri-pattern-error": "Formato de URI inválido.", "url": "URL", "url-pattern": "Formato URL inválido.", "url-required": "Se requiere URL.", + "url-max-length": "URL debe ser menor de 256", "user-info-uri": "URI Información de usuario", "user-info-uri-required": "Se requiere URI de información usuario.", + "username-max-length": "El usuario debe ser menor de 256", "user-name-attribute-name": "Clave de atributos de nombre de usuario", "user-name-attribute-name-required": "Se requiere clave de atributos de nombre de usuario", "protocol": "Protocolo", "domain-schema-http": "HTTP", "domain-schema-https": "HTTPS", "domain-schema-mixed": "HTTP+HTTPS", - "enable": "Activar ajustes OAuth2" + "enable": "Activar ajustes OAuth2", + "domains": "Dominios", + "mobile-apps": "Aplicaciones móviles", + "no-mobile-apps": "No hay aplicaciones configuradas", + "mobile-package": "Paquete de aplicación", + "mobile-package-placeholder": "Ej.: mi.ejemplo.app", + "mobile-package-hint": "Para Android: El ID único de aplicación. Para iOS: Identificador Product bundle.", + "mobile-package-unique": "El paquete de aplicación debe ser único.", + "mobile-app-secret": "Secreto de aplicación", + "invalid-mobile-app-secret": "El secreto de aplicación sólo puede contener carácteres alfanuméricos y debe tener entre 16 y 2048 carácteres de longitud.", + "copy-mobile-app-secret": "Copiar secreto de aplicación", + "add-mobile-app": "Añadir aplicación", + "delete-mobile-app": "Borrar información de aplicación", + "providers": "Proveedores", + "platform-web": "Web", + "platform-android": "Android", + "platform-ios": "iOS", + "all-platforms": "Todas las plataformas", + "allowed-platforms": "Plataformas permitidas" + }, + "smpp-provider": { + "smpp-version": "Versión SMPP", + "smpp-host": "Host SMPP", + "smpp-host-required": "Host SMPP requerido", + "smpp-port": "Puerto SMPP", + "smpp-port-required": "Puerto SMPP requerido", + "system-id": "System ID", + "system-id-required": "System ID requerido", + "password": "Contraseña", + "password-required": "Contraseña requerida", + "type-settings": "Ajustes de tipo", + "source-settings": "Ajustes de origen", + "destination-settings": "Ajustes de destino", + "additional-settings": "Ajustes adicionales", + "system-type": "Tipo de sistema", + "bind-type": "Tipo de enlace", + "service-type": "Tipo de servicio", + "source-address": "Dirección de origen", + "source-ton": "Origen TON", + "source-npi": "Origen NPI", + "destination-ton": "Destino TON (Tipo de número)", + "destination-npi": "Destino NPI (Numbering Plan Identification)", + "address-range": "Rango de dirección", + "coding-scheme": "Esquema de codificación", + "bind-type-tx": "Transmisor", + "bind-type-rx": "Receptor", + "bind-type-trx": "Transceptor", + "ton-unknown": "Desconocido", + "ton-international": "Internacional", + "ton-national": "Nacional", + "ton-network-specific": "Específico de red", + "ton-subscriber-number": "Número de suscriptor", + "ton-alphanumeric": "Alfanumérico", + "ton-abbreviated": "Abreviado", + "npi-unknown": "0 - Desconocido", + "npi-isdn": "1 - RDSI/Teléfono plan numérico (E163/E164)", + "npi-data-numbering-plan": "3 - Datos plan numérico (X.121)", + "npi-telex-numbering-plan": "4 - Telex plan numérico (F.69)", + "npi-land-mobile": "6 - Línea Móvil (E.212)", + "npi-national-numbering-plan": "8 - Nacional", + "npi-private-numbering-plan": "9 - Privado", + "npi-ermes-numbering-plan": "10 - ERMES (ETSI DE/PS 3 01-3)", + "npi-internet": "13 - Internet (IP)", + "npi-wap-client-id": "18 - WAP Client Id (to be defined by WAP Forum)", + "scheme-smsc": "0 - SMSC Default Alphabet (ASCII for short and long code and to GSM for toll-free)", + "scheme-ia5": "1 - IA5 (ASCII for short and long code, Latin 9 for toll-free (ISO-8859-9))", + "scheme-octet-unspecified-2": "2 - Octet Unspecified (8-bit binary)", + "scheme-latin-1": "3 - Latin 1 (ISO-8859-1)", + "scheme-octet-unspecified-4": "4 - Octet Unspecified (8-bit binary)", + "scheme-jis": "5 - JIS (X 0208-1990)", + "scheme-cyrillic": "6 - Cyrillic (ISO-8859-5)", + "scheme-latin-hebrew": "7 - Latin/Hebrew (ISO-8859-8)", + "scheme-ucs-utf": "8 - UCS2/UTF-16 (ISO/IEC-10646)", + "scheme-pictogram-encoding": "9 - Pictogram Encoding", + "scheme-music-codes": "10 - Music Codes (ISO-2022-JP)", + "scheme-extended-kanji-jis": "13 - Extended Kanji JIS (X 0212-1990)", + "scheme-korean-graphic-character-set": "14 - Korean Graphic Character Set (KS C 5601/KS X 1001)" + }, + "queue-select-name": "Select queue name", + "queue-name": "Name", + "queue-name-required": "Queue name is required!", + "queues": "Queues", + "queue-partitions": "Partitions", + "queue-submit-strategy": "Submit strategy", + "queue-processing-strategy": "Processing strategy", + "queue-configuration": "Queue configuration", + "2fa": { + "2fa": "Two-factor authentication", + "available-providers": "Available providers", + "issuer-name": "Issuer name", + "issuer-name-required": "Issuer name is required.", + "max-verification-failures-before-user-lockout": "Max verification failures before user lockout", + "max-verification-failures-before-user-lockout-pattern": "Max verification failures must be a positive integer.", + "number-of-checking-attempts": "Number of checking attempts", + "number-of-checking-attempts-pattern": "Number of checking attempts must be a positive integer.", + "number-of-checking-attempts-required": "Number of checking attempts is required.", + "number-of-codes": "Number of codes", + "number-of-codes-pattern": "Number of codes must be a positive integer.", + "number-of-codes-required": "Number of codes is required.", + "provider": "Provider", + "retry-verification-code-period": "Retry verification code period (sec)", + "retry-verification-code-period-pattern": "Minimal period time is 5 sec", + "retry-verification-code-period-required": "Retry verification code period is required.", + "total-allowed-time-for-verification": "Total allowed time for verification (sec)", + "total-allowed-time-for-verification-pattern": "Minimal total allowed time is 60 sec", + "total-allowed-time-for-verification-required": "Total allowed time is required.", + "use-system-two-factor-auth-settings": "Use system two factor auth settings", + "verification-code-check-rate-limit": "Verification code check rate limit", + "verification-code-lifetime": "Verification code lifetime (sec)", + "verification-code-lifetime-pattern": "Verification code lifetime must be a positive integer.", + "verification-code-lifetime-required": "Verification code lifetime is required.", + "verification-message-template": "Verification message template", + "verification-limitations": "Verification limitations", + "verification-message-template-pattern": "Verification message need to contains pattern: ${code}", + "verification-message-template-required": "Verification message template is required.", + "within-time": "Within time (sec)", + "within-time-pattern": "Time must be a positive integer.", + "within-time-required": "Time is required." } }, "alarm": { @@ -299,6 +435,7 @@ "filter-type-single-entity": "Única entidad", "filter-type-entity-list": "Lista de entidades", "filter-type-entity-name": "Nombre de entidad", + "filter-type-entity-type": "Tipo de entidad", "filter-type-state-entity": "Entidad desde estado de panel", "filter-type-state-entity-description": "Entidad tomada de los parámetros de panel", "filter-type-asset-type": "Tipo de activo", @@ -312,6 +449,7 @@ "filter-type-entity-view-type-and-name-description": "Vistas de entidad del tipo '{{entityView}}' y cuyo nombre comience por '{{prefix}}'", "filter-type-edge-type": "Tipo de borde", "filter-type-edge-type-description": "Bordes del tipo '{{edgeType}}'", + "filter-type-edge-type-and-name-description": "Vistas de entidad del tipo '{{entityViewType}}' y cuyo nombre comience por '{{prefix}}'", "filter-type-relations-query": "Consulta de relaciones", "filter-type-relations-query-description": "{{entities}} que tienen {{relationType}} relación {{direction}} {{rootEntity}}", "filter-type-asset-search-query": "Búsqueda de activos", @@ -323,23 +461,21 @@ "filter-type-apiUsageState": "Uso de API", "filter-type-edge-search-query": "Consultar búsqueda de borde", "filter-type-edge-search-query-description": "Bordes con tipos {{edgeTypes}} que tienen {{relationType}} relación {{direction}} {{rootEntity}}", - "type-assigned-to-edge": "Asignado a borde", - "type-unassigned-from-edge": "Sin asignar desde bordes", - "entity-filter": "Filtro por entidad", - "resolve-multiple": "Tomar como múltiples entidades", - "filter-type": "Filtro por tipo", - "filter-type-required": "Se requiere filtro por tipo.", - "entity-filter-no-entity-matched": "No se han encontrado entidades con el filtro especificado.", - "no-entity-filter-specified": "No hay filtro de entidades especificado", - "root-state-entity": "Usar estado de panel como raíz", - "last-level-relation": "Buscar sólo la relación de último nivel", - "root-entity": "Entidad raiz", - "state-entity-parameter-name": "Nombre de parámetro de entidad de estado", - "default-state-entity": "Entidad de estado predeterminada", + "entity-filter": "Filtro de entidad", + "resolve-multiple": "Resolver como múltiples entidades", + "filter-type": "Tipo de filtro", + "filter-type-required": "Se requiere tipo de filtro.", + "entity-filter-no-entity-matched": "No se encontraron entidades que coincidan con el filtro especificado.", + "no-entity-filter-specified": "No se ha especificado filtro de entidad", + "root-state-entity": "Usar entidad de estado del panel como raíz", + "last-level-relation": "Obtener sólo el último nivel de relación", + "root-entity": "Entidad raíz", + "state-entity-parameter-name": "Parámetro de estado de entidad", + "default-state-entity": "Entidad de estado por defecto", "default-entity-parameter-name": "Por defecto", - "max-relation-level": "Máx nivel de relación", + "max-relation-level": "Máximo nivel de relación", "unlimited-level": "Nivel ilimitado", - "state-entity": "Entidad estado del panel", + "state-entity": "Entidad de estado del panel", "all-entities": "Todas las entidades", "any-relation": "cualquiera" }, @@ -349,6 +485,7 @@ "management": "Gestión de Activos", "view-assets": "Ver Activos", "add": "Añadir Activo", + "assign-to-customer": "Asignar a cliente", "assign-asset-to-customer": "Asignar Activo(s) A Cliente", "assign-asset-to-customer-text": "Selecciona los activos a asignar al cliente", @@ -371,6 +508,8 @@ "asset-types": "Tipos de activo", "name": "Nombre", "name-required": "Nombre requerido.", + "name-max-length": "El nombre debe tener menos de 256", + "label-max-length": "La etiqueta debe tener menos de 256", "description": "Descripción", "type": "Tipo", "type-required": "Tipo requerido.", @@ -380,6 +519,8 @@ "asset-details": "Detalles de activo", "assign-assets": "Asignar activos", "assign-assets-text": "Asignar { count, plural, 1 {1 activo} other {# activos} } a cliente", + "assign-asset-to-edge-title": "Asignar activo(s) al borde", + "assign-asset-to-edge-text": "Por favor, selecciona lo activos a asignar al borde", "delete-assets": "Borrar activos", "unassign-assets": "Cancelar asignación de activo", "unassign-assets-action-title": "Cancelar asignación de { count, plural, 1 {1 activo} other {# activos} } del cliente", @@ -398,25 +539,25 @@ "unassign-asset": "Cancelar asignación de activo", "unassign-assets-title": "Cancelar las asignaciones { count, plural, 1 {1 activo} other {# activos} }?", "unassign-assets-text": "Tras la confirmación todos los activos seleccionados serán desasignados y no serán accesibles por el cliente.", + "unassign-assets-from-edge": "Anular activos de borde", "copyId": "Copiar ID de activo", "idCopiedMessage": "El ID ha sido copiado al portapapeles", "select-asset": "Seleccionar activo", "no-assets-matching": "No se han encontrado activos que coincidan con '{{entity}}' .", "asset-required": "Nombre de activo requerido", "name-starts-with": "El nombre de activo comienza con", + "help-text": "Usar el símbolo '%' de acuerdo a las necesidades: '%nombre_activo_contiene%', '%nombre_activo_acaba_en', 'nombre_activo_comienza_con'.", "import": "Importar activos", "asset-file": "Archivo del activo", - "search": "Buscar activos", - "selected-assets": "{ count, plural, 1 {1 activo} other {# activos} } seleccionados", "label": "Etiqueta", + "search": "Buscar activos", "assign-asset-to-edge": "Asignar activo(s) al borde", - "assign-asset-to-edge-text":"Por favor, seleccione los activos para asignar al borde", "unassign-asset-from-edge": "Anular activo de bodre", "unassign-asset-from-edge-title": "¿Está seguro de que desea desasignar el activo '{{assetName}}'?", "unassign-asset-from-edge-text": "Después de la confirmación, el activo no será asignado y el borde no podrá acceder a él", - "unassign-assets-from-edge-action-title": "Anular asignación {count, plural, 1 {1 activo} other {# activos} } desde el borde", "unassign-assets-from-edge-title": "¿Está seguro de que desea desasignar {count, plural, 1 {1 activo} other {# activos} }?", - "unassign-assets-from-edge-text": "Después de la confirmación, todos los activos seleccionados quedarán sin asignar y el borde no podrá acceder a ellos." + "unassign-assets-from-edge-text": "Después de la confirmación, todos los activos seleccionados quedarán sin asignar y el borde no podrá acceder a ellos.", + "selected-assets": "{ count, plural, 1 {1 activo} other {# activos} } seleccionados" }, "attribute": { "attributes": "Atributos", @@ -428,6 +569,7 @@ "scope-shared": "Atributos Compartidos", "add": "Agregar atributo", "key": "Clave", + "key-max-length": "La clave debe ser menor de 256", "last-update-time": "Hora de última actualización", "key-required": "Clave del atributo requerida.", "value": "Valor", @@ -449,12 +591,16 @@ }, "api-usage": { "api-usage": "Uso de API", + "alarm": "Alarma", + "alarms-created": "Alarmas creadas", + "alarms-created-daily-activity": "Actividad diaria de Alarmas creadas", + "alarms-created-hourly-activity": "Actividad horaria de Alarmas creadas", + "alarms-created-monthly-activity": "Actividad mensual de Alarmas creadas", "data-points": "Puntos de datos", - "data-points-storage-days": "Días de grabación de puntos de datos", + "data-points-storage-days": "Días de guardado de puntos de datos", "email": "Email", "email-messages": "Mensajes de Email", "email-messages-daily-activity": "Actividad diaria de Emails", - "email-messages-hourly-activity": "Actividad horaria de Emails", "email-messages-monthly-activity": "Actividad mensual de Emails", "exceptions": "Excepciones", "executions": "Ejecuciones", @@ -466,6 +612,9 @@ "javascript-functions-monthly-activity": "Actividad mensual de funciones JavaScript", "latest-error": "Último error", "messages": "Mensajes", + "notifications": "Notificaciones", + "notifications-email-sms": "Notificaciones (Email/SMS)", + "notifications-hourly-activity": "Notificaciones actividad horaria", "permanent-failures": "${entityName} Fallos permanentes", "permanent-timeouts": "${entityName} Timeouts permanentes", "processing-failures": "${entityName} Fallos de procesamiento", @@ -483,7 +632,6 @@ "sms": "SMS", "sms-messages": "Mensajes SMS", "sms-messages-daily-activity": "Actividad diaria de mensajes SMS", - "sms-messages-hourly-activity": "Actividad horaria de mensajes SMS", "sms-messages-monthly-activity": "Actividad mensual de mensajes SMS", "successful": "${entityName} Exitoso", "telemetry": "Telemetría", @@ -519,6 +667,8 @@ "type-credentials-updated": "Credenciales actualizados", "type-assigned-to-customer": "Asignado a Cliente", "type-unassigned-from-customer": "Deasignado a Cliente", + "type-assigned-to-edge": "Asignado a Borde", + "type-unassigned-from-edge": "Deasignado de Borde", "type-activated": "Activado", "type-suspended": "Suspendido", "type-credentials-read": "Lectura de credenciales", @@ -561,7 +711,10 @@ "address2": "Dirección 2", "phone": "Teléfono", "email": "Email", - "no-address": "Sin Dirección" + "no-address": "Sin Dirección", + "state-max-length": "Longitud de provincia debe ser menor que 256", + "phone-max-length": "Teléfono debe ser menor que 256", + "city-max-length": "Ciudad debe ser menor que 256" }, "common": { "username": "Usuario", @@ -570,7 +723,9 @@ "enter-password": "Introduce la contraseña", "enter-search": "Introduce búsqueda", "created-time": "Fecha de creación", - "loading": "Cargando..." + "loading": "Cargando...", + "proceed": "Proceder", + "open-details-page": "Abrir detalles" }, "content-type": { "json": "Json", @@ -616,10 +771,10 @@ "manage-dashboards": "Gestionar paneles", "title": "Título", "title-required": "Título requerido.", + "title-max-length": "Título debe ser menor de 256", "description": "Descripción", "details": "Detalles", "events": "Eventos", - "edges": "Bordes del cliente", "copyId": "Copiar ID de cliente", "idCopiedMessage": "El ID de cliente se ha copiado al portapapeles", "select-customer": "Seleccionar Cliente", @@ -629,7 +784,9 @@ "default-customer": "Cliente por defecto", "default-customer-required": "Se requiere cliente por defecto para realizar debu a nivel de propietario", "search": "Buscar clientes", - "selected-customers": "{ count, plural, 1 {1 cliente} other {# clientes} } seleccionados" + "selected-customers": "{ count, plural, 1 {1 cliente} other {# clientes} } seleccionados", + "edges": "Instancias de borde del cliente", + "manage-edges": "Administrar Bordes" }, "datetime": { "date-from": "Fecha desde", @@ -645,6 +802,7 @@ "add": "Agregar Panel", "assign-dashboard-to-customer": "Asignar panel(es) a cliente", "assign-dashboard-to-customer-text": "Por favor, seleccione algún panel para asignar al Cliente.", + "assign-dashboard-to-edge-title": "Asignar panel(es) a Borde", "assign-to-customer-text": "Por favor, seleccione algún cliente para asignar al(los) panel(es).", "assign-to-customer": "Asignar a cliente", "unassign-from-customer": "Desasignar del cliente", @@ -655,19 +813,27 @@ "assign-to-customers": "Asignar Panel / Paneles a Clientes", "assign-to-customers-text": "Selecciona los clientes para asignar los paneles", "unassign-from-customers": "Desasignar Panel / Paneles de clientes", - "unassign-from-customers-text": "Selecciona los clientes para desasignar los paneles", + "unassign-from-customers-text": "Selecciona los clientes para desasignar los paneles", "no-dashboards-text": "Ningún panel encontrado", "no-widgets": "Ningún widget configurado", "add-widget": "Agregar nuevo widget", "title": "Título", + "image": "Imagen de panel", + "mobile-app-settings": "Ajustes de aplicación móvil", + "mobile-order": "Órden de paneles en aplicación móvil", + "mobile-hide": "Ocultar panel en aplicación móvil", + "update-image": "Actualizar imagen de panel", + "take-screenshot": "Captura de pantalla", "select-widget-title": "Seleccionar widget", + "select-widget-value": "{{title}}: seleccionar widget", "select-widget-subtitle": "Lista de tipos de widgets disponibles", "delete": "Eliminar panel", "title-required": "Título requerido.", + "title-max-length": "Título debe ser menor de 256", "description": "Descripción", "details": "Detalles", "dashboard-details": "Detalles del panel", - "add-dashboard-text": "Agregar nuevo panel", + "add-dashboard-text": "Agregar nuevo panel", "assign-dashboards": "Asignar paneles", "assign-new-dashboard": "Asignar nuevo panel", "assign-dashboards-text": "Asignar { count, plural, 1 {1 panel} other {# paneles} } al cliente", @@ -705,8 +871,12 @@ "background-image": "Imagen de fondo", "background-size-mode": "Modo tamaño de fondo", "no-image": "No se ha seleccionado ningúna imagen", + "empty-image": "Sin imagen", "drop-image": "Suelte una imagen o haga clic para seleccionar un archivo para cargar.", + "maximum-upload-file-size": "Tamaño máximo de fichero: {{ size }}", + "cannot-upload-file": "No es posible subir el fichero", "settings": "Ajustes", + "layout-settings": "Ajustes de diseño", "columns-count": "Número de columnas", "columns-count-required": "Número de columnas requerido.", "min-columns-count-message": "Solo se permite un número mínimo de 10 columnas.", @@ -729,14 +899,23 @@ "mobile-row-height-required": "Altura de fila requerida.", "min-mobile-row-height-message": "Sólo se permiten 5 píxeles como altura mínima de fila (móvil).", "max-mobile-row-height-message": "Sólo se permiten 200 píxeles como altura máxima de fila (móvil).", + "title-settings": "Ajustes de título", "display-title": "Mostrar título del panel", - "toolbar-always-open": "Mantener la barra de herramientas abierta", "title-color": "Color del título", + "toolbar-settings": "Ajustes de la barra de herramientas", + "hide-toolbar": "Ocultar barra de herramientas", + "toolbar-always-open": "Mantener la barra de herramientas abierta", "display-dashboards-selection": "Mostrar selección de paneles", "display-entities-selection": "Mostrar selección de entidades", "display-filters": "Mostrar filtros", "display-dashboard-timewindow": "Mostrar ventana de tiempo", "display-dashboard-export": "Mostrar exportar", + "display-update-dashboard-image": "Mostrar actualización de imagen", + "dashboard-logo-settings": "Ajustes del logotipo del panel", + "display-dashboard-logo": "Mostrar logotipo en pantalla completa", + "dashboard-logo-image": "Imagen logotipo", + "advanced-settings": "Ajustes avanzados", + "dashboard-css": "CSS del panel", "import": "Importar panel", "export": "Exportar panel", "export-failed-error": "Imposible exportar panel: {{error}}", @@ -783,11 +962,15 @@ "select-state": "Seleccionar estado destino (target state)", "state-controller": "Controlador de estados", "search": "Buscar paneles", - "selected-dashboards": "{ count, plural, 1 {1 panel} other {# paneles} } seleccionados", + "selected-dashboards": "{count, plural, 1 {1 panel} other {# paneles} } seleccionados", + "home-dashboard": "Panel principal", + "home-dashboard-hide-toolbar": "Ocultar barra de herramientas en panel principal", "unassign-dashboard-from-edge-text": "Después de la confirmación, el tablero no será asignado y el borde no podrá acceder a él", - "unassign-dashboards-from-edge-text": "Después de la confirmación, se anulará la asignación de todos los paneles seleccionados y no serán accesibles por de borde", + "unassign-dashboards-from-edge-title": "Estas seguro de desasignar { count, plural, 1 {1 panel} other {# paneles} }?", + "unassign-dashboards-from-edge-text": "Después de la confirmación, se anulará la asignación de todos los paneles seleccionados y no serán accesibles por de borde", "assign-dashboard-to-edge": "Asignar panel(es) al borde", - "assign-dashboard-to-edge-text": "Por favor selecciona los paneles para asignar al borde" + "assign-dashboard-to-edge-text": "Por favor selecciona los paneles para asignar al borde", + "non-existent-dashboard-state-error": "El panel con id de estado \"{{ stateId }}\" no se ha encontrado" }, "datakey": { "settings": "Ajustes", @@ -809,7 +992,20 @@ "maximum-timeseries-or-attributes": "Máximo { count, plural, 1 {1 timeseries/atributo es permitido.} other {# timeseries/atributos son permitidos} }", "alarm-fields-required": "Campos de alarma requeridos.", "function-types": "Tipos de funciones", + "function-type": "Tipos de función", "function-types-required": "Tipos de funciones requerido.", + "alarm-keys": "Claves de Alarmas", + "alarm-key": "Clave de Slarma", + "alarm-key-functions": "Funciones de claves de Alarmas", + "alarm-key-function": "Función de clave de Alarma", + "latest-keys": "Últimas claves", + "latest-key": "Última clave", + "latest-key-functions": "Funciones de últimas claves", + "latest-key-function": "Función de última clave", + "timeseries-keys": "Claves de series de tiempo", + "timeseries-key": "Clave de series de tiempo", + "timeseries-key-functions": "Funciones de series de tiempo", + "timeseries-key-function": "Función de serie de tiempo", "maximum-function-types": "Máximo { count, plural, 1 {1 tipo de función está permitida.} other {# tipos de funciones están permitidos} }", "time-description": "hora del valor actual", "value-description": "el valor actual", @@ -820,6 +1016,7 @@ "datasource": { "type": "Típo de fuente de datos", "name": "Nombre", + "label": "Etiqueta", "add-datasource-prompt": "Por favor, agrega una fuente de datos" }, "details": { @@ -835,6 +1032,7 @@ "management": "Gestión de Dispositivos", "view-devices": "Ver Dispositivos", "device-alias": "Alias de dispositivo", + "device-type-max-length": "El tipo de dispositivo debe ser menor de 256", "aliases": "Alias de dispositivos", "no-alias-matching": "'{{alias}}' no encontrado.", "no-aliases-found": "Ningún alias encontrado.", @@ -850,6 +1048,7 @@ "remove-alias": "Eliminar alias", "add-alias": "Agregar alias", "name-starts-with": "Nombre empieza con", + "help-text": "Usar '%' de acuerdo a las necesidades: '%nombre_dispositivo_contiene%', '%nombre_dispositivo_termina_en', 'nombre_dispositivo_empieza_con'.", "device-list": "Lista de dispositivos", "use-device-name-filter": "Usar filtro", "device-list-empty": "Ningún dispositivo seleccionado.", @@ -859,6 +1058,8 @@ "assign-to-customer": "Asignar a cliente", "assign-device-to-customer": "Asignar dispositivo(s) a Cliente", "assign-device-to-customer-text": "Por favor, seleccione los dispositivos que serán asignados al cliente", + "assign-device-to-edge-title": "Asignar Dispositivo(s) a Borde", + "assign-device-to-edge-text": "Selecciona los dispositivos a asignar al Borde", "make-public": "Hacer dispositivo público", "make-private": "Hacer dispositivo privado", "no-devices-text": "Ningún dispositivo encontrado", @@ -874,6 +1075,9 @@ "unassign-from-customer": "Desasignar del cliente", "unassign-devices": "Desasignar dispositivos", "unassign-devices-action-title": "Desasignar { count, plural, 1 {1 dispositivo} other {# dispositivos} } del cliente", + "unassign-device-from-edge-title": "¿Está seguro de que desea desasignar el dispositivo '{{deviceName}}'?", + "unassign-device-from-edge-text": "Después de la confirmación, el dispositivo no será asignado y el borde no podrá acceder a él", + "unassign-devices-from-edge": "Desasignar dispositivos del Borde", "assign-new-device": "Asignar nuevo dispositivo", "make-public-device-title": "¿Hacer el dispositivo '{{deviceName}}' público?", "make-public-device-text": "Tras la confirmación, el dispositivo y la información relacionada serán públicos y podrá ser accesible por otros.", @@ -891,18 +1095,51 @@ "unassign-devices-title": "¿Desasignar { count, plural, 1 {1 dispositivo} other {# dispositivos} }?", "unassign-devices-text": "Tras la confirmación, los dispositivos seleccionados serán desasignados y no podrán ser accedidos por el cliente.", "device-credentials": "Credenciales del dispositivo", - "credentials-type": "Tipo de credencial", + "loading-device-credentials": "Cargando credenciales del dispositivo...", + "credentials-type": "Tipo de credenciales", "access-token": "Tóken de acceso", - "access-token-required": "Access token requerido.", - "access-token-invalid": "Access token debe tener entre 1 a 32 caracteres.", + "access-token-required": "Tóken de acceso requerido.", + "access-token-invalid": "Tóken de acceso debe tener entre 1 a 32 caracteres.", + "certificate-pem-format": "Certificado en formato PEM", + "certificate-pem-format-required": "Certificado requerido.", + "lwm2m-security-config": { + "identity": "Identidad Cliente", + "identity-required": "Identidad Cliente requerida.", + "identity-tooltip": "La identidad PSK es un identificador PSK arbitrario hasta 128 bytes, como se describe en el estándar [RFC7925].\nEl identificador PSK DEBE ser convertido a un string y después codificado en octetos usando UTF-8.", + "client-key": "Clave Cliente", + "client-key-required": "Clave Cliente requerida.", + "client-key-tooltip-prk": "La clave RPK o id DEBE estar conforme al estándar [RFC7250] y codificada a un formato Base64!", + "client-key-tooltip-psk": "La clave PSK DEBE estar conforme al estándar [RFC4279] y en formato HexDec de: 32, 64 o 128 caracteres!", + "endpoint": "Nombre Endpoint Cliente", + "endpoint-required": "Nombre Endpoint requerido.", + "client-public-key": "Clave pública Cliente", + "client-public-key-hint": "Si la clave pública está vacía, se usará el certificado de confianza", + "client-public-key-tooltip": "La clave pública X509 debe estar codificada en formato DER X509v3 y soportar exclusivamente el algoritmo EC y codificada en formato Base64!", + "mode": "Modo de Seguridad", + "client-tab": "Configuración de Seguridad del cliente", + "client-certificate": "Certificado de Cliente", + "bootstrap-tab": "Cliente Bootstrap", + "bootstrap-server": "Servidor Bootstrap", + "lwm2m-server": "Servidor LwM2M", + "client-publicKey-or-id": "Id o clave pública de cliente", + "client-publicKey-or-id-required": "Id o clave pública requerida.", + "client-publicKey-or-id-tooltip-psk": "La clave pública PSK es un identificador PSK arbitrario hasta 128 bytes, como se describe en el estándar [RFC7925].\nEl identificador PSK DEBE ser convertido a un string y después codificado en octetos usando UTF-8.", + "client-publicKey-or-id-tooltip-rpk": "La clave pública RPK o id DEBE estar conforme al estándar [RFC7250] y codificada a un formato Base64!", + "client-publicKey-or-id-tooltip-x509": "La clave pública X509 debe estar codificada en formato DER X509v3 y soportar exclusivamente el algoritmo EC y codificada en formato Base64", + "client-secret-key": "Clave secreta de Cliente", + "client-secret-key-required": "Clave secreta requerida.", + "client-secret-key-tooltip-psk": "La clave PSK debe ser en el estándar [RFC4279] y en formato HexDec de: 32, 64 o 128 caracteres!", + "client-secret-key-tooltip-prk": "La clave RPK debe estar en formato PKCS_8 (Codificación DER, estándar [RFC5958]) y luego codificada en formato Base64!", + "client-secret-key-tooltip-x509": "La clave X509 debe estar en formato PKCS_8 (Codificación DER, estándar [RFC5958]) y luego codificada en formato Base64!" + }, "client-id": "ID Cliente", "client-id-pattern": "Contiene carácter inválido.", "user-name": "Nombre Usuario", "user-name-required": "Se requiere nombre de usuario.", "client-id-or-user-name-necessary": "El ID Cliente y/o el Nombre de usuario son necesarios", "password": "Contraseña", - "secret": "Secreta", - "secret-required": "Secreta requerida.", + "secret": "Secreto", + "secret-required": "Secreto requerido.", "device-type": "Tipo de dispositivo", "device-type-required": "Tipo de dispositivo requerido.", "select-device-type": "Seleccionar tipo de dispositivo", @@ -913,6 +1150,8 @@ "device-types": "Tipos de dispositivo", "name": "Nombre", "name-required": "El nombre es requerido.", + "name-max-length": "El nombre debe ser menor de 256", + "label-max-length": "La etiqueta debe ser menor de 256", "description": "Descripción", "label": "Etiqueta", "events": "Eventos", @@ -946,9 +1185,6 @@ "customer-to-assign-device": "Cliente al que asignar el dispositivo", "add-credentials": "Añadir credencial" }, - "assign-device-to-edge-text": "Seleccione los dispositivos para asignar al borde", - "unassign-device-from-edge-title": "¿Está seguro de que desea desasignar el dispositivo '{{deviceName}}'?", - "unassign-device-from-edge-text": "Después de la confirmación, el dispositivo no será asignado y el borde no podrá acceder a él", "unassign-devices-from-edge-title": "¿Está seguro de que desea desasignar {count, plural, 1 {1 dispositivo} other {# dispositivos} }?", "unassign-devices-from-edge-text": "Después de la confirmación, todos los dispositivos seleccionados quedarán sin asignar y el borde no podrá acceder a ellos." }, @@ -968,6 +1204,7 @@ "set-default": "Hacer perfil por defecto", "delete": "Borrar perfil de dispositivo", "copyId": "Copiar ID de perfil", + "name-max-length": "El nombre debe ser menor de 256", "new-device-profile-name": "Nombre de perfil", "new-device-profile-name-required": "Se requiere nombre de perfil.", "name": "Nombre", @@ -975,19 +1212,26 @@ "type": "Tipo de perfil", "type-required": "Se requiere tipo de perfil.", "type-default": "Por defecto", + "image": "Imagen del perfil de dispositivo", "transport-type": "Tipo de transporte", "transport-type-required": "Se requiere tipo de transporte.", "transport-type-default": "Por defecto", "transport-type-default-hint": "Soporta transportes por MQTT básico, HTTP y CoAP", "transport-type-mqtt": "MQTT", "transport-type-mqtt-hint": "Activa ajustes avanzados de transporte MQTT", + "transport-type-coap": "CoAP", + "transport-type-coap-hint": "Activa ajustes avanzados del transporte CoAP", "transport-type-lwm2m": "LWM2M", "transport-type-lwm2m-hint": "Transporte LWM2M", + "transport-type-snmp": "SNMP", + "transport-type-snmp-hint": "Configuración transporte SNMP", "description": "Descripción", "default": "Defecto", "profile-configuration": "Configuración de perfil", "transport-configuration": "Configuración de transporte", "default-rule-chain": "Cadena de reglas por defecto", + "mobile-dashboard": "Panel móvil", + "mobile-dashboard-hint": "Usado por la aplicación móvil como panel de detalle", "select-queue-hint": "Selecciona desde el desplegable o añade un nombre personalizado.", "delete-device-profile-title": "Eliminar el perfil '{{deviceProfileName}}'?", "delete-device-profile-text": "Atención, tras la confirmación el perfil y todos sus datos serán borrados e irrecuperables.", @@ -1002,18 +1246,43 @@ "mqtt-device-payload-type": "Payload de dispositivo MQTT", "mqtt-device-payload-type-json": "JSON", "mqtt-device-payload-type-proto": "Protobuf", + "mqtt-enable-compatibility-with-json-payload-format": "Activar compatibilidad con otros formatos de payload.", + "mqtt-enable-compatibility-with-json-payload-format-hint": "Si se activa, la plataforma usará un formato de payload Protobuf por defecto. Si el parseo falla, la plataforma intentará usar el formato JSON. Útil para retrocompatibilidad durante actualizaciones de firmware. Por ejemplo, si la versión inicial de firmware usa JSON y una versión posterior usa Protobuf. Durante el proceso de actualización de firmware a los dispositivos, se requiere soportar Protobuf y JSON al mismo tiempo. El modo de retrocompatibilidad introduce una pequeña degradación en el rendimiento, es recomendable desactivarlo una vez que todos los dispositivos estén actualizados.", + "mqtt-use-json-format-for-default-downlink-topics": "Usar formato JSON para los downlink", + "mqtt-use-json-format-for-default-downlink-topics-hint": "When enabled, the platform will use Json payload format to push attributes and RPC via the following topics: v1/devices/me/attributes/response/$request_id, v1/devices/me/attributes, v1/devices/me/rpc/request/$request_id, v1/devices/me/rpc/response/$request_id. This setting does not impact attribute and rpc subscriptions sent using new (v2) topics: v2/a/res/$request_id, v2/a, v2/r/req/$request_id, v2/r/res/$request_id. Where $request_id is an integer request identifier.", + "mqtt-send-ack-on-validation-exception": "Enviar PUBACK en error de validación al publicar (PUBLISH)", + "mqtt-send-ack-on-validation-exception-hint": "By default, the platform will close the MQTT session on message validation failure. When enabled, the platform will send publish acknowledgment instead of closing the session.", + "snmp-add-mapping": "Añadir mapeado SNMP", + "snmp-mapping-not-configured": "No hay mapeado OID a series de tiempo/telemetría configurado", + "snmp-timseries-or-attribute-name": "Nombre Series de tiempo/nombre atributo para mapeado", + "snmp-timseries-or-attribute-type": "Tipo Series de tiempo/nombre atributo para mapeado", + "snmp-method-pdu-type-get-request": "GetRequest", + "snmp-method-pdu-type-get-next-request": "GetNextRequest", + "snmp-oid": "OID", + "transport-device-payload-type-json": "JSON", + "transport-device-payload-type-proto": "Protobuf", "mqtt-payload-type-required": "Se requiere tipo de Payload.", + "coap-device-type": "Tipo de dispositivo CoAP", + "coap-device-payload-type": "Payload dispositivo CoAP", + "coap-device-type-required": "Se requiere tipo de dispositivo CoAP.", + "coap-device-type-default": "Por defecto", + "coap-device-type-efento": "Efento NB-IoT", "support-level-wildcards": "Se soportan los wilcards únicos [+] y multi-nivel [#].", "telemetry-topic-filter": "Filtro de topic en telemetría", "telemetry-topic-filter-required": "Se requiere filtro de topic (telemetría).", "attributes-topic-filter": "Filtro de topic en atributos", "attributes-topic-filter-required": "Se requiere filtro de topic (atributos).", - "telemetry-proto-schema": "Proto schema de telemetría", - "telemetry-proto-schema-required": "Se requiere proto schema de telemetría.", - "attributes-proto-schema": "Proto schema de atributos", - "attributes-proto-schema-required": "Se requiere proto schema de atributos.", + "telemetry-proto-schema": "Proto esquema de telemetría", + "telemetry-proto-schema-required": "Se requiere proto esquema de telemetría.", + "attributes-proto-schema": "Proto esquema de atributos", + "attributes-proto-schema-required": "Se requiere proto esquema de atributos.", + "rpc-response-proto-schema": "Proto esquema de respuesta RPC", + "rpc-response-proto-schema-required": "Se requiere proto esquema de respuesta RPC.", "rpc-response-topic-filter": "Filtro de topic de respuesta RPC", - "rpc-response-topic-filter-required": "Se requiere fitro de respuesta RPC.", + "rpc-response-topic-filter-required": "Se requiere filtro de topic respuesta RPC.", + "rpc-request-proto-schema": "Proto esquema de petición RPC", + "rpc-request-proto-schema-required": "Se requiere proto esquema de petición RPC.", + "rpc-request-proto-schema-hint": "Las peticiones RPC deben contener siempre los siguientes campos: string method = 1; int32 requestId = 2; y params = 3 para cualquier tipo de datoas.", "not-valid-pattern-topic-filter": "No es un patrón de filtro válido", "not-valid-single-character": "Uso inválido de wildcard único", "not-valid-multi-character": "Uso inválido de wildcard multi-nivel", @@ -1027,6 +1296,7 @@ "alarm-type": "Tipo de alarma", "alarm-type-required": "Se requiere tipo de alarma.", "alarm-type-unique": "El tipo de alarma, debe ser único dentro de las reglas de alarma del perfil de dispositivo.", + "alarm-type-max-length": "El tipo de alarma debe ser menor de 256", "create-alarm-pattern": "Crear alarma {{alarmType}}", "create-alarm-rules": "Crear reglas de alarma", "no-create-alarm-rules": "No hay condiciones de creación de alarma configuradas", @@ -1046,14 +1316,20 @@ "condition-duration-time-unit-required": "Se requiere una unidad de tiempo.", "advanced-settings": "Ajustes avanzados", "alarm-rule-details": "Detalles", + "alarm-rule-details-hint": "Ayuda: usa ${nombredeClave} para sustituir los valores de atributos o telemetrías usadas en la condición de la regla de alarma.", "add-alarm-rule-details": "Añadir detalles", + "alarm-rule-mobile-dashboard": "Panel Móvil", + "alarm-rule-mobile-dashboard-hint": "Usado por la aplicación móvil como panel de detalle de alarmas", + "alarm-rule-no-mobile-dashboard": "No hay panel seleccionado", "propagate-alarm": "Propagar alarma", "alarm-rule-relation-types-list": "Tipos de relación para propagar", "alarm-rule-relation-types-list-hint": "Si no está seleccionado 'propagar relaciones', las alarmas serán propagadas sin filtrar por relación.", + "propagate-alarm-to-owner": "Propagar alarma al propietario de la entidad (Cliente o Administrador)", + "propagate-alarm-to-tenant": "Propagar alarma a Administrador", "alarm-details": "Detalles de alarma", "alarm-rule-condition": "Condiciones de regla de alarma", "enter-alarm-rule-condition-prompt": "Por favor, añade una condición de alarma", - "edit-alarm-rule-condition": "Editar condición de alarma", + "edit-alarm-rule-condition": "Editar condición de alarma", "device-provisioning": "Aprovisionamiento de dispositivos", "provision-strategy": "Estrategia de aprovisionamiento", "provision-strategy-required": "Se requiere estrategia de aprovisionamiento.", @@ -1073,6 +1349,7 @@ "condition-type-simple": "Simple", "condition-type-duration": "Duración", "condition-during": "Durante {{during}}", + "condition-during-dynamic": "Durante \"{{ attribute }}\" ({{during}})", "condition-type-repeating": "Repetitiva", "condition-type-required": "Se requiere tipo de condición.", "condition-repeating-value": "Nº de eventos", @@ -1080,12 +1357,13 @@ "condition-repeating-value-pattern": "Nº de eventos debe ser un número entero.", "condition-repeating-value-required": "Se requiere Nº de eventos.", "condition-repeat-times": "Repetición { count, plural, 1 {1 vez} other {# veces} }", + "condition-repeat-times-dynamic": "Repetición \"{ atributo }\" ({ count, plural, 1 {1 vez} other {# veces} })", "schedule-type": "Tipo de horario", "schedule-type-required": "Tipo de horario requerido.", "schedule": "Horario", "edit-schedule": "Editar horario de alarma", "schedule-any-time": "Siempre activo", - "schedule-specific-time": "Activo en una hora específica", + "schedule-specific-time": "Activo en una hora específica", "schedule-custom": "Personalizado", "schedule-day": { "monday": "Lunes", @@ -1098,9 +1376,205 @@ }, "schedule-days": "Días", "schedule-time": "Hora", - "schedule-time-from": "De", + "schedule-time-from": "Desde", "schedule-time-to": "Hasta", - "schedule-days-of-week-required": "Debe ser seleccionado por lo menos un día de la semana." + "schedule-days-of-week-required": "Seleccionar por lo menos un día de la semana.", + "create-device-profile": "Crear un nuevo perfil de dispositivo", + "import": "Importar perfil de dispositivo", + "export": "Exportar perfil de dispositivo", + "export-failed-error": "No ha sido posible exportar el perfil de dispositivo: {{error}}", + "device-profile-file": "Archivo de perfil de dispositivo", + "invalid-device-profile-file-error": "No ha sido posible importar perfil de dispositivo: Estructura de datos inválida.", + "power-saving-mode": "Modo de ahorro de energía (PSM)", + "power-saving-mode-type": { + "default": "Usar el del perfil del dispositivo", + "psm": "Power Saving Mode (PSM)", + "drx": "Discontinuous Reception (DRX)", + "edrx": "Extended Discontinuous Reception (eDRX)" + }, + "edrx-cycle": "Ciclo eDRX", + "edrx-cycle-required": "Se requiere ciclo eDRX.", + "edrx-cycle-pattern": "El ciclo eDRX debe ser un número entero positivo.", + "edrx-cycle-min": "El mínimo de ciclo eDRX es de {{ min }} segundos.", + "paging-transmission-window": "Ventana de transmisión (Paging Transmission Window)", + "paging-transmission-window-required": "Se requiere ventana de transmisión (Paging transmission window).", + "paging-transmission-window-pattern": "Ventana de transmision debe ser un número entero positivo.", + "paging-transmission-window-min": "El mínimo de ventana de transmisión es de {{ min }} segundos.", + "psm-activity-timer": "Tiempo Actividad PSM (PSM Activity Timer)", + "psm-activity-timer-required": "Se requiere el tiempo de actividad PSM.", + "psm-activity-timer-pattern": "Tiempo de actividad PSM debe ser un número entero positivo.", + "psm-activity-timer-min": "El tiempo de actividad PSM mínimo es de {{ min }} segundos.", + "lwm2m": { + "object-list": "Lista de objetos", + "object-list-empty": "No hay objetos seleccionados.", + "no-objects-found": "No se encontraron objetos.", + "no-objects-matching": "No hay objetos que coincidan con '{{object}}'.", + "model-tab": "Modelo LWM2M", + "add-new-instances": "Añadir nueva instancia", + "instances-list": "Lista de Instancias", + "instances-list-required": "Se requiere lista de instancias.", + "instance-id-pattern": "El id de instancia debe ser un número entero positivo.", + "instance-id-max": "El id máximo de instancia es {{max}}.", + "instance": "Instancia", + "resource-label": "#ID Nombre Recurso", + "observe-label": "Observar", + "attribute-label": "Atributo", + "telemetry-label": "Telemetría", + "edit-observe-select": "Para editar observar, selecciona telemetría o atributo", + "edit-attributes-select": "Para editar atributo, selecciona telemetría o atributo", + "no-attributes-set": "No hay atributos ajustados", + "key-name": "Nombre de clave", + "key-name-required": "Se requiere nombre de clave", + "attribute-name": "Nombre Atributo", + "attribute-name-required": "Se requiere nombre de atributo.", + "attribute-value": "Valor de atributo", + "attribute-value-required": "Se requiere valor de atributo.", + "attribute-value-pattern": "El valor del atributo debe ser un número entero positivo.", + "edit-attributes": "Edita atributos: {{ name }}", + "view-attributes": "Ver atributos: {{ name }}", + "add-attribute": "Añadir atributo", + "edit-attribute": "Editar atributo", + "view-attribute": "Ver atributos", + "remove-attribute": "Borrar atributos", + "delete-server-text": "Atención, tras la confirmación la configuración del servidor se borrará y será irrecuperable.", + "delete-server-title": "Estas seguro de borrar el servidor?", + "mode": "Configuración de seguridad", + "bootstrap-tab": "Bootstrap", + "bootstrap-server-legend": "Servidor Bootstrap (ShortId...)", + "lwm2m-server-legend": "Servidor LwM2M (ShortId...)", + "server": "Servidor", + "short-id": "Short server ID", + "short-id-tooltip": "Id corto del servidor. Usado como enlace para asociar las instancias de objetos del servidor.\nEste identificador sirve para identificar únicamente cada servidor LwM2M configurado para el cliente LwM2M.\nLos recursos DEBEN ser ajustados cuando el servidor Bootstrap tenga un valor de 'false'.\nLos valores ID:0 and ID:65535 NO DEBEN ser usados para identificar al servidor LwM2M.", + "short-id-required": "Se requiere Short server ID.", + "short-id-range": "Short server ID debe estar en un rango de 1 a 65534.", + "short-id-pattern": "Short server ID debe ser un número entero positivo.", + "lifetime": "Ciclo de vida registro de cliente (Registration Lifetime)", + "lifetime-required": "Se requiere ciclo de vida.", + "lifetime-pattern": "Ciclo de vida debe ser un número entero positivo.", + "default-min-period": "Periodo mínimo entre notificaciones (s)", + "default-min-period-tooltip": "Valor predeterminado que el cliente LwM2M debe usar para el período mínimo de una Observación en ausencia de éste parámetro incluido en una observación.", + "default-min-period-required": "Período mímino requerido.", + "default-min-period-pattern": "El período mínimo debe ser un número entero positivo.", + "notification-storing": "Grabado de notificaciones cuando esté desactivado u offline", + "binding": "Binding", + "binding-type": { + "u": "U: El cliente es alcanzable por UDP en cualquier momento.", + "m": "M: El cliente es alcanzable por MQTT en cualquier momento.", + "h": "H: El cliente es alcanzable por HTTP en cualquier momento.", + "t": "T: El cliente es alcanzable por TCP en cualquier momento.", + "s": "S: El cliente es alcanzable por SMS en cualquier momento.", + "n": "N: El cliente DEBE enviar las respuestas a las peticiones sobre el modo Non-IP (soportado desde la versión LWM2M 1.1).", + "uq": "UQ: Conexión UDP en modo de cola (no soportado desde la versión LWM2M 1.1)", + "uqs": "UQS: Conexiones UDP y SMS activas, UDP en modo cola, SMS en modo estándar (no soportado desde la versión LWM2M 1.1)", + "tq": "TQ: Conexión TCP en modo de cola (no soportado desde la versión LWM2M 1.1)", + "tqs": "TQS: Conexiones TCP y SMS activas, TCP en modo cola, SMS en modo estándar (no soportado desde la versión LWM2M 1.1)", + "sq": "SQ: Conexión SMS en modo de cola (no soportado desde la versión LWM2M 1.1)" + }, + "binding-tooltip": "This is the list in the\"binding\" resource of the LwM2M server object - /1/x/7.\nIndicates the supported binding modes in the LwM2M Client.\nThis value SHOULD be the same as the value in the “Supported Binding and Modes” resource in the Device Object (/3/0/16).\nWhile multiple transports are supported, only one transport binding can be used during the entire Transport Session.\nAs an example, when UDP and SMS are both supported, the LwM2M Client and the LwM2M Server can choose to communicate either over UDP or SMS during the entire Transport Session.", + "bootstrap-server": "Servidor Bootstrap", + "lwm2m-server": "Servidor LwM2M", + "include-bootstrap-server": "Incluir actualizaciónes del servidor Bootstrap", + "bootstrap-update-title": "Ya has configurado un servidor Bootstrap. Estás seguro de que quieres excluir las actualizaciones?", + "bootstrap-update-text": "Atención, tras la confirmación la configuración del servidor Bootstrap será irrecuperable.", + "server-host": "Host", + "server-host-required": "Se requiere Host.", + "server-port": "Puerto", + "server-port-required": "Se requiere Puerto.", + "server-port-pattern": "El puerto debe ser un número entero positivo.", + "server-port-range": "El puerto debe comprender en el rango 1 a 65535.", + "server-public-key": "Clave Pública del Servidor", + "server-public-key-required": "Se requiere la Clave Pública del servidor.", + "client-hold-off-time": "Tiempo de espera (Hold Off Time)", + "client-hold-off-time-required": "Se requiere tiempo de espera.", + "client-hold-off-time-pattern": "El tiempo de espera debe ser un número entero positivo.", + "client-hold-off-time-tooltip": "El tiempo de espera se usa sólamente con un servidor Bootstrap", + "account-after-timeout": "Cuenta tras el tiempo de espera", + "account-after-timeout-required": "Se requiere Cuenta tras el tiempo de espera.", + "account-after-timeout-pattern": "Cuenta tras el tiempo de espera debe ser un número entero positivo.", + "account-after-timeout-tooltip": "Bootstrap-Server Account after the timeout value given by this resource.", + "server-type": "Tipo de servidor", + "add-new-server-title": "Añadir nueva configuración", + "add-server-config": "Añadir configuración de servidor", + "add-lwm2m-server-config": "Añadir Servidor LwM2M", + "no-config-servers": "No hay servidores configurados", + "others-tab": "Otros ajustes", + "client-strategy": "Estrategia del cliente en la conexión", + "client-strategy-label": "Estrategia", + "client-strategy-only-observe": "Realizar un request para observar al cliente tras la conexión inicial", + "client-strategy-read-all": "Leer todos los recursos y realizar un request para observar al cliente tras el registro", + "fw-update": "Actualización de Firmware", + "fw-update-strategy": "Estrategia de actualización de Firmware", + "fw-update-strategy-data": "Enviar actualización de firmware como un fichero binario usando el Objeto 19 y el Recurso 0 (Data)", + "fw-update-strategy-package": "Enviar actualización de firmware como un fichero binario usando el Objeto 5 y el Recurso 0 (Package)", + "fw-update-strategy-package-uri": "Auto-generar una URL CoAP única para la descarga del paquete y enviarla usando el Objeto 5 y el Recurso 1 (Package URI)", + "sw-update": "Actualización de Software", + "sw-update-strategy": "Estrategia de Actualización de Software", + "sw-update-strategy-package": "Enviar como un fichero binario usando el Objeto 9 y el Recurso 2 (Package)", + "sw-update-strategy-package-uri": "Auto-generar una URL CoAP única para la descarga del paquete y enviarla usando el Objeto 9 y el Recurso 3 (Package URI)", + "fw-update-resource": "Recurso CoAP Actualización de Firmware", + "fw-update-resource-required": "Se requiere el Recurso CoAP Actualización de Firmware.", + "sw-update-resource": "Recurso CoAP Actualización de Software", + "sw-update-resource-required": "Se requiere el Recurso CoAP Actualización de Software.", + "config-json-tab": "Configuracion Json Perfil de dispositivo", + "attributes-name": { + "min-period": "Período mínimo", + "max-period": "Período máximo", + "greater-than": "Mayor que", + "less-than": "Menor que", + "step": "Paso", + "min-evaluation-period": "Período mínimo de evaluación", + "max-evaluation-period": "Período máximo de evaluación" + }, + "composite-operations-support": "Soporta operaciones Lectura/Escritura/Observación Compuestas" + }, + "snmp": { + "add-communication-config": "Añadir configuración de comunicaciones", + "add-mapping": "Añadir mapeado", + "authentication-passphrase": "Frase de contraseña", + "authentication-passphrase-required": "Se requiere frase de contraseña.", + "authentication-protocol": "Protocolo de autenticación", + "authentication-protocol-required": "Se requiere Protocolo de autenticación.", + "communication-configs": "Configuracion de comunicaciones", + "community": "Community", + "community-required": "Se requiere Community.", + "context-name": "Nombre de contexto", + "data-key": "Clave de datos", + "data-key-required": "Se requiere clave de datos.", + "data-type": "Tipo de datos", + "data-type-required": "Se requiere tipo de datos.", + "engine-id": "Engine ID", + "host": "Host", + "host-required": "Se requiere Host.", + "oid": "OID", + "oid-pattern": "Formato OID inválido.", + "oid-required": "Se requiere OID.", + "please-add-communication-config": "Por favor, añadir configuración de comunicación", + "please-add-mapping-config": "Por favor, añadir configuración de mapeado", + "port": "Puerto", + "port-format": "Formato de puerto inválido.", + "port-required": "Se requiere puerto.", + "privacy-passphrase": "Frase de clave de privacidad", + "privacy-passphrase-required": "Se requiere Frase de clave de privacidad.", + "privacy-protocol": "Protocolo privacidad", + "privacy-protocol-required": "Se requiere Protocolo privacidad.", + "protocol-version": "Versión protocolo", + "protocol-version-required": "Se requiere versión protocolo.", + "querying-frequency": "Frecuencia de peticiones, ms", + "querying-frequency-invalid-format": "Frecuencia de peticiones debe ser un número entero positivo.", + "querying-frequency-required": "Se requiere frecuencia de peticiones.", + "retries": "Reintentos", + "retries-invalid-format": "Reintentos debe ser un número entero positivo.", + "retries-required": "Se requiere reintentos.", + "scope": "Alcance", + "scope-required": "Se requiere alcance.", + "security-name": "Nombre seguridad", + "security-name-required": "Se requiere Nombre seguridad.", + "timeout-ms": "Timeout, ms", + "timeout-ms-invalid-format": "Timeout debe ser un número entero positivo.", + "timeout-ms-required": "Se requiere timeout.", + "user-name": "Usuario", + "user-name-required": "Se requiere usuario." + } }, "dialog": { "close": "Cerrar diálogo" @@ -1113,6 +1587,9 @@ "edge": "Borde", "edge-instances": "Instancias de Borde", "edge-file": "Archivo de borde", + "name-max-length": "El nombre debe ser menor de 256", + "label-max-length": "La etiqueta debe ser menor de 256", + "type-max-length": "El tipo debe ser menor de 256", "management": "Gestión de bordes", "no-edges-matching": "No se encontraron bordes que coincidan con '{{entity}}'", "add": "Agregar borde", @@ -1125,7 +1602,7 @@ "delete-edges-title": "¿Está seguro de que desea edge {count, plural, 1 {1 borde} other {# bordes} }?", "delete-edges-text": "Tenga cuidado, después de la confirmación se eliminarán todos los bordes seleccionados y todos los datos relacionados se volverán irrecuperables", "name": "Nombre", - "name-starts-with": "Edge name starts with", + "name-starts-with": "Nombre de Borde comienza con", "name-required": "Se requiere nombre", "description": "Descripción", "details": "Detalles", @@ -1133,8 +1610,8 @@ "copy-id": "Copiar ID de borde", "id-copied-message": "El ID de borde se ha copiado al portapapeles", "sync": "Sinc Edge", - "edge-required": "Edge required", - "edge-type": "Type de la bordure", + "edge-required": "Borde Requerido", + "edge-type": "Tipo de Borde", "edge-type-required": "El tipo de borde es requerido.", "event-action": "Información de la entidad", "entity-id": "ID de entidad", @@ -1155,7 +1632,7 @@ "make-public-edge-title": "¿Estás seguro de que quieres hacer público el edge '{{edgeName}}'?", "make-public-edge-text": "Después de la confirmación, el borde y todos sus datos serán públicos y accesibles para otros", "make-private": "Hacer que edge sea privado", - "public": "Public", + "public": "Público", "make-private-edge-title": "¿Está seguro de que desea que el borde '{{edgeName}}' sea privado?", "make-private-edge-text": "Después de la confirmación, el borde y todos sus datos se harán privados y otros no podrán acceder a ellos", "import": "Importar borde", @@ -1198,40 +1675,40 @@ "widget-datasource-error": "Este widget solo admite la fuente de datos de la entidad EDGE" }, "edge-event": { - "type-dashboard": "Dashboard", - "type-asset": "Asset", - "type-device": "Device", - "type-device-profile": "Device Profile", - "type-entity-view": "Entity View", - "type-alarm": "Alarm", - "type-rule-chain": "Rule Chain", - "type-rule-chain-metadata": "Rule Chain Metadata", - "type-edge": "Edge", - "type-user": "User", - "type-customer": "Customer", - "type-relation": "Relation", - "type-widgets-bundle": "Widgets Bundle", - "type-widgets-type": "Widgets Type", - "type-admin-settings": "Admin Settings", - "action-type-added": "Added", - "action-type-deleted": "Deleted", - "action-type-updated": "Updated", - "action-type-post-attributes": "Post Attributes", - "action-type-attributes-updated": "Attributes Updated", - "action-type-attributes-deleted": "Attributes Deleted", - "action-type-timeseries-updated": "Timeseries Updated", - "action-type-credentials-updated": "Credentials Updated", - "action-type-assigned-to-customer": "Assigned to Customer", - "action-type-unassigned-from-customer": "Unassigned from Customer", - "action-type-relation-add-or-update": "Relation Add or Update", - "action-type-relation-deleted": "Relation Deleted", - "action-type-rpc-call": "RPC Call", - "action-type-alarm-ack": "Alarm Ack", - "action-type-alarm-clear": "Alarm Clear", - "action-type-assigned-to-edge": "Assigned to Edge", - "action-type-unassigned-from-edge": "Unassigned from Edge", - "action-type-credentials-request": "Credentials Request", - "action-type-entity-merge-request": "Entity Merge Request" + "type-dashboard": "Panel", + "type-asset": "Activo", + "type-device": "Dispositivo", + "type-device-profile": "Perfil de dispositivo", + "type-entity-view": "Vista de entidad", + "type-alarm": "Alarma", + "type-rule-chain": "Cadena de reglas", + "type-rule-chain-metadata": "Metadatos de Cadena de Reglas", + "type-edge": "Borde", + "type-user": "Usuario", + "type-customer": "Cliente", + "type-relation": "Relación", + "type-widgets-bundle": "Paquete de Widgets", + "type-widgets-type": "Tipos de Widgets", + "type-admin-settings": "Ajustes de Administración", + "action-type-added": "Añadido", + "action-type-deleted": "Borrado", + "action-type-updated": "Actualizado", + "action-type-post-attributes": "Envío de Atributos", + "action-type-attributes-updated": "Atributos Actualizados", + "action-type-attributes-deleted": "Atributos Borrados", + "action-type-timeseries-updated": "Series de tiempo Actualizadas", + "action-type-credentials-updated": "Credenciales Actualizadas", + "action-type-assigned-to-customer": "Asignado a Cliente", + "action-type-unassigned-from-customer": "Desasignado de Cliente", + "action-type-relation-add-or-update": "Añadir o Actualizar relación", + "action-type-relation-deleted": "Relación borrada", + "action-type-rpc-call": "Llamada RPC", + "action-type-alarm-ack": "ACK Alarma", + "action-type-alarm-clear": "Borrado de Alarma", + "action-type-assigned-to-edge": "Asignado a Borde", + "action-type-unassigned-from-edge": "Desasignado de Borde", + "action-type-credentials-request": "Obtención de Credenciales", + "action-type-entity-merge-request": "Unión de entidades" }, "error": { "unable-to-connect": "Imposible conectar con el servidor! Por favor, revise su conexión a internet.", @@ -1241,6 +1718,7 @@ "entity": { "entity": "Entidad", "entities": "Entidades", + "entities-count": "Nº de entidades", "aliases": "Alias de entidad", "entity-alias": "Alias de entidad", "unable-delete-entity-alias-title": "No ha sido posible eliminar el alias de entidad", @@ -1261,6 +1739,7 @@ "no-entities-matching": "No se han encontrado entidades que coincidan con '{{entity}}' .", "no-entity-types-matching": "No se han encontrado tipos de entidad que coincidan con '{{entityType}}' .", "name-starts-with": "Nombre empieza con", + "help-text": "Usar el símbolo '%' de acuerdo a las necesidades: '%entity_name_contains%', '%entity_name_ends', 'entity_starts_with'.", "use-entity-name-filter": "Usar filtro", "entity-list-empty": "No hay entidades seleccionadas.", "entity-type-list-empty": "No hay tipos de entidad seleccionados.", @@ -1350,7 +1829,9 @@ "type-edge": "Borde", "type-edges": "Bordes", "list-of-edges": "{count, plural, 1 {Un borde} other {Lista de # bordes} }", - "edge-name-starts-with": "Bordes cuyos nombres comienzan con '{{prefijo}}'" + "edge-name-starts-with": "Bordes cuyos nombres comienzan con '{{prefijo}}'", + "type-tb-resource": "Recurso", + "type-ota-package": "Paquete OTA" }, "entity-field": { "created-time": "Hora de creación", @@ -1392,6 +1873,7 @@ "remove-alias": "Borrar alias de la vista de entidad", "add-alias": "Añadir alias a la vista de entidad", "name-starts-with": "Nombre de vista de entidad comienza con", + "help-text": "Usar el símbolo '%' de acuerdo a las necesidades: '%entity-view_name_contains%', '%entity-view_name_ends', 'entity-view_starts_with'.", "entity-view-list": "Lista de vistas de entidad", "use-entity-view-name-filter": "Usar el filtro", "entity-view-list-empty": "No hay vistas de entidad seleccionadas.", @@ -1402,6 +1884,7 @@ "assign-to-customer": "Asignar a cliente", "assign-entity-view-to-customer": "Asignar vista de entidad a cliente", "assign-entity-view-to-customer-text": "Por favor, seleccione las vistas de entidad para asignar al cliente", + "assign-entity-view-to-edge-title": "Asignar vista(s) de entidad a Borde", "no-entity-views-text": "No se encontraron vistas de entidad", "assign-to-customer-text": "Por favor, seleccione el cliente para asignar la vista de entidad", "entity-view-details": "Detalles de la vista de entidad", @@ -1435,6 +1918,8 @@ "created-time": "Fecha de creación", "name": "Nombre", "name-required": "Nombre Requerido.", + "name-max-length": "Nombre debe ser menor de 256", + "type-max-length": "Tipo de vista de entidad debe ser menor de 256", "description": "Descripción", "events": "Eventos", "details": "Detalles", @@ -1464,12 +1949,12 @@ "attributes-propagation-hint": "La vista de entidad copiará automáticamente los atributos especificados de la entidad de destino cada vez que guarde o actualice esta vista de entidad. Por razones de rendimiento, los atributos de entidad objetivo no se propagan a la vista de entidad en cada cambio de atributo. Puede habilitar la propagación automática configurando el nodo de la regla \"copiar a la vista\" en su cadena de reglas y vincular los mensajes \"Atributos de la publicación\" y \"Atributos actualizados\" al nuevo nodo de la regla.", "timeseries-data": "Datos de series temporales", "timeseries-data-hint": "Configure las claves de los datos de las series temporales de la entidad de destino que serán accesibles para la vista de la entidad. Los datos de esta serie temporal son de solo lectura.", + "search": "Buscar vistas de entidad", + "selected-entity-views": "{ count, plural, 1 {1 vista de entidad} other {# vistas de entidad} } seleccionadas", "make-public-entity-view-title": "¿Está seguro de que desea que la vista de entidad '{{entityViewName}}' sea pública?", "make-public-entity-view-text": "Tras la confirmación, la vista de la entidad y todos sus datos se harán públicos y accesibles para otros.", "make-private-entity-view-title": "¿Está seguro de que desea que la vista de entidad '{{entityViewName}}' sea privada?", "make-private-entity-view-text": "Tras la confirmación, la vista de la entidad y todos sus datos se harán privados y no serán accesibles para otros.", - "search": "Buscar vistas de entidad", - "selected-entity-views": "{ count, plural, 1 {1 vista de entidad} other {# vistas de entidad} } seleccionadas", "assign-entity-view-to-edge": "Asignar vista (s) de entidad a borde", "assign-entity-view-to-edge-text": "Seleccione las vistas de entidad para asignar al borde", "unassign-entity-view-from-edge-title": "¿Está seguro de que desea anular la asignación de la vista de entidad '{{entityViewName}}'?", @@ -1481,14 +1966,15 @@ }, "event": { "event-type": "Tipo de evento", + "events-filter": "Filtro de Eventos", + "clean-events": "Borrar Eventos", "type-error": "Error", "type-lc-event": "Ciclo de vida del evento", "type-stats": "Estadísticas", "type-debug-rule-node": "Debug", "type-debug-rule-chain": "Debug", "no-events-prompt": "Ningún evento encontrado.", - "error": "Error", - "type-edge-event": "Downlink", + "error": "Error", "alarm": "Alarma", "event-time": "Hora del evento", "server": "Servidor", @@ -1506,9 +1992,17 @@ "success": "Éxito", "failed": "Fallo", "messages-processed": "Mensajes procesados", + "min-messages-processed": "Mínimo de mensajes procesados", "errors-occurred": "Ocurrieron errores", + "min-errors-occurred": "Minimum errors occurred", + "min-value": "El valor mínimo es 0.", "all-events": "Todos", - "entity-type": "Tipo de entidad" + "has-error": "Tiene error", + "entity-id": "Id de Entidad", + "entity-type": "Tipo de entidad", + "clear-filter": "Limpiar Filtro", + "clear-request-title": "Borrar todos los eventos", + "clear-request-text": "Estás seguro de borrar todos los eventos?" }, "extension": { "extensions": "Extensiones", @@ -1668,25 +2162,25 @@ "invalid-file-error": "Fichero de extensiones inválido" }, "filter": { - "add": "Añadir filtro", - "edit": "Editar filtro", - "name": "Nombre de filtro", - "name-required": "Se requiere nombre de filtro.", - "duplicate-filter": "Ya existe un filtro con el mismo nombre.", - "filters": "Filtros", - "unable-delete-filter-title": "Error borrando filtro", - "unable-delete-filter-text": "El filtro '{{filter}}' no puede ser borrado debido a que está siendo usado actualmente por los siguientes widgets:
{{widgetsList}}", - "duplicate-filter-error": "Se ha encontrado un filtro duplicado '{{filter}}'.
Los filtros deben ser únicos en el panel.", - "missing-key-filters-error": "No se encontró la clave de filtros para el filtro '{{filter}}'.", - "filter": "Filtro", - "editable": "Editable", - "no-filters-found": "No se encontraron filtros.", - "no-filter-text": "No se ha especificado filtro", - "add-filter-prompt": "Por favos, añadir filtro", - "no-filter-matching": "'{{filter}}' no encontrado.", - "create-new-filter": "Crear un filtro nuevo!", - "filter-required": "Se requiere filtro.", - "operation": { + "add": "Añadir filtro", + "edit": "Editar filtro", + "name": "Nombre de filtro", + "name-required": "Se requiere nombre de filtro.", + "duplicate-filter": "Ya existe un filtro con el mismo nombre.", + "filters": "Filtros", + "unable-delete-filter-title": "Error borrando filtro", + "unable-delete-filter-text": "El filtro '{{filter}}' no puede ser borrado debido a que está siendo usado actualmente por los siguientes widgets:
{{widgetsList}}", + "duplicate-filter-error": "Se ha encontrado un filtro duplicado '{{filter}}'.
Los filtros deben ser únicos en el panel.", + "missing-key-filters-error": "No se encontró la clave de filtros para el filtro '{{filter}}'.", + "filter": "Filtro", + "editable": "Editable", + "no-filters-found": "No se encontraron filtros.", + "no-filter-text": "No se ha especificado filtro", + "add-filter-prompt": "Por favos, añadir filtro", + "no-filter-matching": "'{{filter}}' no encontrado.", + "create-new-filter": "Crear un filtro nuevo!", + "filter-required": "Se requiere filtro.", + "operation": { "operation": "Operación", "equal": "igual", "not-equal": "no igual", @@ -1699,7 +2193,9 @@ "greater-or-equal": "mayor o igual", "less-or-equal": "menor o igual", "and": "y", - "or": "o" + "or": "o", + "in": "en", + "not-in": "no en" }, "ignore-case": "Ignorar mayús/minus", "value": "Valor", @@ -1725,7 +2221,8 @@ "key-type": "Tipo de clave", "attribute": "Atributo", "timeseries": "Timeseries", - "entity-field": "Campo de entidad" + "entity-field": "Campo de entidad", + "constant": "Constante" }, "value-type": { "value-type": "Tipo de valor", @@ -1752,7 +2249,9 @@ "no-dynamic-value": "Sin valor dinámico", "source-attribute": "Atributo de origen", "switch-to-dynamic-value": "Cambiar a valor dinámico", - "switch-to-default-value": "Cambiar a valor por defecto" + "switch-to-default-value": "Cambiar a valor por defecto", + "inherit-owner": "Heredar de propietario", + "source-attribute-not-set": "Si los atributos de origen no están seleccionados" }, "fullscreen": { "expand": "Expandir a Pantalla Completa", @@ -1851,7 +2350,8 @@ "scroll-to-top": "Ir hacia arriba" }, "help": { - "goto-help-page": "Ir a la página de ayuda" + "goto-help-page": "Ir a la página de ayuda", + "show-help": "Mostrar ayuda" }, "home": { "home": "Principal", @@ -1861,17 +2361,31 @@ "avatar": "Avatar", "open-user-menu": "Abrir menú de usuario" }, + "file-input": { + "browse-file": "Navegar fichero", + "browse-files": "Navegar ficheros" + }, + "image-input": { + "drop-image-or": "Arrastrar y soltar una imagen o", + "drop-images-or": "Arrastrar y soltar imagenes o", + "no-images": "No hay imágenes seleccionadas", + "images": "imágenes" + }, "import": { "no-file": "Ningún archivo seleccionado", "drop-file": "Suelte un archivo JSON o haga clic para seleccionar un archivo para cargar.", + "drop-json-file-or": "Suele un archivo JSON o", "drop-file-csv": "Suelte un archivo CSV o haga clic para seleccionar un archivo para cargar.", + "drop-file-csv-or": "Suelte un archivo CSV o", "column-value": "Valor", "column-title": "Título", "column-example": "Datos de ejemplo", "column-key": "Clave de atributo/telemetría", + "credentials": "Credenciales", "csv-delimiter": "Delimitador CSV", "csv-first-line-header": "La primera línea contiene nombres de columna.", "csv-update-data": "Actualizar atributos/telemetría", + "details": "Detalles", "import-csv-number-columns-error": "Un archivo debe contener al menos dos columnas", "import-csv-invalid-format-error": "Formato de archivo inválido. Línea: '{{line}}'", "column-type": { @@ -1885,9 +2399,30 @@ "timeseries": "Series de tiempo", "entity-field": "Campo de entidad", "access-token": "Token de acceso", + "x509": "X.509", + "mqtt": { + "client-id": "Client ID MQTT", + "user-name": "Usuario MQTT", + "password": "Contraseña MQTT" + }, + "lwm2m": { + "client-endpoint": "Nombre endpoint cliente LwM2M", + "security-config-mode": "Configuración de seguridad LwM2M", + "client-identity": "Identidad cliente LwM2M", + "client-key": "Clave cliente LwM2M", + "client-cert": "Clave pública cliente LwM2M", + "bootstrap-server-security-mode": "Modo seguridad servidor Bootstrap LwM2M", + "bootstrap-server-secret-key": "Clave secreta servidor Bootstrap LwM2M", + "bootstrap-server-public-key-id": "Clave pública o id servidor Bootstrap LwM2M", + "lwm2m-server-security-mode": "Modo seguridad servidor LwM2M", + "lwm2m-server-secret-key": "Clave secreta servidor LwM2M", + "lwm2m-server-public-key-id": "Clave pública servidor LwM2M" + }, "isgateway": "Es Gateway", "activity-time-from-gateway-device": "Fecha de actividad desde el dispositivo gateway", - "description": "Descripción" + "description": "Descripción", + "routing-key": "Clave Edge", + "secret": "Secreto Edge" }, "stepper-text": { "select-file": "Seleccione un archivo", @@ -1896,7 +2431,7 @@ "creat-entities": "Creando nuevas entidades" }, "message": { - "create-entities": "Se crearon{{count}} nuevas entidades correctamente.", + "create-entities": "Se crearon {{count}} nuevas entidades correctamente.", "update-entities": "{{count}} entidades se actualizaron correctamente.", "error-entities": "Se produjo un error al crear {{count}} entidades." } @@ -1940,6 +2475,8 @@ "avg": "promedio", "total": "total", "comparison-time-ago": { + "previousInterval": "(intervalo anterior)", + "customInterval": "(intervalo personalizado)", "days": "(hace un día)", "weeks": "(hace una semana)", "months": "(hace un mes)", @@ -1951,6 +2488,7 @@ "request-password-reset": "Solicitar restablecer contraseña", "reset-password": "Restablecer contraseña", "create-password": "Crear contraseña", + "two-factor-authentication": "Two factor authentication", "passwords-mismatch-error": "¡Las contraseñas introducidas deben ser iguales!", "password-again": "Repita la contraseña de nuevo", "sign-in": "Por favor, inicie sesión", @@ -1963,9 +2501,93 @@ "new-password-again": "Repita la nueva contraseña", "password-link-sent-message": "Se ha enviado el enlace de restablecimiento de contraseña con éxito!", "email": "Email", - "login-with": "Iniciar sesión con {{name}}", + "login-with": "Login con {{name}}", "or": "o", - "error": "Error de login" + "error": "Error de Login", + "verify-your-identity": "Verify your identity", + "select-way-to-verify": "Select a way to verify", + "resend-code": "Resend code", + "resend-code-wait": "Resend code in { time, plural, 1 {1 second} other {# seconds} }", + "try-another-way": "Try another way", + "totp-auth-description": "Please enter the security code from your authenticator app.", + "totp-auth-placeholder": "Code", + "sms-auth-description": "A security code has been sent to your phone at {{contact}}.", + "sms-auth-placeholder": "SMS code", + "email-auth-description": "A security code has been sent to your email address at {{contact}}.", + "email-auth-placeholder": "Email code", + "backup-code-auth-description": "Please enter one of your backup codes.", + "backup-code-auth-placeholder": "Backup code" + }, + "markdown": { + "edit": "Editar", + "preview": "Previsualizar", + "copy-code": "Click para copiar", + "copied": "Copiado!" + }, + "ota-update": { + "add": "Añadir paquete", + "assign-firmware": "Firmware asignado", + "assign-firmware-required": "Firmware asignado requerido", + "assign-software": "Software asignado", + "assign-software-required": "Software asignado requerido", + "auto-generate-checksum": "Auto-generar checksum", + "checksum": "Checksum", + "checksum-hint": "Si el checksum está vacío, se generará automáticamente", + "checksum-algorithm": "Algoritmo checksum", + "checksum-copied-message": "El checksum del paquete se ha copiado al portapapeles", + "change-firmware": "El cambio del firmware provocará la actualización de { count, plural, 1 {1 dispositivo} other {# dispositivos} }.", + "change-software": "El cambio del software provocará la actualización de { count, plural, 1 {1 dispositivo} other {# dispositivos} }.", + "chose-compatible-device-profile": "El paquete subido, sólamente estará disponible para los dispositivos con el perfil seleccionado.", + "chose-firmware-distributed-device": "Elige el firmware que se distribuirá a los dispositivos", + "chose-software-distributed-device": "Elige el software que se distribuirá a los dispositivos", + "content-type": "Tipo de contenido", + "copy-checksum": "Copiar checksum", + "copy-direct-url": "Copiar URL directa", + "copyId": "Copiar Id de paquete", + "copied": "Copiado!", + "delete": "Borrar paquete", + "delete-ota-update-text": "Atención, tras la confirmación la actualización OTA será irrecuperable.", + "delete-ota-update-title": "Estás seguro de borrar la actualización OTA '{{title}}'?", + "delete-ota-updates-text": "Atención, tras la confirmación todas las actualizaciones OTA seleccionadas se borrarán.", + "delete-ota-updates-title": "Estás seguro de borrar { count, plural, 1 {1 Actualización OTA} other {# Actualizaciones OTA} }?", + "description": "Descripción", + "direct-url": "URL Directa", + "direct-url-copied-message": "La URL directa del paquete se ha copiado al portapapeles", + "direct-url-required": "URL Directa requerida", + "download": "Descargar paquete", + "drop-file": "Arrastra un fichero o haz click para seleccionar un fichero a subir.", + "drop-package-file-or": "Arrastrar y soltar un fichero o", + "file-name": "Nombre del fichero", + "file-size": "Tamaño del fichero", + "file-size-bytes": "Tamaño del fichero en bytes", + "idCopiedMessage": "El Id del paquete se ha copiado al portapapeles", + "no-firmware-matching": "No se ha encontrado ningún paquete de firmware OTA compatible que coincidan con '{{entity}}'.", + "no-firmware-text": "No hay aprovisionado ningún paquete de firmware OTA.", + "no-packages-text": "No se han encontrado paquetes", + "no-software-matching": "No se ha encontrado ningún paquete de software OTA compatible que coincidan con '{{entity}}'.", + "no-software-text": "No hay aprovisionado ningún paquete de software OTA.", + "ota-update": "Actualización OTA", + "ota-update-details": "Detalles de actualización OTA", + "ota-updates": "Actualizaciones OTA", + "package-type": "Tipo de paquete", + "packages-repository": "Repositorio de paquetes", + "search": "Buscar paquetes", + "selected-package": "{ count, plural, 1 {1 paquete} other {# paquetes} } selected", + "title": "Título", + "title-required": "Título requerido.", + "title-max-length": "El título debe ser menor de 256", + "types": { + "firmware": "Firmware", + "software": "Software" + }, + "upload-binary-file": "Subir fichero binario", + "use-external-url": "Usar una URL externa", + "version": "Versión", + "version-required": "Versión requerida.", + "version-tag": "Tag de Versión", + "version-tag-hint": "El tag de versión debe coincidir con la versión reportada por el dispositivo.", + "version-max-length": "Versión debe ser menor que 256", + "warning-after-save-no-edit": "Una vez que el paquete se haya subido, no se podrá modificar el título, versión, perfil de dispositivo y tipo de paquete." }, "position": { "top": "Superior", @@ -1977,7 +2599,68 @@ "profile": "Perfil", "last-login-time": "Último acceso", "change-password": "Cambiar contraseña", - "current-password": "Contraseña actual" + "current-password": "Contraseña actual", + "copy-jwt-token": "Copiar JWT", + "valid-till": "Válido hasta {{expirationData}}", + "tokenCopiedSuccessMessage": "JWT copiado al portapapeles", + "tokenCopiedWarnMessage": "JWT caducado, por favor actualiza la página." + }, + "security": { + "security": "Security", + "2fa": { + "2fa": "Two-factor authentication", + "2fa-description": "Two-factor authentication protects your account from unauthorized access. All you have to do is enter a security code when you log in.", + "authenticate-with": "You can authenticate with:", + "disable-2fa-provider-text": "Disabling {{name}} will make your account less secure", + "disable-2fa-provider-title": "Are you sure you want to disable {{name}}?", + "get-new-code": "Get new code", + "main-2fa-method": "Use as main two-factor authentication method", + "dialog": { + "activation-step-description-email": "The next time you login in, you will be prompted to enter the security code that will be sent to your email address.", + "activation-step-description-sms": "The next time you login in, you will be prompted to enter the security code that will be sent to the phone number.", + "activation-step-description-totp": "The next time you login in, you will need to provide a two-factor authentication code.", + "activation-step-label": "Activation", + "backup-code-description": "Print out the codes so you have them handy when you need to use them to log in to your account. You can use each backup code once.", + "backup-code-warn": "Once you leave this page, these codes cannot be shown again. Store them safely using the options below.", + "download-txt": "Download (txt)", + "email-step-description": "Enter an email to use as your authenticator.", + "email-step-label": "Email", + "enable-email-title": "Enable email authenticator", + "enable-sms-title": "Enable SMS authenticator", + "enable-totp-title": "Enable authenticator app", + "enter-verification-code": "Enter the 6-digit code here", + "get-backup-code-title": "Get backup code", + "next": "Next", + "scan-qr-code": "Scan this QR code with your verification app", + "send-code": "Send code", + "sms-step-description": "Enter a phone number to use as your authenticator.", + "sms-step-label": "Phone Number", + "success": "Success!", + "totp-step-description-install": "You can install apps like Google Authenticator, Authy, or Duo.", + "totp-step-description-open": "Open the authenticator app on your mobile phone.", + "totp-step-label": "Get app", + "verification-code": "6-digit code", + "verification-code-invalid": "Invalid verification code format", + "verification-code-incorrect": "Verification code is incorrect", + "verification-code-many-request": "Too many requests check verification code", + "verification-step-description": "Enter a 6-digit code we just sent to {{address}}", + "verification-step-label": "Verification" + }, + "provider": { + "email": "Email", + "email-description": "Use a security code sent to your email address to authenticate.", + "email-hint": "Authentication codes are sent via email to {{ info }}", + "sms": "SMS", + "sms-description": "Use your phone to authenticate. We'll send you a security code via SMS message when you log in.", + "sms-hint": "Authentication codes are sent by text message to {{ info }}", + "totp": "Authenticator app", + "totp-description": "Use apps like Google Authenticator, Authy, or Duo on your phone to authenticate. It will generate a security code for logging in.", + "totp-hint": "Authenticator app is set up for your account", + "backup_code": "Backup code", + "backup-code-description": "These printable one-time passcodes allow you to sign in when away from your phone, like when you’re traveling.", + "backup-code-hint": "{{ info }} single-use codes are active at this time" + } + } }, "relation": { "relations": "Relaciones", @@ -2003,6 +2686,7 @@ "delete": "Borrar relación", "relation-type": "Tipo de relación", "relation-type-required": "Tipo de relación requerido.", + "relation-type-max-length": "Tipo de relación debe ser menor de 256", "any-relation-type": "Cualquier tipo", "add": "Añadir relación", "edit": "Editar relación", @@ -2022,6 +2706,35 @@ "invalid-additional-info": "Error al analizar el fichero JSON de información adicional.", "no-relations-text": "No se encontraron relaciones" }, + "resource": { + "add": "Añadir Recurso", + "copyId": "Copiar Id de recurso", + "delete": "Borrar recurso", + "delete-resource-text": "Atención, tras la confirmación el recurso será irrecuperable.", + "delete-resource-title": "Estás seguro de borrar el recurso '{{resourceTitle}}'?", + "delete-resources-action-title": "Borrar { count, plural, 1 {1 recurso} other {# recursos} }", + "delete-resources-text": "Los recursos serán borrados, incluso si están siendo usados en los perfiles de dispositivo.", + "delete-resources-title": "Estás seguro de borrar { count, plural, 1 {1 recurso} other {# recursos} }?", + "download": "Descargar recurso", + "drop-file": "Arrastra un fichero o haz click para seleccionar un fichero a subir.", + "drop-resource-file-or": "Arrastrar y soltar un fichero o", + "empty": "El recurso está vacío", + "file-name": "Nombre de fichero", + "idCopiedMessage": "El Id de recurso ha sido copiado al portapapeles", + "no-resource-matching": "No se han encontrado recursos que coincidan con '{{widgetsBundle}}'.", + "no-resource-text": "No se encontraron recursos", + "open-widgets-bundle": "Abrir paquete de widgets", + "resource": "Recurso", + "resource-library-details": "Detalles de recurso", + "resource-type": "Tipo de recurso", + "resources-library": "Librería de recursos", + "search": "Buscar recursos", + "selected-resources": "{ count, plural, 1 {1 recurso} other {# recursos} } seleccionados", + "system": "Sistema", + "title": "Título", + "title-required": "Título requerido.", + "title-max-length": "El título debe ser menor de 256" + }, "rulechain": { "rulechain": "Cadena de Regla", "rulechains": "Cadenas de Reglas", @@ -2029,6 +2742,7 @@ "delete": "Borrar cadena de reglas", "name": "Nombre", "name-required": "Nombre requerido.", + "name-max-length": "Nombre debe ser menor de 256", "description": "Descripción", "add": "Añadir Cadena", "set-root": "Hacer la cadena de reglas Raíz", @@ -2061,14 +2775,12 @@ "search": "Buscar cadenas de reglas", "selected-rulechains": "{ count, plural, 1 {1 cadena de reglas} other {# cadenas de reglas} } seleccionadas", "open-rulechain": "Abrir cadena de reglas", - "assign-rulechains": "Asignar cadenas de reglas", "assign-new-rulechain": "Asignar nueva cadena de reglas", - "delete-rulechains": "Eliminar cadenas de reglas", - "unassign-rulechain": "Anular asignación de cadena de reglas", - "unassign-rulechains": "Anular asignación de cadenas de reglas", - "unassign-rulechain-title": "¿Está seguro de que desea desasignar la cadena de reglas '{{ruleChainName}}'?", + "edge-template-root": "Raíz de plantilla", + "assign-to-edge": "Asignar a Edge", + "edge-rulechain": "Cadena de reglas de borde", "unassign-rulechain-from-edge-text": "Después de la confirmación, la cadena de reglas quedará sin asignar y el borde no podrá acceder a ella", - "unassign-rulechains-from-edge-action-title": "Anular asignación {count, plural, 1 {1 cadena de reglas} other {# cadenas de reglas} } des bordes", + "unassign-rulechains-from-edge-title": "Estás seguro de desasignar { count, plural, 1 {1 cadena de reglas} other {# cadenas de reglas} }?", "unassign-rulechains-from-edge-text": "Después de la confirmación, todas las cadenas de reglas seleccionadas quedarán sin asignar y el borde no podrá acceder a ellas", "assign-rulechain-to-edge-title": "Asignar cadena (s) de reglas a borde", "assign-rulechain-to-edge-text": "Seleccione las cadenas de reglas para asignar al borde", @@ -2082,9 +2794,8 @@ "unset-auto-assign-to-edge": "Desmarcar asignar cadena de reglas a los bordes en la creación", "unset-auto-assign-to-edge-title": "¿Está seguro de que desea anular la asignación de la cadena de reglas de borde '{{ruleChainName}}' a los bordes en la creación?", "unset-auto-assign-to-edge-text": "Después de la confirmación, la cadena de reglas de borde ya no se asignará automáticamente a los bordes en la creación.", - "edge-template-root": "Raíz de plantilla", - "assign-to-edge": "Asignar a Edge", - "edge-rulechain": "Cadena de regla de borde" + "unassign-rulechain-title": "¿Está seguro de que desea desasignar la cadena de reglas '{{ruleChainName}}'?", + "unassign-rulechains": "Anular asignación de cadenas de reglas" }, "rulenode": { "details": "Detalles", @@ -2094,6 +2805,7 @@ "add": "Añadir nodo de reglas", "name": "Nombre", "name-required": "El nombre es requerido.", + "name-max-length": "Nombre debe ser menor de 256", "type": "Tipo", "description": "Descripción", "delete": "Eliminar nodo de reglas", @@ -2101,6 +2813,7 @@ "deselect-all-objects": "Deshacer selección de todos los nodos y conexiones", "delete-selected-objects": "Eliminar nodos y conexiones seleccionados", "delete-selected": "Eliminar seleccionado", + "create-nested-rulechain": "Crear cadena de reglas anidada", "select-all": "Seleccionar todos", "copy-selected": "Copiar seleccionado", "deselect-all": "Deshacer selección de todos", @@ -2131,6 +2844,8 @@ "type-external-details": "Interactuar con sistemas externos", "type-rule-chain": "Cadena de reglas", "type-rule-chain-details": "Reenvíar los mensajes entrantes a la cadena de reglas especificada", + "type-flow": "Flujo", + "type-flow-details": "Organiza el flujo de mensajes", "type-input": "Entrada", "type-input-details": "Entrada lógica de la Cadena de Reglas, reenvíar los mensajes entrantes al siguiente nodo de regla relacionado.", "type-unknown": "Desconocido", @@ -2154,12 +2869,89 @@ "timezone": "Zona Horaria", "select-timezone": "Seleccionar zona horaria", "no-timezones-matching": "No hay zonas horarias que coincidan con '{{timezone}}'.", - "timezone-required": "Se requiere zona horaria." + "timezone-required": "Se requiere zona horaria.", + "browser-time": "Hora del navegador" }, "queue": { - "select_name": "Selecciona el nombre de la cola", - "name": "Nombre Cola", - "name_required": "Necesario especificar el nombre de cola" + "queue-name": "Queue", + "no-queues-matching": "No se encontraron colas que coincidan con '{{queue}}'.", + "select_name": "Selecciona el nombre de la cola", + "name": "Nombre Cola", + "name_required": "Necesario especificar el nombre de cola", + "name-unique": "Queue name is not unique!", + "queue-required": "Queue is required!", + "topic-required": "Queue topic is required!", + "poll-interval-required": "Poll interval is required!", + "poll-interval-min-value": "Poll interval value can't be less then 1", + "partitions-required": "Partitions is required!", + "partitions-min-value": "Partitions value can't be less then 1", + "pack-processing-timeout-required": "Processing timeout is required", + "pack-processing-timeout-min-value": "Processing timeout value can't be less then 1", + "batch-size-required": "Batch size is required!", + "batch-size-min-value": "Batch size value can't be less then 1", + "retries-required": "Retries is required!", + "retries-min-value": "Retries value can't be negative", + "failure-percentage-required": "Failure percentage is required!", + "failure-percentage-min-value": "Failure percentage value can't be less then 0", + "failure-percentage-max-value": "Failure percentage value can't be more then 100", + "pause-between-retries-required": "Pause between retries is required!", + "pause-between-retries-min-value": "Pause between retries value can't be less then 1", + "max-pause-between-retries-required": "Max pause between retries is required!", + "max-pause-between-retries-min-value": "Max pause between retries value can't be less then 1", + "submit-strategy-type-required": "Submit strategy type is required!", + "processing-strategy-type-required": "Processing strategy type is required!", + "queues": "Queues", + "selected-queues": "{ count, plural, 1 {1 queue} other {# queues} } selected", + "delete-queue-title": "Are you sure you want to delete the queue '{{queueName}}'?", + "delete-queues-title": "Are you sure you want to delete { count, plural, 1 {1 queue} other {# queues} }?", + "delete-queue-text": "Be careful, after the confirmation the queue and all related data will become unrecoverable.", + "delete-queues-text": "After the confirmation all selected queues will be deleted and won't be accessible.", + "search": "Search queue", + "add" : "Add queue", + "details": "Queue details", + "topic": "Topic", + "submit-strategy": "Submit Strategy", + "processing-strategy": "Processing Strategy", + "poll-interval": "Poll interval", + "partitions": "Partitions", + "consumer-per-partition": "Consumer per partition", + "consumer-per-partition-hint": "Enable separate consumer(s) per each partition", + "processing-timeout": "Processing timeout, ms", + "batch-size": "Batch size", + "retries": "Retries (0 - unlimited)", + "failure-percentage": "Failure Percentage", + "pause-between-retries": "Pause between retries", + "max-pause-between-retries": "Maximal pause between retries", + "delete": "Delete queue", + "copyId": "Copy queue Id", + "idCopiedMessage": "Queue Id has been copied to clipboard", + "description": "Description", + "description-hint": "This text will be displayed in the Queue description instead of the selected strategy", + "alt-description": "Submit Strategy: {{submitStrategy}}, Processing Strategy: {{processingStrategy}}", + "strategies": { + "sequential-by-originator-label": "Sequential by originator", + "sequential-by-originator-hint": "New message for e.g. device A is not submitted until previous message for device A is acknowledged", + "sequential-by-tenant-label": "Sequential by tenant", + "sequential-by-tenant-hint": "New message for e.g tenant A is not submitted until previous message for tenant A is acknowledged", + "sequential-label": "Sequential", + "sequential-hint": "New message is not submitted until previous message is acknowledged", + "burst-label": "Burst", + "burst-hint": "All messages are submitted to the rule chains in the order they arrive", + "batch-label": "Batch", + "batch-hint": "New batch is not submitted until previous batch is acknowledged", + "skip-all-failures-label": "Skip all failures", + "skip-all-failures-hint": "Ignore all failures", + "skip-all-failures-and-timeouts-label": "Skip all failures and timeouts", + "skip-all-failures-and-timeouts-hint": "Ignore all failures and timeouts", + "retry-all-label": "Retry all", + "retry-all-hint": "Retry all messages from processing pack", + "retry-failed-label": "Retry failed", + "retry-failed-hint": "Retry all failed messages from processing pack", + "retry-timeout-label": "Retry timeout", + "retry-timeout-hint": "Retry all timed-out messages from processing pack", + "retry-failed-and-timeout-label": "Retry failed and timeout", + "retry-failed-and-timeout-hint": "Retry all failed and timed-out messages from processing pack" + } }, "tenant": { "tenant": "Propietario", @@ -2172,6 +2964,7 @@ "add-tenant-text": "Agregar nuevo propietario", "no-tenants-text": "Ningún propietario encontrado", "tenant-details": "Detalles del propietario", + "title-max-length": "Título debe ser menor de 256", "delete-tenant-title": "¿Quieres eliminar el propietario '{{tenantTitle}}'?", "delete-tenant-text": "Atención, tras la confirmación el propietario será eliminado y la información relacionada será irrecuperable.", "delete-tenants-title": "¿Quieres eliminar { count, plural, 1 {1 propietario} other {# propietarios} }?", @@ -2201,6 +2994,7 @@ "edit": "Editar perfil de propietario", "tenant-profile-details": "Detalles perfil de propietario", "no-tenant-profiles-text": "No se encontraron perfiles de propietario", + "name-max-length": "El nombre debe ser menor de 256", "search": "Buscar perfiles de propietario", "selected-tenant-profiles": "{ count, plural, 1 {1 perfil de propietario} other {# perfiles de propietario} } seleccionados", "no-tenant-profiles-matching": "No se han encontrado perfiles de propietario que coincidan con '{{entity}}'.", @@ -2223,6 +3017,12 @@ "set-default-tenant-profile-text": "Tras la confirmación, el perfil propietario será marcado por defecto y será usado por los nuevos perfiles propietarios que no tengan perfil específico.", "no-tenant-profiles-found": "No se encontraron perfiles de propietario.", "create-new-tenant-profile": "Crear un nuevo perfil!", + "create-tenant-profile": "Crear un nuevo perfil de propietario", + "import": "Importar perfil de propietario", + "export": "Exportar perfil de propietario", + "export-failed-error": "No se ha podido exportar el perfil de propietario: {{error}}", + "tenant-profile-file": "Archivo de perfil de propietario", + "invalid-tenant-profile-file-error": "No se ha podido importar el perfil de propietario: Estructura de datos inválida.", "maximum-devices": "Nº Máximo de dispositivos (0 - sin límite)", "maximum-devices-required": "Nº Máximo de dispositivos requerido.", "maximum-devices-range": "Nº Máximo de dispositivos no puede ser negativo", @@ -2241,6 +3041,12 @@ "maximum-rule-chains": "Nº Máximo de cadenas de reglas (0 - sin límite)", "maximum-rule-chains-required": "Nº Máximo de cadenas de reglas requerido.", "maximum-rule-chains-range": "Nº Máximo de cadenas de reglas no puede ser negativo", + "maximum-resources-sum-data-size": "Tamaño máximo de ficheros de recursos en bytes (0 - sin límite)", + "maximum-resources-sum-data-size-required": "Tamaño máximo de ficheros de recursos requerido.", + "maximum-resources-sum-data-size-range": "Tamaño máximo de ficheros de recursos no puede ser negativo", + "maximum-ota-packages-sum-data-size": "Tamaño máximo de paquetes OTA en bytes (0 - sin límite)", + "maximum-ota-package-sum-data-size-required": "Tamaño máximo de paquetes OTA requerido.", + "maximum-ota-package-sum-data-size-range": "Tamaño máximo de paquetes OTA no puede ser negativo", "transport-tenant-msg-rate-limit": "Tasa de mensajes de transporte por propietario.", "transport-tenant-telemetry-msg-rate-limit": "Tasa de mensajes de telemetría por propietario.", "transport-tenant-telemetry-data-points-rate-limit": "Tasa de datapoints por propietario.", @@ -2265,6 +3071,12 @@ "default-storage-ttl-days": "Días por defecto grabado TTL (0 - sin límite)", "default-storage-ttl-days-required": "Días por defecto TTL requerido.", "default-storage-ttl-days-range": "Días por defecto TTL no puede ser negativo", + "alarms-ttl-days": "Días de TTL alarmas (0 - sin límite)", + "alarms-ttl-days-required": "Días de TTL alarmas requerido", + "alarms-ttl-days-days-range": "Días de TTL alarmas no puede ser negativo", + "rpc-ttl-days": "Días TTL RPC (0 - sin límite)", + "rpc-ttl-days-required": "Días TTL RPC requerido", + "rpc-ttl-days-days-range": "Días TTL RPC no puede ser negativo", "max-rule-node-executions-per-message": "Nº Máximo de ejecuciones (cadena de reglas) por mensaje (0 - sin límite)", "max-rule-node-executions-per-message-required": "Nº Máximo de ejecuciones por mensaje requerido.", "max-rule-node-executions-per-message-range": "Nº Máximo de ejecuciones por mensaje no puede ser negativo", @@ -2273,20 +3085,47 @@ "max-emails-range": "Nº Máximo de emails no puede ser negativo", "max-sms": "Nº Máximo de mensajes SMS (0 - sin límite)", "max-sms-required": "Nº Máximo de mensajes SMS requerido.", - "max-sms-range": "Nº Máximo de mensajes SMS no puede ser negativo" + "max-sms-range": "Nº Máximo de mensajes SMS no puede ser negativo", + "max-created-alarms": "Nº Máximo de alarmas creadas (0 - sin límite)", + "max-created-alarms-required": "Nº Máximo de alarmas creadas requerido.", + "max-created-alarms-range": "Nº Máximo de alarmas creadas no puede ser negativo", + "no-queue": "No Queue configured", + "add-queue": "Add Queue", + "queues-with-count": "Queues ({{count}})" }, "timeinterval": { - "seconds-interval": "{ seconds, plural, 1 {1 segundo} other {# segundos} }", - "minutes-interval": "{ minutes, plural, 1 {1 minuto} other {# minutos} }", - "hours-interval": "{ hours, plural, 1 {1 hora} other {# horas} }", - "days-interval": "{ days, plural, 1 {1 día} other {# días} }", - "days": "Días", - "hours": "Horas", - "minutes": "Minutos", - "seconds": "Segundos", - "advanced": "Avanzado" + "seconds-interval": "{ seconds, plural, 1 {1 segundo} other {# segundos} }", + "minutes-interval": "{ minutes, plural, 1 {1 minuto} other {# minutos} }", + "hours-interval": "{ hours, plural, 1 {1 hora} other {# horas} }", + "days-interval": "{ days, plural, 1 {1 día} other {# días} }", + "days": "Días", + "hours": "Horas", + "minutes": "Minutos", + "seconds": "Segundos", + "advanced": "Avanzado", + "predefined": { + "yesterday": "Ayer", + "day-before-yesterday": "Anteayer", + "this-day-last-week": "Hoy hace una semana", + "previous-week": "Semana anterior (Dom - Sáb)", + "previous-week-iso": "Semana anterior (Lun - Dom)", + "previous-month": "Mes anterior", + "previous-year": "Año anterior", + "current-hour": "Hora actual", + "current-day": "Día actual", + "current-day-so-far": "Día actual hasta ahora", + "current-week": "Semana actual (Dom - Sáb)", + "current-week-iso": "Semana actual (Lun - Dom)", + "current-week-so-far": "Semana actual hasta hoy (Dom - Sáb)", + "current-week-iso-so-far": "Semana actual hasta hoy (Lun - Dom)", + "current-month": "Mes actual", + "current-month-so-far": "Mes actual hasta hoy", + "current-year": "Año actual", + "current-year-so-far": "Año actual hasta ahora" + } }, "timeunit": { + "milliseconds": "Milisegundos", "seconds": "Segundos", "minutes": "Minutos", "hours": "Horas", @@ -2305,7 +3144,8 @@ "date-range": "Rango de fechas", "last": "Últimos(s)", "time-period": "Período de tiempo", - "hide": "Ocultar" + "hide": "Ocultar", + "interval": "Intervalo" }, "user": { "user": "Usuario", @@ -2354,7 +3194,9 @@ "disable-account": "Deshabilitar cuenta de usuario", "enable-account": "Habilitar cuenta de usuario", "enable-account-message": "¡La cuenta de usuario se ha habilitado correctamente!", - "disable-account-message": "¡La cuenta de usuario se deshabilitó correctamente!" + "disable-account-message": "¡La cuenta de usuario se deshabilitó correctamente!", + "copyId": "Copiar Id de usuario", + "idCopiedMessage": "El Id de usuario se ha copiado al portapapeles" }, "value": { "type": "Tipo de valor", @@ -2381,6 +3223,7 @@ "widget": { "widget-library": "Bibloteca de Widgets", "widget-bundle": "Paquetes de Widgets", + "all-bundles": "Todos los paquetes", "select-widgets-bundle": "Seleccionar paquete de widgets", "management": "Gestión de Widgets", "editor": "Editor de widgets", @@ -2420,6 +3263,13 @@ "css": "CSS", "settings-schema": "Esquema de configuración", "datakey-settings-schema": "Esquema de configuración de clave de datos", + "latest-datakey-settings-schema": "Esquema de últimos valores", + "widget-settings": "Ajustes de widget", + "description": "Descripción", + "image-preview": "Imagen previsualización", + "settings-form-selector": "Selector formulario de ajustes", + "data-key-settings-form-selector": "Selector formulario de ajustes claves de datos", + "latest-data-key-settings-form-selector": "Selector formulario de últimos valores", "javascript": "Javascript", "js": "JS", "remove-widget-type-title": "¿Eliminar el tipo del widget '{{widgetName}}'?", @@ -2433,7 +3283,10 @@ "export": "Exportar widget", "no-data": "No hay datos para mostrar en widget", "data-overflow": "El widget muestra {{count}} de {{total}} entidades", - "alarm-data-overflow": "El widget muestra alarmas para {{allowedEntities}} entidades (máximo permitido) de {{totalEntities}} entidades" + "alarm-data-overflow": "El widget muestra alarmas para {{allowedEntities}} entidades (máximo permitido) de {{totalEntities}} entidades", + "search": "Buscar widget", + "filter": "Filtro tipo de widget", + "loading-widgets": "Cargando widgets..." }, "widget-action": { "header-button": "Botón de encabezado widget", @@ -2442,18 +3295,52 @@ "open-dashboard": "Navegar hacia otro panel", "custom": "Acción personalizada", "custom-pretty": "Acción personalizada (con plantilla HTML)", + "mobile-action": "Acción en dispositivo móvil", "target-dashboard-state": "Estado de panel de destino", "target-dashboard-state-required": "Se requiere estado de panel de destino", "set-entity-from-widget": "Establecer entidad desde widget", "target-dashboard": "Panel de destino", "open-right-layout": "Abrir diseño de panel (derecho)(vista móvil)", + "state-display-type": "Opciones de display de panel", + "open-normal": "Normal", "open-in-separate-dialog": "Abrir en un diálogo separado", + "open-in-popover": "Abrir en popover", "dialog-title": "Título del diálogo", "dialog-hide-dashboard-toolbar": "Ocultar barra de herramientas en el diálogo", "dialog-width": "Ancho de diálogo en porcentaje relativo al ancho del viewport", "dialog-height": "Alto de diálogo en porcentaje relativo al alto del viewport", "dialog-size-range-error": "El tamaño del diálogo debe ser entre un rango de 1 a 100", - "open-new-browser-tab": "Abrir en una nueva pestaña" + "popover-preferred-placement": "Ubicación preferida popover", + "popover-placement-top": "Superior", + "popover-placement-topLeft": "Superior izquierda", + "popover-placement-topRight": "Superior derecha", + "popover-placement-right": "Derecha", + "popover-placement-rightTop": "Derecha superior", + "popover-placement-rightBottom": "Derecha inferior", + "popover-placement-bottom": "Inferior", + "popover-placement-bottomLeft": "Inferior izquierda", + "popover-placement-bottomRight": "Inferior derecha", + "popover-placement-left": "Izquierda", + "popover-placement-leftTop": "Izquierda superior", + "popover-placement-leftBottom": "Izquierda inferior", + "popover-hide-on-click-outside": "Ocultar en click fuera del popover", + "popover-hide-dashboard-toolbar": "Ocultar caja de herramientas en popover", + "popover-width": "Ancho de popover en unidades de navegador (ej. 100px, 25vw)", + "popover-height": "Altura de popover en unidades de navegador (ej. 100px, 25vh)", + "popover-style": "Estilo de popover", + "open-new-browser-tab": "Abrir en una nueva pestaña", + "mobile": { + "action-type": "Tipo de acción móvil", + "action-type-required": "Tipo de acción móvil requerida", + "take-picture-from-gallery": "Tomar foto de galería", + "take-photo": "Tomar foto", + "map-direction": "Abrir indicaciones en mapa", + "map-location": "Abrir localización en mapa", + "scan-qr-code": "Escanear código QR", + "make-phone-call": "Hacer llamada telefónica", + "get-location": "Obtener localización del teléfono", + "take-screenshot": "Obtener captura de pantalla" + } }, "widgets-bundle": { "current": "Paquete actual", @@ -2462,6 +3349,9 @@ "delete": "Eliminar paquete de widgets", "title": "Título", "title-required": "Título requerido.", + "title-max-length": "El título debe ser menor de 256", + "description": "Descripción", + "image-preview": "Imagen previsualización", "add-widgets-bundle-text": "Agregar nuevo paquete de widgets", "no-widgets-bundles-text": "Ningún paquete de widgets encontrado", "empty": "Paquete de widgets vacío.", @@ -2483,7 +3373,8 @@ "invalid-widgets-bundle-file-error": "Imposible importar paquete de widgets: Estructura de datos inválida.", "search": "Buscar paquete de widgets", "selected-widgets-bundles": "{ count, plural, 1 {1 paquete de widgets} other {# paquetes de widgets} } seleccionados", - "open-widgets-bundle": "Abrir paquete de widgets" + "open-widgets-bundle": "Abrir paquete de widgets", + "loading-widgets-bundles": "Cargando paquete de widgets..." }, "widget-config": { "data": "Datos", @@ -2500,15 +3391,18 @@ "padding": "Relleno", "margin": "Margen", "widget-style": "Estilo de widget", + "widget-css": "CSS de widget", "title-style": "Estilo de título", "mobile-mode-settings": "Ajustes móvil.", "order": "Orden", "height": "Altura", + "mobile-hide": "Ocultar widget en modo móvil", "units": "Caracter especial a mostrar en el siguiente valor", "decimals": "Números de dígitos después de la coma", "timewindow": "Ventana de tiempo", "use-dashboard-timewindow": "Usar ventana de tiempo del Panel", "display-timewindow": "Mostrar ventana de tiempo", + "legend": "Leyenda", "display-legend": "Mostrar leyenda", "datasources": "Set de datos", "maximum-datasources": "Un máximo de { count, plural, 1 {1 set de datos es permitido.} other {# set de datos son permitidos} }", @@ -2529,15 +3423,22 @@ "action-name-required": "Nombre de accion requerido.", "action-name-not-unique": "Existe una acción con el mismo nombre.
El nombre de acción debe ser único dentro de la misma fuente de acción (origen).", "action-icon": "Icono", + "show-hide-action-using-function": "Mostrar/Ocultar acción usando función", "action-type": "Tipo", "action-type-required": "Tipo de acción requerido.", "edit-action": "Editar acción", "delete-action": "Borrar acción", "delete-action-title": "Borrar acción de widget", "delete-action-text": "Eliminar la acción de widget con el nombre '{{actionName}}'?", - "display-icon": "Mostrar icono del título", + "title-icon": "Título de icono", + "display-icon": "Mostrar título de icono", "icon-color": "Color del icono", - "icon-size": "Tamaño del icono" + "icon-size": "Tamaño del icono", + "advanced-settings": "Ajustes avanzados", + "data-settings": "Ajustes de datos", + "no-data-display-message": "\"No hay datos que mostrar\" mensaje alternativo", + "data-page-size": "Nº Máximo de entidades por origen de datos", + "settings-component-not-found": "Componente de ajustes no encontrado para el selector '{{selector}}'" }, "widget-type": { "import": "Importar tipo de widget", @@ -2548,7 +3449,146 @@ "invalid-widget-type-file-error": "No se puede importar tipo de widget: Estructura de datos del tipo de widget es inválida." }, "widgets": { + "chart": { + "common-settings": "Common settings", + "enable-stacking-mode": "Enable stacking mode", + "line-shadow-size": "Line shadow size", + "display-smooth-lines": "Display smooth (curved) lines", + "default-bar-width": "Default bar width for non-aggregated data (milliseconds)", + "bar-alignment": "Bar alignment", + "bar-alignment-left": "Left", + "bar-alignment-right": "Right", + "bar-alignment-center": "Center", + "default-font-size": "Default font size", + "default-font-color": "Default font color", + "thresholds-line-width": "Default line width for all thresholds", + "tooltip-settings": "Tooltip settings", + "show-tooltip": "Show tooltip", + "hover-individual-points": "Hover individual points", + "show-cumulative-values": "Show cumulative values in stacking mode", + "hide-zero-false-values": "Hide zero/false values from tooltip", + "tooltip-value-format-function": "Tooltip value format function", + "grid-settings": "Grid settings", + "show-vertical-lines": "Show vertical lines", + "show-horizontal-lines": "Show horizontal lines", + "grid-outline-border-width": "Grid outline/border width (px)", + "primary-color": "Primary color", + "background-color": "Background color", + "ticks-color": "Ticks color", + "xaxis-settings": "X axis settings", + "axis-title": "Axis title", + "xaxis-tick-labels-settings": "X axis tick labels settings", + "show-tick-labels": "Show axis tick labels", + "yaxis-settings": "Y axis settings", + "min-scale-value": "Minimum value on the scale", + "max-scale-value": "Maximum value on the scale", + "yaxis-tick-labels-settings": "Y axis tick labels settings", + "tick-step-size": "Step size between ticks", + "number-of-decimals": "The number of decimals to display", + "ticks-formatter-function": "Ticks formatter function", + "comparison-settings": "Comparison settings", + "enable-comparison": "Enable comparison", + "time-for-comparison": "Comparison period", + "time-for-comparison-previous-interval": "Previous interval (default)", + "time-for-comparison-days": "Day ago", + "time-for-comparison-weeks": "Week ago", + "time-for-comparison-months": "Month ago", + "time-for-comparison-years": "Year ago", + "time-for-comparison-custom-interval": "Custom interval", + "custom-interval-value": "Custom interval value (ms)", + "comparison-x-axis-settings": "Comparison X axis settings", + "axis-position": "Axis position", + "axis-position-top": "Top (default)", + "axis-position-bottom": "Bottom", + "custom-legend-settings": "Custom legend settings", + "enable-custom-legend": "Enable custom legend (this will allow you to use attribute/timeseries values in key labels)", + "key-name": "Key name", + "key-name-required": "Key name is required", + "key-type": "Key type", + "key-type-attribute": "Attribute", + "key-type-timeseries": "Timeseries", + "label-keys-list": "Keys list to use in labels", + "no-label-keys": "No keys configured", + "add-label-key": "Add new key", + "line-width": "Line width", + "color": "Color", + "data-is-hidden-by-default": "Data is hidden by default", + "disable-data-hiding": "Disable data hiding", + "remove-from-legend": "Remove datakey from legend", + "exclude-from-stacking": "Exclude from stacking(available in \"Stacking\" mode)", + "line-settings": "Line settings", + "show-line": "Show line", + "fill-line": "Fill line", + "points-settings": "Points settings", + "show-points": "Show points", + "points-line-width": "Line width of points", + "points-radius": "Radius of points", + "point-shape": "Point shape", + "point-shape-circle": "Circle", + "point-shape-cross": "Cross", + "point-shape-diamond": "Diamond", + "point-shape-square": "Square", + "point-shape-triangle": "Triangle", + "point-shape-custom": "Custom function", + "point-shape-draw-function": "Point shape draw function", + "show-separate-axis": "Show separate axis", + "axis-position-left": "Left", + "axis-position-right": "Right", + "thresholds": "Thresholds", + "no-thresholds": "No thresholds configured", + "add-threshold": "Add new threshold", + "show-values-for-comparison": "Show historical values for comparison", + "comparison-values-label": "Historical values label", + "threshold-settings": "Threshold settings", + "use-as-threshold": "Use key value as threshold", + "threshold-line-width": "Threshold line width", + "threshold-color": "Threshold color", + "common-pie-settings": "Common pie settings", + "radius": "Radius", + "inner-radius": "Inner radius", + "tilt": "Tilt", + "stroke-settings": "Stroke settings", + "width-pixels": "Width (pixels)", + "show-labels": "Show labels", + "animation-settings": "Animation settings", + "animated-pie": "Enable pie animation (experimental)", + "border-settings": "Border settings", + "border-width": "Border width", + "border-color": "Border color", + "legend-settings": "Legend settings", + "display-legend": "Display legend", + "labels-font-color": "Labels font color" + }, + "dashboard-state": { + "dashboard-state-settings": "Dashboard state settings", + "dashboard-state": "Dashboard state id", + "autofill-state-layout": "Autofill state layout height by default", + "default-margin": "Default widgets margin", + "default-background-color": "Default background color", + "sync-parent-state-params": "Sync state params with parent dashboard" + }, "date-range-navigator": { + "date-range-picker-settings": "Date range picker settings", + "hide-date-range-picker": "Hide date range picker", + "picker-one-panel": "Date range picker one panel", + "picker-auto-confirm": "Date range picker auto confirm", + "picker-show-template": "Date range picker show template", + "first-day-of-week": "First day of the week", + "interval-settings": "Interval settings", + "hide-interval": "Hide interval", + "initial-interval": "Initial interval", + "interval-hour": "Hour", + "interval-day": "Day", + "interval-week": "Week", + "interval-two-weeks": "2 weeks", + "interval-month": "Month", + "interval-three-months": "3 months", + "interval-six-months": "6 months", + "step-settings": "Step settings", + "hide-step-size": "Hide step size", + "initial-step-size": "Initial step size", + "hide-labels": "Hide labels", + "use-session-storage": "Use session storage", "localizationMap": { "Sun": "Dom.", "Mon": "Lun.", @@ -2605,6 +3645,176 @@ "Ok": "Ok" } }, + "entities-hierarchy": { + "hierarchy-data-settings": "Hierarchy data settings", + "relations-query-function": "Node relations query function", + "has-children-function": "Node has children function", + "node-state-settings": "Node state settings", + "node-opened-function": "Default node opened function", + "node-disabled-function": "Node disabled function", + "display-settings": "Display settings", + "node-icon-function": "Node icon function", + "node-text-function": "Node text function", + "sort-settings": "Sort settings", + "nodes-sort-function": "Nodes sort function" + }, + "edge": { + "display-default-title": "Display default title" + }, + "gateway": { + "general-settings": "General settings", + "widget-title": "Widget title", + "default-archive-file-name": "Default archive file name", + "device-type-for-new-gateway": "Device type for new gateway", + "messages-settings": "Messages settings", + "save-config-success-message": "Text message about successfully saved gateway configuration", + "device-name-exists-message": "Text message when device with entered name is already exists", + "gateway-title": "Gateway form", + "read-only": "Read only", + "events-title": "Gateway events form title", + "events-filter": "Events filter", + "event-key-contains": "Event key contains..." + }, + "gauge": { + "default-color": "Default color", + "radial-gauge-settings": "Radial gauge settings", + "ticks-settings": "Ticks settings", + "min-value": "Minimum value", + "max-value": "Maximum value", + "start-ticks-angle": "Start ticks angle", + "ticks-angle": "Ticks angle", + "major-ticks-count": "Major ticks count", + "major-ticks-color": "Major ticks color", + "minor-ticks-count": "Minor ticks count", + "minor-ticks-color": "Minor ticks color", + "tick-numbers-font": "Tick numbers font", + "unit-title-settings": "Unit title settings", + "show-unit-title": "Show unit title", + "unit-title": "Unit title", + "title-font": "Title text font", + "units-settings": "Units settings", + "units-font": "Units text font", + "value-box-settings": "Value box settings", + "show-value-box": "Show value box", + "value-int": "Digits count for integer part of value", + "value-font": "Value text font", + "value-box-rect-stroke-color": "Value box rectangle stroke color", + "value-box-rect-stroke-color-end": "Value box rectangle stroke color - end gradient", + "value-box-background-color": "Value box background color", + "value-box-shadow-color": "Value box shadow color", + "plate-settings": "Plate settings", + "show-plate-border": "Show plate border", + "plate-color": "Plate color", + "needle-settings": "Needle settings", + "needle-circle-size": "Needle circle size", + "needle-color": "Needle color", + "needle-color-end": "Needle color - end gradient", + "needle-color-shadow-up": "Upper half of the needle shadow color", + "needle-color-shadow-down": "Drop shadow needle color", + "highlights-settings": "Highlights settings", + "highlights-width": "Highlights width", + "highlights": "Highlights", + "highlight-from": "From", + "highlight-to": "To", + "highlight-color": "Color", + "no-highlights": "No highlights configured", + "add-highlight": "Add highlight", + "animation-settings": "Animation settings", + "enable-animation": "Enable animation", + "animation-duration": "Animation duration", + "animation-rule": "Animation rule", + "animation-linear": "Linear", + "animation-quad": "Quad", + "animation-quint": "Quint", + "animation-cycle": "Cycle", + "animation-bounce": "Bounce", + "animation-elastic": "Elastic", + "animation-dequad": "Dequad", + "animation-dequint": "Dequint", + "animation-decycle": "Decycle", + "animation-debounce": "Debounce", + "animation-delastic": "Delastic", + "linear-gauge-settings": "Linear gauge settings", + "bar-stroke-width": "Bar stroke width", + "bar-stroke-color": "Bar stroke color", + "bar-background-color": "Gauge bar background color", + "bar-background-color-end": "Bar background color - end gradient", + "progress-bar-color": "Progress bar color", + "progress-bar-color-end": "Progress bar color - end gradient", + "major-ticks-names": "Major ticks names", + "show-stroke-ticks": "Show ticks stroke", + "major-ticks-font": "Major ticks font", + "border-color": "Border color", + "border-width": "Border width", + "needle-circle-color": "Needle circle color", + "animation-target": "Animation target", + "animation-target-needle": "Needle", + "animation-target-plate": "Plate", + "common-settings": "Common gauge settings", + "gauge-type": "Gauge type", + "gauge-type-arc": "Arc", + "gauge-type-donut": "Donut", + "gauge-type-horizontal-bar": "Horizontal bar", + "gauge-type-vertical-bar": "Vertical bar", + "donut-start-angle": "Angle to start from", + "bar-settings": "Gauge bar settings", + "relative-bar-width": "Relative bar width", + "neon-glow-brightness": "Neon glow effect brightness, (0-100), 0 - disable effect", + "stripes-thickness": "Thickness of the stripes, 0 - no stripes", + "rounded-line-cap": "Display rounded line cap", + "bar-color-settings": "Bar color settings", + "use-precise-level-color-values": "Use precise color levels", + "bar-colors": "Bar colors, from lower to upper", + "color": "Color", + "no-bar-colors": "No bar colors configured", + "add-bar-color": "Add bar color", + "from": "From", + "to": "To", + "fixed-level-colors": "Bar colors using boundary values", + "gauge-title-settings": "Gauge title settings", + "show-gauge-title": "Show gauge title", + "gauge-title": "Gauge title", + "gauge-title-font": "Gauge title font", + "unit-title-and-timestamp-settings": "Unit title and timestamp settings", + "show-timestamp": "Show value timestamp", + "timestamp-format": "Timestamp format", + "label-font": "Font of label showing under value", + "value-settings": "Value settings", + "show-value": "Show value text", + "min-max-settings": "Minimum/maximum labels settings", + "show-min-max": "Show min and max values", + "min-max-font": "Font of minimum and maximum labels", + "show-ticks": "Show ticks", + "tick-width": "Tick width", + "tick-color": "Tick color", + "tick-values": "Tick values", + "no-tick-values": "No tick values configured", + "add-tick-value": "Add tick value" + }, + "gpio": { + "pin": "Pin", + "label": "Label", + "row": "Row", + "column": "Column", + "color": "Color", + "panel-settings": "Panel settings", + "background-color": "Background color", + "gpio-switches": "GPIO switches", + "no-gpio-switches": "No GPIO switches configured", + "add-gpio-switch": "Add GPIO switch", + "gpio-status-request": "GPIO status request", + "method-name": "Method name", + "method-body": "Method body", + "gpio-status-change-request": "GPIO status change request", + "parse-gpio-status-function": "Parse gpio status function", + "gpio-leds": "GPIO leds", + "no-gpio-leds": "No GPIO leds configured", + "add-gpio-led": "Add GPIO led" + }, + "html-card": { + "html": "HTML", + "css": "CSS" + }, "input-widgets": { "attribute-not-allowed": "El parámetro de atributo no se puede usar en este widget", "blocked-location": "La función de geolocalización está bloqueada en tu navegador", @@ -2649,7 +3859,570 @@ "update-successful": "Actualización exitosa", "update-attribute": "Actualizar atributo", "update-timeseries": "Actualizar series de tiempo", - "value": "Valor" + "value": "Valor", + "general-settings": "General settings", + "widget-title": "Widget title", + "claim-button-label": "Claiming button label", + "show-secret-key-field": "Show 'Secret key' input field", + "labels-settings": "Labels settings", + "show-labels": "Show labels", + "device-name-label": "Label for device name input field", + "secret-key-label": "Label for secret key input field", + "messages-settings": "Messages settings", + "claim-device-success-message": "Text message of successful device claiming", + "claim-device-not-found-message": "Text message when device not found", + "claim-device-failed-message": "Text message of failed device claiming", + "claim-device-name-required-message": "'Device name required' error message", + "claim-device-secret-key-required-message": "'Secret key required' error message", + "show-label": "Show label", + "label": "Label", + "required": "Required", + "required-error-message": "'Required' error message", + "show-result-message": "Show result message", + "integer-field-settings": "Integer field settings", + "min-value": "Min value", + "max-value": "Max value", + "double-field-settings": "Double field settings", + "text-field-settings": "Text field settings", + "min-length": "Min length", + "max-length": "Max length", + "checkbox-settings": "Checkbox settings", + "true-label": "Checked label", + "false-label": "Unchecked label", + "image-input-settings": "Image input settings", + "display-preview": "Display preview", + "display-clear-button": "Display clear button", + "display-apply-button": "Display apply button", + "display-discard-button": "Display discard button", + "datetime-field-settings": "Date/time field settings", + "display-time-input": "Display time input", + "latitude-key-name": "Latitude key name", + "longitude-key-name": "Longitude key name", + "show-get-location-button": "Show button 'Get current location'", + "use-high-accuracy": "Use high accuracy", + "location-fields-settings": "Location fields settings", + "latitude-label": "Label for latitude", + "longitude-label": "Label for longitude", + "input-fields-alignment": "Input fields alignment", + "input-fields-alignment-column": "Column (default)", + "input-fields-alignment-row": "Row", + "latitude-field-required": "Latitude field required", + "longitude-field-required": "Longitude field required", + "attribute-settings": "Attribute settings", + "widget-mode": "Widget mode", + "widget-mode-update-attribute": "Update attribute", + "widget-mode-update-timeseries": "Update timeseries", + "attribute-scope": "Attribute scope", + "attribute-scope-server": "Server attribute", + "attribute-scope-shared": "Shared attribute", + "value-required": "Value required", + "image-settings": "Image settings", + "image-format": "Image format", + "image-format-jpeg": "JPEG", + "image-format-png": "PNG", + "image-format-webp": "WEBP", + "image-quality": "Image quality that use lossy compression such as jpeg and webp", + "max-image-width": "Maximum image width", + "max-image-height": "Maximum image height", + "action-buttons": "Action buttons", + "show-action-buttons": "Show action buttons", + "update-all-values": "Update all values, not only modified", + "save-button-label": "'SAVE' button label", + "reset-button-label": "'UNDO' button label", + "group-settings": "Group settings", + "show-group-title": "Show title for group of fields, related to different entities", + "group-title": "Group title", + "fields-alignment": "Fields alignment", + "fields-alignment-row": "Row (default)", + "fields-alignment-column": "Column", + "fields-in-row": "Number of fields in the row", + "option-value": "Value (write 'null' for create empty option)", + "option-label": "Label", + "hide-input-field": "Hide input field", + "datakey-type": "Datakey type", + "datakey-type-server": "Server attribute (default)", + "datakey-type-shared": "Shared attribute", + "datakey-type-timeseries": "Timeseries", + "datakey-value-type": "Datakey value type", + "datakey-value-type-string": "String", + "datakey-value-type-double": "Double", + "datakey-value-type-integer": "Integer", + "datakey-value-type-boolean-checkbox": "Boolean (Checkbox)", + "datakey-value-type-boolean-switch": "Boolean (Switch)", + "datakey-value-type-date-time": "Date & Time", + "datakey-value-type-date": "Date", + "datakey-value-type-time": "Time", + "datakey-value-type-select": "Select", + "value-is-required": "Value is required", + "ability-to-edit-attribute": "Ability to edit attribute", + "ability-to-edit-attribute-editable": "Editable (default)", + "ability-to-edit-attribute-disabled": "Disabled", + "ability-to-edit-attribute-readonly": "Read-only", + "disable-on-datakey-name": "Disable on false value of another datakey (specify datakey name)", + "slide-toggle-settings": "Slide toggle settings", + "slide-toggle-label-position": "Slide toggle label position", + "slide-toggle-label-position-after": "After", + "slide-toggle-label-position-before": "Before", + "select-options": "Select options", + "no-select-options": "No select options configured", + "add-select-option": "Add select option", + "numeric-field-settings": "Numeric field settings", + "step-interval": "Step interval between values", + "error-messages": "Error messages", + "min-value-error-message": "'Min value' error message", + "max-value-error-message": "'Max value' error message", + "invalid-date-error-message": "'Invalid date' error message", + "icon-settings": "Icon settings", + "use-custom-icon": "Use custom icon", + "input-cell-icon": "Icon to show before input cell", + "value-conversion-settings": "Value conversion settings", + "get-value-settings": "Get value settings", + "use-get-value-function": "Use getValue function", + "get-value-function": "getValue function", + "set-value-settings": "Set value settings", + "use-set-value-function": "Use setValue function", + "set-value-function": "setValue function" + }, + "invalid-qr-code-text": "Invalid input text for QR code. Input should have a string type", + "qr-code": { + "use-qr-code-text-function": "Use QR code text function", + "qr-code-text-pattern": "QR code text pattern (for ex. '${entityName} | ${keyName} - some text.')", + "qr-code-text-pattern-required": "QR code text pattern is required.", + "qr-code-text-function": "QR code text function" + }, + "label-widget": { + "label-pattern": "Pattern", + "label-pattern-hint": "Hint: for ex. 'Text ${keyName} units.' or ${#<key index>} units'", + "label-pattern-required": "Pattern is required", + "label-position": "Position (Percentage relative to background)", + "x-pos": "X", + "y-pos": "Y", + "background-color": "Background color", + "font-settings": "Font settings", + "background-image": "Background image", + "labels": "Labels", + "no-labels": "No labels configured", + "add-label": "Add label" + }, + "navigation": { + "title": "Title", + "navigation-path": "Navigation path", + "filter-type": "Filter type", + "filter-type-all": "All items", + "filter-type-include": "Include items", + "filter-type-exclude": "Exclude items", + "items": "Items", + "enter-urls-to-filter": "Enter urls to filter..." + }, + "persistent-table": { + "rpc-id": "RPC ID", + "message-type": "Message type", + "method": "Method", + "params": "Params", + "created-time": "Created time", + "expiration-time": "Expiration time", + "retries": "Retries", + "status": "Status", + "filter": "Filter", + "refresh": "Refresh", + "add": "Add RPC request", + "details": "Details", + "delete": "Delete", + "delete-request-title": "Delete Persistent RPC request", + "delete-request-text": "Are you sure you want to delete request?", + "details-title": "Details RPC ID: ", + "additional-info": "Additional info", + "response": "Response", + "any-status": "Any status", + "rpc-status-list": "RPC status list", + "no-request-prompt": "No request to display", + "send-request": "Send request", + "add-title": "Create Persistent RPC request", + "method-error": "Method is required.", + "timeout-error": "Min timeout value is 5000 (5 seconds).", + "white-space-error": "White space is not allowed.", + "rpc-status": { + "QUEUED": "QUEUED", + "SENT": "SENT", + "DELIVERED": "DELIVERED", + "SUCCESSFUL": "SUCCESSFUL", + "TIMEOUT": "TIMEOUT", + "EXPIRED": "EXPIRED", + "FAILED": "FAILED" + }, + "rpc-search-status-all": "ALL", + "message-types": { + "false": "Two-way", + "true": "One-way" + }, + "general-settings": "General settings", + "enable-filter": "Enable filter", + "enable-sticky-header": "Display header while scrolling", + "enable-sticky-action": "Display actions column while scrolling", + "display-request-details": "Display request details", + "allow-send-request": "Allow send RPC request", + "allow-delete-request": "Allow delete request", + "columns-settings": "Columns settings", + "display-columns": "Columns to display", + "column": "Column", + "no-columns-found": "No columns found", + "no-columns-matching": "'{{column}}' not found." + }, + "rpc": { + "value-settings": "Value settings", + "initial-value": "Initial value", + "retrieve-value-settings": "Retrieve on/off value settings", + "retrieve-value-method": "Retrieve value using method", + "retrieve-value-method-none": "Don't retrieve", + "retrieve-value-method-rpc": "Call RPC get value method", + "retrieve-value-method-attribute": "Subscribe for attribute", + "retrieve-value-method-timeseries": "Subscribe for timeseries", + "attribute-value-key": "Attribute key", + "timeseries-value-key": "Timeseries key", + "get-value-method": "RPC get value method", + "parse-value-function": "Parse value function", + "update-value-settings": "Update value settings", + "set-value-method": "RPC set value method", + "convert-value-function": "Convert value function", + "rpc-settings": "RPC settings", + "request-timeout": "RPC request timeout (ms)", + "persistent-rpc-settings": "Persistent RPC settings", + "request-persistent": "RPC request persistent", + "persistent-polling-interval": "Polling interval (ms) to get persistent RPC command response", + "common-settings": "Common settings", + "switch-title": "Switch title", + "show-on-off-labels": "Show on/off labels", + "slide-toggle-label": "Slide toggle label", + "label-position": "Label position", + "label-position-before": "Before", + "label-position-after": "After", + "slider-color": "Slider color", + "slider-color-primary": "Primary", + "slider-color-accent": "Accent", + "slider-color-warn": "Warn", + "button-style": "Button style", + "button-raised": "Raised button", + "button-primary": "Primary color", + "button-background-color": "Button background color", + "button-text-color": "Button text color", + "widget-title": "Widget title", + "button-label": "Button label", + "device-attribute-scope": "Device attribute scope", + "server-attribute": "Server attribute", + "shared-attribute": "Shared attribute", + "device-attribute-parameters": "Device attribute parameters", + "is-one-way-command": "Is one way command", + "rpc-method": "RPC method", + "rpc-method-params": "RPC method params", + "show-rpc-error": "Show RPC command execution error", + "led-title": "LED title", + "led-color": "LED color", + "check-status-settings": "Check status settings", + "perform-rpc-status-check": "Perform RPC device status check", + "retrieve-led-status-value-method": "Retrieve led status value using method", + "led-status-value-attribute": "Device attribute containing led status value", + "led-status-value-timeseries": "Device timeseries containing led status value", + "check-status-method": "RPC check device status method", + "parse-led-status-value-function": "Parse led status value function", + "knob-title": "Knob title", + "min-value": "Minimum value", + "max-value": "Maximum value" + }, + "maps": { + "select-entity": "Select entity", + "select-entity-hint": "Hint: after selection click at the map to set position", + "tooltips": { + "placeMarker": "Click to place '{{entityName}}' entity", + "firstVertex": "Polygon for '{{entityName}}': click to place first point", + "firstVertex-cut": "Click to place first point", + "continueLine": "Polygon for '{{entityName}}': click to continue drawing", + "continueLine-cut": "Click to continue drawing", + "finishLine": "Click any existing marker to finish", + "finishPoly": "Polygon for '{{entityName}}': click first marker to finish and save", + "finishPoly-cut": "Click first marker to finish and save", + "finishRect": "Polygon for '{{entityName}}': click to finish and save", + "startCircle": "Circle for '{{entityName}}': click to place circle center", + "finishCircle": "Circle for '{{entityName}}': click to finish circle", + "placeCircleMarker": "Click to place circle marker" + }, + "actions": { + "finish": "Finish", + "cancel": "Cancel", + "removeLastVertex": "Remove last point" + }, + "buttonTitles": { + "drawMarkerButton": "Place entity", + "drawPolyButton": "Create polygon", + "drawLineButton": "Create polyline", + "drawCircleButton": "Create circle", + "drawRectButton": "Create rectangle", + "editButton": "Edit mode", + "dragButton": "Drag-drop mode", + "cutButton": "Cut polygon area", + "deleteButton": "Remove", + "drawCircleMarkerButton": "Create circle marker", + "rotateButton": "Rotate polygon" + }, + "map-provider-settings": "Map provider settings", + "map-provider": "Map provider", + "map-provider-google": "Google maps", + "map-provider-openstreet": "OpenStreet maps", + "map-provider-here": "HERE maps", + "map-provider-image": "Image map", + "map-provider-tencent": "Tencent maps", + "openstreet-provider": "OpenStreet map provider", + "openstreet-provider-mapnik": "OpenStreetMap.Mapnik (Default)", + "openstreet-provider-hot": "OpenStreetMap.HOT", + "openstreet-provider-esri-street": "Esri.WorldStreetMap", + "openstreet-provider-esri-topo": "Esri.WorldTopoMap", + "openstreet-provider-cartodb-positron": "CartoDB.Positron", + "openstreet-provider-cartodb-dark-matter": "CartoDB.DarkMatter", + "use-custom-provider": "Use custom provider", + "custom-provider-tile-url": "Custom provider tile URL", + "google-maps-api-key": "Google Maps API Key", + "default-map-type": "Default map type", + "google-map-type-roadmap": "Roadmap", + "google-map-type-satelite": "Satellite", + "google-map-type-hybrid": "Hybrid", + "google-map-type-terrain": "Terrain", + "map-layer": "Map layer", + "here-map-normal-day": "HERE.normalDay (Default)", + "here-map-normal-night": "HERE.normalNight", + "here-map-hybrid-day": "HERE.hybridDay", + "here-map-terrain-day": "HERE.terrainDay", + "credentials": "Credentials", + "here-app-id": "HERE app id", + "here-app-code": "HERE app code", + "tencent-maps-api-key": "Tencent Maps API Key", + "tencent-map-type-roadmap": "Roadmap", + "tencent-map-type-satelite": "Satellite", + "tencent-map-type-hybrid": "Hybrid", + "image-map-background": "Image map background", + "image-map-background-from-entity-attribute": "Take image map background from entity attribute", + "image-url-source-entity-alias": "Image URL source entity alias", + "image-url-source-entity-attribute": "Image URL source entity attribute", + "common-map-settings": "Common map settings", + "x-pos-key-name": "X position key name", + "y-pos-key-name": "Y position key name", + "latitude-key-name": "Latitude key name", + "longitude-key-name": "Longitude key name", + "default-map-zoom-level": "Default map zoom level (0 - 20)", + "default-map-center-position": "Default map center position (0,0)", + "disable-scroll-zooming": "Disable scroll zooming", + "disable-zoom-control-buttons": "Disable zoom control buttons", + "fit-map-bounds": "Fit map bounds to cover all markers", + "use-default-map-center-position": "Use default map center position", + "entities-limit": "Limit of entities to load", + "markers-settings": "Markers settings", + "marker-offset-x": "Marker X offset relative to position multiplied by marker width", + "marker-offset-y": "Marker Y offset relative to position multiplied by marker height", + "position-function": "Position conversion function, should return x,y coordinates as double from 0 to 1 each", + "draggable-marker": "Draggable marker", + "label": "Label", + "show-label": "Show label", + "use-label-function": "Use label function", + "label-pattern": "Label (pattern examples: '${entityName}', '${entityName}: (Text ${keyName} units.)' )", + "label-function": "Label function", + "tooltip": "Tooltip", + "show-tooltip": "Show tooltip", + "show-tooltip-action": "Action for displaying the tooltip", + "show-tooltip-action-click": "Show tooltip on click (Default)", + "show-tooltip-action-hover": "Show tooltip on hover", + "auto-close-tooltips": "Auto-close tooltips", + "use-tooltip-function": "Use tooltip function", + "tooltip-pattern": "Tooltip (for ex. 'Text ${keyName} units.' or Link text')", + "tooltip-function": "Tooltip function", + "tooltip-offset-x": "Tooltip X offset relative to marker anchor multiplied by marker width", + "tooltip-offset-y": "Tooltip Y offset relative to marker anchor multiplied by marker height", + "color": "Color", + "use-color-function": "Use color function", + "color-function": "Color function", + "marker-image": "Marker image", + "use-marker-image-function": "Use marker image function", + "custom-marker-image": "Custom marker image", + "custom-marker-image-size": "Custom marker image size (px)", + "marker-image-function": "Marker image function", + "marker-images": "Marker images", + "polygon-settings": "Polygon settings", + "show-polygon": "Show polygon", + "polygon-key-name": "Polygon key name", + "enable-polygon-edit": "Enable polygon edit", + "polygon-label": "Polygon label", + "show-polygon-label": "Show polygon label", + "use-polygon-label-function": "Use polygon label function", + "polygon-label-pattern": "Polygon label (pattern examples: '${entityName}', '${entityName}: (Text ${keyName} units.)' )", + "polygon-label-function": "Polygon label function", + "polygon-tooltip": "Polygon tooltip", + "show-polygon-tooltip": "Show polygon tooltip", + "auto-close-polygon-tooltips": "Auto-close polygon tooltips", + "use-polygon-tooltip-function": "Use polygon tooltip function", + "polygon-tooltip-pattern": "Tooltip (for ex. 'Text ${keyName} units.' or Link text')", + "polygon-tooltip-function": "Polygon tooltip function", + "polygon-color": "Polygon color", + "polygon-opacity": "Polygon opacity", + "use-polygon-color-function": "Use polygon color function", + "polygon-color-function": "Polygon color function", + "polygon-stroke": "Polygon stroke", + "stroke-color": "Stroke color", + "stroke-opacity": "Stroke opacity", + "stroke-weight": "Stroke weight", + "use-polygon-stroke-color-function": "Use polygon stroke color function", + "polygon-stroke-color-function": "Polygon stroke color function", + "circle-settings": "Circle settings", + "show-circle": "Show circle", + "circle-key-name": "Circle key name", + "enable-circle-edit": "Enable circle edit", + "circle-label": "Circle label", + "show-circle-label": "Show circle label", + "use-circle-label-function": "Use circle label function", + "circle-label-pattern": "Circle label (pattern examples: '${entityName}', '${entityName}: (Text ${keyName} units.)' )", + "circle-label-function": "Circle label function", + "circle-tooltip": "Circle tooltip", + "show-circle-tooltip": "Show circle tooltip", + "auto-close-circle-tooltips": "Auto-close circle tooltips", + "use-circle-tooltip-function": "Use circle tooltip function", + "circle-tooltip-pattern": "Tooltip (for ex. 'Text ${keyName} units.' or Link text')", + "circle-tooltip-function": "Circle tooltip function", + "circle-fill-color": "Circle fill color", + "circle-fill-color-opacity": "Circle fill color opacity", + "use-circle-fill-color-function": "Use circle fill color function", + "circle-fill-color-function": "Circle fill color function", + "circle-stroke": "Circle stroke", + "use-circle-stroke-color-function": "Use circle stroke color function", + "circle-stroke-color-function": "Circle stroke color function", + "markers-clustering-settings": "Markers clustering settings", + "use-map-markers-clustering": "Use map markers clustering", + "zoom-on-cluster-click": "Zoom when clicking on a cluster", + "max-cluster-zoom": "The maximum zoom level when a marker can be part of a cluster (0 - 18)", + "max-cluster-radius-pixels": "Maximum radius that a cluster will cover in pixels", + "cluster-zoom-animation": "Show animation on markers when zooming", + "show-markers-bounds-on-cluster-mouse-over": "Show the bounds of markers when mouse over a cluster", + "spiderfy-max-zoom-level": "Spiderfy at the max zoom level (to see all cluster markers)", + "load-optimization": "Load optimization", + "cluster-chunked-loading": "Use chunks for adding markers so that the page does not freeze", + "cluster-markers-lazy-load": "Use lazy load for adding markers", + "editor-settings": "Editor settings", + "enable-snapping": "Enable snapping to other vertices for precision drawing", + "init-draggable-mode": "Initialize map in draggable mode", + "hide-all-edit-buttons": "Hide all edit control buttons", + "hide-draw-buttons": "Hide draw buttons", + "hide-edit-buttons": "Hide edit buttons", + "hide-remove-button": "Hide remove button", + "route-map-settings": "Route map settings", + "trip-animation-settings": "Trip animation settings", + "normalization-step": "Normalization data step (ms)", + "tooltip-background-color": "Tooltip background color", + "tooltip-font-color": "Tooltip font color", + "tooltip-opacity": "Tooltip opacity (0-1)", + "auto-close-tooltip": "Auto-close tooltip", + "rotation-angle": "Set additional rotation angle for marker (deg)", + "path-settings": "Path settings", + "path-color": "Path color", + "use-path-color-function": "Use path color function", + "path-color-function": "Path color function", + "path-decorator": "Path decorator", + "use-path-decorator": "Use path decorator", + "decorator-symbol": "Decorator symbol", + "decorator-symbol-arrow-head": "Arrow", + "decorator-symbol-dash": "Dash", + "decorator-symbol-size": "Decorator symbol size (px)", + "use-path-decorator-custom-color": "Use path decorator custom color", + "decorator-custom-color": "Decorator custom color", + "decorator-offset": "Decorator offset", + "end-decorator-offset": "End decorator offset", + "decorator-repeat": "Decorator repeat", + "points-settings": "Points settings", + "show-points": "Show points", + "point-color": "Point color", + "point-size": "Point size (px)", + "use-point-color-function": "Use point color function", + "point-color-function": "Point color function", + "use-point-as-anchor": "Use point as anchor", + "point-as-anchor-function": "Point as anchor function", + "independent-point-tooltip": "Independent point tooltip" + }, + "markdown": { + "use-markdown-text-function": "Use markdown/HTML value function", + "markdown-text-function": "Markdown/HTML value function", + "markdown-text-pattern": "Markdown/HTML pattern (markdown or HTML with variables, for ex. '${entityName} or ${keyName} - some text.')", + "markdown-css": "Markdown/HTML CSS" + }, + "simple-card": { + "label-position": "Label position", + "label-position-left": "Left", + "label-position-top": "Top" + }, + "table": { + "common-table-settings": "Common Table Settings", + "enable-search": "Enable search", + "enable-sticky-header": "Always display header", + "enable-sticky-action": "Always display actions column", + "hidden-cell-button-display-mode": "Hidden cell button actions display mode", + "show-empty-space-hidden-action": "Show empty space instead of hidden cell button action", + "dont-reserve-space-hidden-action": "Don't reserve space for hidden action buttons", + "display-timestamp": "Display timestamp column", + "display-milliseconds": "Display timestamp milliseconds", + "display-pagination": "Display pagination", + "default-page-size": "Default page size", + "use-entity-label-tab-name": "Use entity label in tab name", + "hide-empty-lines": "Hide empty lines", + "row-style": "Row style", + "use-row-style-function": "Use row style function", + "row-style-function": "Row style function", + "cell-style": "Cell style", + "use-cell-style-function": "Use cell style function", + "cell-style-function": "Cell style function", + "cell-content": "Cell content", + "use-cell-content-function": "Use cell content function", + "cell-content-function": "Cell content function", + "show-latest-data-column": "Show latest data column", + "latest-data-column-order": "Latest data column order", + "entities-table-title": "Entities table title", + "enable-select-column-display": "Enable select columns to display", + "display-entity-name": "Display entity name column", + "entity-name-column-title": "Entity name column title", + "display-entity-label": "Display entity label column", + "entity-label-column-title": "Entity label column title", + "display-entity-type": "Display entity type column", + "default-sort-order": "Default sort order", + "column-width": "Column width (px or %)", + "default-column-visibility": "Default column visibility", + "column-visibility-visible": "Visible", + "column-visibility-hidden": "Hidden", + "column-selection-to-display": "Column selection in 'Columns to Display'", + "column-selection-to-display-enabled": "Enabled", + "column-selection-to-display-disabled": "Disabled", + "alarms-table-title": "Alarms table title", + "enable-alarms-selection": "Enable alarms selection", + "enable-alarms-search": "Enable alarms search", + "enable-alarm-filter": "Enable alarm filter", + "display-alarm-details": "Display alarm details", + "allow-alarms-ack": "Allow alarms acknowledgment", + "allow-alarms-clear": "Allow alarms clear" + }, + "value-source": { + "value-source": "Value source", + "predefined-value": "Predefined value", + "entity-attribute": "Value taken from entity attribute", + "value": "Value", + "source-entity-alias": "Source entity alias", + "source-entity-attribute": "Source entity attribute" + }, + "widget-font": { + "font-family": "Font family", + "size": "Size", + "relative-font-size": "Relative font size (percents)", + "font-style": "Style", + "font-style-normal": "Normal", + "font-style-italic": "Italic", + "font-style-oblique": "Oblique", + "font-weight": "Weight", + "font-weight-normal": "Normal", + "font-weight-bold": "Bold", + "font-weight-bolder": "Bolder", + "font-weight-lighter": "Lighter", + "color": "Color", + "shadow-color": "Shadow color" } }, "icon": { @@ -2664,6 +4437,7 @@ "row-click": "En click de fila", "polygon-click": "Clic en polígono", "marker-click": "En click de marcador", + "circle-click": "En click de círculo", "tooltip-tag-action": "Acción de la etiqueta Tooltip", "node-selected": "Clic en el nodo seleccionado", "element-click": "Clic en el elemento HTML", @@ -2672,6 +4446,6 @@ } }, "language": { - "language": "Lenguaje" + "language": "Idioma" } } From 165148a1264e826821df3299f527abb00f17d69b Mon Sep 17 00:00:00 2001 From: Andrii Shvaika Date: Fri, 3 Jun 2022 12:18:47 +0300 Subject: [PATCH 753/798] Fixed and improved startup sequence --- .../server/actors/service/DefaultActorService.java | 4 ++-- .../service/queue/DefaultTbCoreConsumerService.java | 4 ++-- .../queue/processing/AbstractConsumerService.java | 4 ++-- .../transport/TbCoreTransportApiService.java | 4 ++-- .../queue/common/DefaultTbQueueRequestTemplate.java | 6 +++--- .../queue/discovery/DummyDiscoveryService.java | 4 ++-- .../queue/discovery/HashPartitionService.java | 8 +++++--- .../server/queue/discovery/ZkDiscoveryService.java | 4 ++-- .../queue/usagestats/DefaultTbApiUsageClient.java | 4 +--- .../thingsboard/server/queue/util/AfterStartUp.java | 13 ++++++++++++- .../lwm2m/server/DefaultLwM2mTransportService.java | 7 +------ .../lwm2m/server/client/LwM2mClientContextImpl.java | 2 +- .../server/model/LwM2MModelConfigServiceImpl.java | 2 +- .../server/transport/snmp/SnmpTransportContext.java | 2 +- .../transport/service/DefaultTransportService.java | 11 +++++------ .../service/TransportQueueRoutingInfoService.java | 9 ++++++--- 16 files changed, 48 insertions(+), 40 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/actors/service/DefaultActorService.java b/application/src/main/java/org/thingsboard/server/actors/service/DefaultActorService.java index 0809afb4b5..3d3bf2bbbb 100644 --- a/application/src/main/java/org/thingsboard/server/actors/service/DefaultActorService.java +++ b/application/src/main/java/org/thingsboard/server/actors/service/DefaultActorService.java @@ -35,6 +35,7 @@ import org.thingsboard.server.actors.stats.StatsActor; import org.thingsboard.server.common.msg.queue.PartitionChangeMsg; import org.thingsboard.server.queue.discovery.TbApplicationEventListener; import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent; +import org.thingsboard.server.queue.util.AfterStartUp; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; @@ -113,8 +114,7 @@ public class DefaultActorService extends TbApplicationEventListener staleRequest, long currentNs) { if (currentNs >= staleRequest.getSubmitTime() + staleRequest.getTimeout()) { - log.warn("Request timeout detected, currentNs [{}], {}, key [{}]", currentNs, staleRequest, key); + log.debug("Request timeout detected, currentNs [{}], {}, key [{}]", currentNs, staleRequest, key); } else { - log.error("Request timeout detected, currentNs [{}], {}, key [{}]", currentNs, staleRequest, key); + log.info("Request timeout detected, currentNs [{}], {}, key [{}]", currentNs, staleRequest, key); } staleRequest.future.setException(new TimeoutException()); } @@ -173,7 +173,7 @@ public class DefaultTbQueueRequestTemplate expectedResponse = pendingRequests.remove(requestId); if (expectedResponse == null) { - log.warn("[{}] Invalid or stale request, response: {}", requestId, String.valueOf(response).replace("\n", " ")); + log.debug("[{}] Invalid or stale request, response: {}", requestId, String.valueOf(response).replace("\n", " ")); } else { expectedResponse.future.set(response); } diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/discovery/DummyDiscoveryService.java b/common/queue/src/main/java/org/thingsboard/server/queue/discovery/DummyDiscoveryService.java index 6718471abf..d124419135 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/discovery/DummyDiscoveryService.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/discovery/DummyDiscoveryService.java @@ -22,6 +22,7 @@ import org.springframework.context.annotation.DependsOn; import org.springframework.context.event.EventListener; import org.springframework.core.annotation.Order; import org.springframework.stereotype.Service; +import org.thingsboard.server.queue.util.AfterStartUp; import java.util.Collections; @@ -40,8 +41,7 @@ public class DummyDiscoveryService implements DiscoveryService { this.partitionService = partitionService; } - @EventListener(ApplicationReadyEvent.class) - @Order(value = 1) + @AfterStartUp(order = AfterStartUp.DISCOVERY_SERVICE) public void onApplicationEvent(ApplicationReadyEvent event) { partitionService.recalculatePartitions(serviceInfoProvider.getServiceInfo(), Collections.emptyList()); } diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/discovery/HashPartitionService.java b/common/queue/src/main/java/org/thingsboard/server/queue/discovery/HashPartitionService.java index e7db76bead..8a56a86bcc 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/discovery/HashPartitionService.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/discovery/HashPartitionService.java @@ -31,6 +31,7 @@ import org.thingsboard.server.gen.transport.TransportProtos.ServiceInfo; import org.thingsboard.server.queue.discovery.event.ClusterTopologyChangeEvent; import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent; import org.thingsboard.server.queue.discovery.event.ServiceListChangedEvent; +import org.thingsboard.server.queue.util.AfterStartUp; import javax.annotation.PostConstruct; import java.util.ArrayList; @@ -89,10 +90,10 @@ public class HashPartitionService implements PartitionService { @PostConstruct public void init() { this.hashFunction = forName(hashFunctionName); - partitionsInit(); } - private void partitionsInit() { + @AfterStartUp(order = AfterStartUp.QUEUE_INFO_INITIALIZATION) + public void partitionsInit() { QueueKey coreKey = new QueueKey(ServiceType.TB_CORE); partitionSizesMap.put(coreKey, corePartitions); partitionTopicsMap.put(coreKey, coreTopic); @@ -101,6 +102,7 @@ public class HashPartitionService implements PartitionService { String serviceType = serviceInfoProvider.getServiceType(); + if ("tb-transport".equals(serviceType)) { //If transport started earlier than tb-core int getQueuesRetries = 10; @@ -111,7 +113,7 @@ public class HashPartitionService implements PartitionService { queueRoutingInfoList = queueRoutingInfoService.getAllQueuesRoutingInfo(); break; } catch (Exception e) { - log.info("Failed to get queues routing info!"); + log.info("Failed to get queues routing info: {}!", e.getMessage()); getQueuesRetries--; } try { diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/discovery/ZkDiscoveryService.java b/common/queue/src/main/java/org/thingsboard/server/queue/discovery/ZkDiscoveryService.java index 8afd4dc768..89eda16cad 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/discovery/ZkDiscoveryService.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/discovery/ZkDiscoveryService.java @@ -41,6 +41,7 @@ import org.springframework.util.Assert; import org.thingsboard.common.util.ThingsBoardThreadFactory; import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.queue.discovery.event.ServiceListChangedEvent; +import org.thingsboard.server.queue.util.AfterStartUp; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; @@ -115,8 +116,7 @@ public class ZkDiscoveryService implements DiscoveryService, PathChildrenCacheLi .collect(Collectors.toList()); } - @EventListener(ApplicationReadyEvent.class) - @Order(value = 1) + @AfterStartUp(order = AfterStartUp.DISCOVERY_SERVICE) public void onApplicationEvent(ApplicationReadyEvent event) { if (stopped) { log.debug("Ignoring application ready event. Service is stopped."); diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/usagestats/DefaultTbApiUsageClient.java b/common/queue/src/main/java/org/thingsboard/server/queue/usagestats/DefaultTbApiUsageClient.java index 60867f314f..11f01cba2b 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/usagestats/DefaultTbApiUsageClient.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/usagestats/DefaultTbApiUsageClient.java @@ -59,9 +59,7 @@ public class DefaultTbApiUsageClient implements TbApiUsageClient { private final EnumMap> stats = new EnumMap<>(ApiUsageRecordKey.class); - @Lazy - @Autowired - private PartitionService partitionService; + private final PartitionService partitionService; private final SchedulerComponent scheduler; private final TbQueueProducerProvider producerProvider; private TbQueueProducer> msgProducer; diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/util/AfterStartUp.java b/common/queue/src/main/java/org/thingsboard/server/queue/util/AfterStartUp.java index 19e56e31cb..487e734c33 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/util/AfterStartUp.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/util/AfterStartUp.java @@ -30,6 +30,17 @@ import java.lang.annotation.Target; @EventListener(ApplicationReadyEvent.class) @Order public @interface AfterStartUp { + + int QUEUE_INFO_INITIALIZATION = 1; + int DISCOVERY_SERVICE = 2; + + int ACTOR_SYSTEM = 9; + int REGULAR_SERVICE = 10; + + int BEFORE_TRANSPORT_SERVICE = Integer.MAX_VALUE - 1001; + int TRANSPORT_SERVICE = Integer.MAX_VALUE - 1000; + int AFTER_TRANSPORT_SERVICE = Integer.MAX_VALUE - 999; + @AliasFor(annotation = Order.class, attribute = "value") - int order() default Integer.MAX_VALUE; + int order(); } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2mTransportService.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2mTransportService.java index 9b49030f7e..d28ee5ee4c 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2mTransportService.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2mTransportService.java @@ -20,15 +20,11 @@ import lombok.extern.slf4j.Slf4j; import org.eclipse.californium.scandium.config.DtlsConfig; import org.eclipse.californium.scandium.config.DtlsConnectorConfig; import org.eclipse.californium.scandium.dtls.cipher.CipherSuite; -import org.eclipse.leshan.core.node.LwM2mNode; import org.eclipse.leshan.core.node.codec.DefaultLwM2mDecoder; import org.eclipse.leshan.core.node.codec.DefaultLwM2mEncoder; -import org.eclipse.leshan.core.request.SendRequest; import org.eclipse.leshan.server.californium.LeshanServer; import org.eclipse.leshan.server.californium.LeshanServerBuilder; import org.eclipse.leshan.server.californium.registration.CaliforniumRegistrationStore; -import org.eclipse.leshan.server.registration.Registration; -import org.eclipse.leshan.server.send.SendListener; import org.springframework.stereotype.Component; import org.thingsboard.server.cache.ota.OtaPackageDataCache; import org.thingsboard.server.common.data.DataConstants; @@ -44,7 +40,6 @@ import org.thingsboard.server.transport.lwm2m.utils.LwM2mValueConverterImpl; import javax.annotation.PreDestroy; import java.security.cert.X509Certificate; -import java.util.Map; import static org.eclipse.californium.scandium.config.DtlsConfig.DTLS_RECOMMENDED_CIPHER_SUITES_ONLY; import static org.eclipse.californium.scandium.config.DtlsConfig.DTLS_RECOMMENDED_CURVES_ONLY; @@ -76,7 +71,7 @@ public class DefaultLwM2mTransportService implements LwM2MTransportService { private LeshanServer server; - @AfterStartUp(order = Integer.MAX_VALUE - 1) + @AfterStartUp(order = AfterStartUp.BEFORE_TRANSPORT_SERVICE) public void init() { this.server = getLhServer(); /* diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContextImpl.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContextImpl.java index 260dcd7ab1..c25c2d3c9a 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContextImpl.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContextImpl.java @@ -84,7 +84,7 @@ public class LwM2mClientContextImpl implements LwM2mClientContext { private final Map lwM2mClientsByRegistrationId = new ConcurrentHashMap<>(); private final Map profiles = new ConcurrentHashMap<>(); - @AfterStartUp(order = Integer.MAX_VALUE - 1) + @AfterStartUp(order = AfterStartUp.BEFORE_TRANSPORT_SERVICE) public void init() { String nodeId = context.getNodeId(); Set fetchedClients = clientStore.getAll(); diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/model/LwM2MModelConfigServiceImpl.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/model/LwM2MModelConfigServiceImpl.java index 4432a0c83c..2634672a3b 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/model/LwM2MModelConfigServiceImpl.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/model/LwM2MModelConfigServiceImpl.java @@ -69,7 +69,7 @@ public class LwM2MModelConfigServiceImpl implements LwM2MModelConfigService { private ConcurrentMap currentModelConfigs; - @AfterStartUp(order = Integer.MAX_VALUE - 1) + @AfterStartUp(order = AfterStartUp.BEFORE_TRANSPORT_SERVICE) private void init() { List models = modelStore.getAll(); log.debug("Fetched model configs: {}", models); diff --git a/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/SnmpTransportContext.java b/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/SnmpTransportContext.java index 42190974b9..1dd46dc7cd 100644 --- a/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/SnmpTransportContext.java +++ b/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/SnmpTransportContext.java @@ -73,7 +73,7 @@ public class SnmpTransportContext extends TransportContext { private final Map sessions = new ConcurrentHashMap<>(); private final Collection allSnmpDevicesIds = new ConcurrentLinkedDeque<>(); - @AfterStartUp(order = Integer.MAX_VALUE) + @AfterStartUp(order = AfterStartUp.AFTER_TRANSPORT_SERVICE) public void fetchDevicesAndEstablishSessions() { log.info("Initializing SNMP devices sessions"); diff --git a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/DefaultTransportService.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/DefaultTransportService.java index 5b29d3c3ff..9f6ddc87a4 100644 --- a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/DefaultTransportService.java +++ b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/DefaultTransportService.java @@ -157,13 +157,10 @@ public class DefaultTransportService implements TransportService { @Autowired @Lazy private TbApiUsageClient apiUsageClient; - @Autowired - @Lazy - private PartitionService partitionService; - private final Map statsMap = new LinkedHashMap<>(); private final Gson gson = new Gson(); + private final PartitionService partitionService; private final TbTransportQueueFactory queueProvider; private final TbQueueProducerProvider producerProvider; @@ -197,7 +194,8 @@ public class DefaultTransportService implements TransportService { private volatile boolean stopped = false; - public DefaultTransportService(TbServiceInfoProvider serviceInfoProvider, + public DefaultTransportService(PartitionService partitionService, + TbServiceInfoProvider serviceInfoProvider, TbTransportQueueFactory queueProvider, TbQueueProducerProvider producerProvider, NotificationsTopicService notificationsTopicService, @@ -207,6 +205,7 @@ public class DefaultTransportService implements TransportService { TransportRateLimitService rateLimitService, DataDecodingEncodingService dataDecodingEncodingService, SchedulerComponent scheduler, TransportResourceCache transportResourceCache, ApplicationEventPublisher eventPublisher) { + this.partitionService = partitionService; this.serviceInfoProvider = serviceInfoProvider; this.queueProvider = queueProvider; this.producerProvider = producerProvider; @@ -240,7 +239,7 @@ public class DefaultTransportService implements TransportService { mainConsumerExecutor = Executors.newSingleThreadExecutor(ThingsBoardThreadFactory.forName("transport-consumer")); } - @AfterStartUp + @AfterStartUp(order = AfterStartUp.TRANSPORT_SERVICE) private void start() { mainConsumerExecutor.execute(() -> { while (!stopped) { diff --git a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/TransportQueueRoutingInfoService.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/TransportQueueRoutingInfoService.java index ae1b6934c3..95fcef99f3 100644 --- a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/TransportQueueRoutingInfoService.java +++ b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/TransportQueueRoutingInfoService.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.common.transport.service; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; @@ -33,9 +34,11 @@ import java.util.stream.Collectors; @ConditionalOnExpression("'${service.type:null}'=='tb-transport'") public class TransportQueueRoutingInfoService implements QueueRoutingInfoService { - @Lazy - @Autowired - private TransportService transportService; + private final TransportService transportService; + + public TransportQueueRoutingInfoService(@Lazy TransportService transportService) { + this.transportService = transportService; + } @Override public List getAllQueuesRoutingInfo() { From 0ba1dc6d481d5ec443e42f0f3def57a323d8f215 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20N=C3=BA=C3=B1ez?= Date: Fri, 3 Jun 2022 11:40:25 +0200 Subject: [PATCH 754/798] Add security and 2FA translations --- .../assets/locale/locale.constant-es_ES.json | 406 +++++++++--------- 1 file changed, 203 insertions(+), 203 deletions(-) diff --git a/ui-ngx/src/assets/locale/locale.constant-es_ES.json b/ui-ngx/src/assets/locale/locale.constant-es_ES.json index 578153c62f..ddc8847ce8 100644 --- a/ui-ngx/src/assets/locale/locale.constant-es_ES.json +++ b/ui-ngx/src/assets/locale/locale.constant-es_ES.json @@ -238,23 +238,23 @@ "domain-schema-mixed": "HTTP+HTTPS", "enable": "Activar ajustes OAuth2", "domains": "Dominios", - "mobile-apps": "Aplicaciones móviles", - "no-mobile-apps": "No hay aplicaciones configuradas", - "mobile-package": "Paquete de aplicación", - "mobile-package-placeholder": "Ej.: mi.ejemplo.app", - "mobile-package-hint": "Para Android: El ID único de aplicación. Para iOS: Identificador Product bundle.", - "mobile-package-unique": "El paquete de aplicación debe ser único.", - "mobile-app-secret": "Secreto de aplicación", - "invalid-mobile-app-secret": "El secreto de aplicación sólo puede contener carácteres alfanuméricos y debe tener entre 16 y 2048 carácteres de longitud.", - "copy-mobile-app-secret": "Copiar secreto de aplicación", - "add-mobile-app": "Añadir aplicación", - "delete-mobile-app": "Borrar información de aplicación", - "providers": "Proveedores", - "platform-web": "Web", - "platform-android": "Android", - "platform-ios": "iOS", - "all-platforms": "Todas las plataformas", - "allowed-platforms": "Plataformas permitidas" + "mobile-apps": "Aplicaciones móviles", + "no-mobile-apps": "No hay aplicaciones configuradas", + "mobile-package": "Paquete de aplicación", + "mobile-package-placeholder": "Ej.: mi.ejemplo.app", + "mobile-package-hint": "Para Android: El ID único de aplicación. Para iOS: Identificador Product bundle.", + "mobile-package-unique": "El paquete de aplicación debe ser único.", + "mobile-app-secret": "Secreto de aplicación", + "invalid-mobile-app-secret": "El secreto de aplicación sólo puede contener carácteres alfanuméricos y debe tener entre 16 y 2048 carácteres de longitud.", + "copy-mobile-app-secret": "Copiar secreto de aplicación", + "add-mobile-app": "Añadir aplicación", + "delete-mobile-app": "Borrar información de aplicación", + "providers": "Proveedores", + "platform-web": "Web", + "platform-android": "Android", + "platform-ios": "iOS", + "all-platforms": "Todas las plataformas", + "allowed-platforms": "Plataformas permitidas" }, "smpp-provider": { "smpp-version": "Versión SMPP", @@ -299,61 +299,61 @@ "npi-private-numbering-plan": "9 - Privado", "npi-ermes-numbering-plan": "10 - ERMES (ETSI DE/PS 3 01-3)", "npi-internet": "13 - Internet (IP)", - "npi-wap-client-id": "18 - WAP Client Id (to be defined by WAP Forum)", - "scheme-smsc": "0 - SMSC Default Alphabet (ASCII for short and long code and to GSM for toll-free)", - "scheme-ia5": "1 - IA5 (ASCII for short and long code, Latin 9 for toll-free (ISO-8859-9))", - "scheme-octet-unspecified-2": "2 - Octet Unspecified (8-bit binary)", + "npi-wap-client-id": "18 - WAP Client Id (a definir por WAP Forum)", + "scheme-smsc": "0 - Alfabeto por defecto SMSC (ASCII para códigos cortos y largos y GSM para gratuitos)", + "scheme-ia5": "1 - IA5 (ASCII para códigos cortos y largos, Latin 9 para gratuitos (ISO-8859-9))", + "scheme-octet-unspecified-2": "2 - Octetos sin especificar (binario 8-bit)", "scheme-latin-1": "3 - Latin 1 (ISO-8859-1)", - "scheme-octet-unspecified-4": "4 - Octet Unspecified (8-bit binary)", + "scheme-octet-unspecified-4": "4 - Octetos sin especificar (binario 8-bit)", "scheme-jis": "5 - JIS (X 0208-1990)", - "scheme-cyrillic": "6 - Cyrillic (ISO-8859-5)", - "scheme-latin-hebrew": "7 - Latin/Hebrew (ISO-8859-8)", + "scheme-cyrillic": "6 - Ciríllico (ISO-8859-5)", + "scheme-latin-hebrew": "7 - Latin/Hebreo (ISO-8859-8)", "scheme-ucs-utf": "8 - UCS2/UTF-16 (ISO/IEC-10646)", - "scheme-pictogram-encoding": "9 - Pictogram Encoding", - "scheme-music-codes": "10 - Music Codes (ISO-2022-JP)", - "scheme-extended-kanji-jis": "13 - Extended Kanji JIS (X 0212-1990)", - "scheme-korean-graphic-character-set": "14 - Korean Graphic Character Set (KS C 5601/KS X 1001)" + "scheme-pictogram-encoding": "9 - Codificación por Pictograma", + "scheme-music-codes": "10 - Códigos musicales (ISO-2022-JP)", + "scheme-extended-kanji-jis": "13 - Kanji extendido JIS (X 0212-1990)", + "scheme-korean-graphic-character-set": "14 - Set de carácteres Koreanos (KS C 5601/KS X 1001)" }, - "queue-select-name": "Select queue name", - "queue-name": "Name", - "queue-name-required": "Queue name is required!", - "queues": "Queues", - "queue-partitions": "Partitions", - "queue-submit-strategy": "Submit strategy", - "queue-processing-strategy": "Processing strategy", - "queue-configuration": "Queue configuration", + "queue-select-name": "Seleccionar nombre de cola", + "queue-name": "Nombre", + "queue-name-required": "Nombre de cola requerido!", + "queues": "Colas", + "queue-partitions": "Particiones", + "queue-submit-strategy": "Estrategia de envíos", + "queue-processing-strategy": "Estrategia de procesamiento", + "queue-configuration": "Configuración Cola", "2fa": { - "2fa": "Two-factor authentication", - "available-providers": "Available providers", - "issuer-name": "Issuer name", - "issuer-name-required": "Issuer name is required.", - "max-verification-failures-before-user-lockout": "Max verification failures before user lockout", - "max-verification-failures-before-user-lockout-pattern": "Max verification failures must be a positive integer.", - "number-of-checking-attempts": "Number of checking attempts", - "number-of-checking-attempts-pattern": "Number of checking attempts must be a positive integer.", - "number-of-checking-attempts-required": "Number of checking attempts is required.", - "number-of-codes": "Number of codes", - "number-of-codes-pattern": "Number of codes must be a positive integer.", - "number-of-codes-required": "Number of codes is required.", - "provider": "Provider", - "retry-verification-code-period": "Retry verification code period (sec)", - "retry-verification-code-period-pattern": "Minimal period time is 5 sec", - "retry-verification-code-period-required": "Retry verification code period is required.", - "total-allowed-time-for-verification": "Total allowed time for verification (sec)", - "total-allowed-time-for-verification-pattern": "Minimal total allowed time is 60 sec", - "total-allowed-time-for-verification-required": "Total allowed time is required.", - "use-system-two-factor-auth-settings": "Use system two factor auth settings", - "verification-code-check-rate-limit": "Verification code check rate limit", - "verification-code-lifetime": "Verification code lifetime (sec)", - "verification-code-lifetime-pattern": "Verification code lifetime must be a positive integer.", - "verification-code-lifetime-required": "Verification code lifetime is required.", - "verification-message-template": "Verification message template", - "verification-limitations": "Verification limitations", - "verification-message-template-pattern": "Verification message need to contains pattern: ${code}", - "verification-message-template-required": "Verification message template is required.", - "within-time": "Within time (sec)", - "within-time-pattern": "Time must be a positive integer.", - "within-time-required": "Time is required." + "2fa": "Autenticación de dos factores (2FA)", + "available-providers": "Proveedores disponibles", + "issuer-name": "Nombre de emisor", + "issuer-name-required": "Nombre de emisor requerido.", + "max-verification-failures-before-user-lockout": "Máximo de fallos de verificación antes de bloquear cuenta", + "max-verification-failures-before-user-lockout-pattern": "Máximo de fallos debe ser un número entero positivo.", + "number-of-checking-attempts": "Número de intentos de verificación", + "number-of-checking-attempts-pattern": "Número de intentos debe ser un número entero positivo.", + "number-of-checking-attempts-required": "Número de intentos requerido.", + "number-of-codes": "Número de códigos", + "number-of-codes-pattern": "Número de códigos debe ser un número entero positivo.", + "number-of-codes-required": "Número de códigos requerido.", + "provider": "Proveedor", + "retry-verification-code-period": "Reintentos de código de verificación (sec)", + "retry-verification-code-period-pattern": "El período mínimo es de 5 sec", + "retry-verification-code-period-required": "Reintentos requerido.", + "total-allowed-time-for-verification": "Total de tiempo permitido para verificación (sec)", + "total-allowed-time-for-verification-pattern": "El mínimo del total de tiempo perminito es de 60 sec", + "total-allowed-time-for-verification-required": "Total de tiempo requerido.", + "use-system-two-factor-auth-settings": "Usar ajustes 2FA del sistema", + "verification-code-check-rate-limit": "Límite de chequeo del código de verificación", + "verification-code-lifetime": "Tiempo de vida del código de verificación (sec)", + "verification-code-lifetime-pattern": "Tiempo de vida del código de verificación debe ser un número entero positivo.", + "verification-code-lifetime-required": "Tiempo de vida requerido.", + "verification-message-template": "Plantilla del mensaje de verificación", + "verification-limitations": "Límites de verificación", + "verification-message-template-pattern": "El mensaje de verificación debe contener el patrón: ${code}", + "verification-message-template-required": "Plantilla de mensaje de verificación requerida.", + "within-time": "Rango de tiempo (sec)", + "within-time-pattern": "Tiempo debe ser un número entero positivo.", + "within-time-required": "Tiempo requerido." } }, "alarm": { @@ -2401,22 +2401,22 @@ "access-token": "Token de acceso", "x509": "X.509", "mqtt": { - "client-id": "Client ID MQTT", - "user-name": "Usuario MQTT", - "password": "Contraseña MQTT" + "client-id": "Client ID MQTT", + "user-name": "Usuario MQTT", + "password": "Contraseña MQTT" }, "lwm2m": { - "client-endpoint": "Nombre endpoint cliente LwM2M", - "security-config-mode": "Configuración de seguridad LwM2M", - "client-identity": "Identidad cliente LwM2M", - "client-key": "Clave cliente LwM2M", - "client-cert": "Clave pública cliente LwM2M", - "bootstrap-server-security-mode": "Modo seguridad servidor Bootstrap LwM2M", - "bootstrap-server-secret-key": "Clave secreta servidor Bootstrap LwM2M", - "bootstrap-server-public-key-id": "Clave pública o id servidor Bootstrap LwM2M", - "lwm2m-server-security-mode": "Modo seguridad servidor LwM2M", - "lwm2m-server-secret-key": "Clave secreta servidor LwM2M", - "lwm2m-server-public-key-id": "Clave pública servidor LwM2M" + "client-endpoint": "Nombre endpoint cliente LwM2M", + "security-config-mode": "Configuración de seguridad LwM2M", + "client-identity": "Identidad cliente LwM2M", + "client-key": "Clave cliente LwM2M", + "client-cert": "Clave pública cliente LwM2M", + "bootstrap-server-security-mode": "Modo seguridad servidor Bootstrap LwM2M", + "bootstrap-server-secret-key": "Clave secreta servidor Bootstrap LwM2M", + "bootstrap-server-public-key-id": "Clave pública o id servidor Bootstrap LwM2M", + "lwm2m-server-security-mode": "Modo seguridad servidor LwM2M", + "lwm2m-server-secret-key": "Clave secreta servidor LwM2M", + "lwm2m-server-public-key-id": "Clave pública servidor LwM2M" }, "isgateway": "Es Gateway", "activity-time-from-gateway-device": "Fecha de actividad desde el dispositivo gateway", @@ -2504,19 +2504,19 @@ "login-with": "Login con {{name}}", "or": "o", "error": "Error de Login", - "verify-your-identity": "Verify your identity", - "select-way-to-verify": "Select a way to verify", - "resend-code": "Resend code", - "resend-code-wait": "Resend code in { time, plural, 1 {1 second} other {# seconds} }", - "try-another-way": "Try another way", - "totp-auth-description": "Please enter the security code from your authenticator app.", - "totp-auth-placeholder": "Code", - "sms-auth-description": "A security code has been sent to your phone at {{contact}}.", - "sms-auth-placeholder": "SMS code", - "email-auth-description": "A security code has been sent to your email address at {{contact}}.", - "email-auth-placeholder": "Email code", - "backup-code-auth-description": "Please enter one of your backup codes.", - "backup-code-auth-placeholder": "Backup code" + "verify-your-identity": "Verificar identidad", + "select-way-to-verify": "Selecciona el modo de verificación", + "resend-code": "Reenviar código", + "resend-code-wait": "Reenviar código en { time, plural, 1 {1 segundo} other {# segundos} }", + "try-another-way": "Otros modos de verificación", + "totp-auth-description": "Por favor, introduce el código de seguridad de tu aplicación de autenticación.", + "totp-auth-placeholder": "Código", + "sms-auth-description": "Se ha enviado un código de verificación a tu número: {{contact}} proporcionado.", + "sms-auth-placeholder": "Código SMS", + "email-auth-description": "Se ha enviado un código de verificación al email {{contact}} proporcionado.", + "email-auth-placeholder": "Código Email", + "backup-code-auth-description": "Por favor, introduce el código de backup.", + "backup-code-auth-placeholder": "Código de Backup" }, "markdown": { "edit": "Editar", @@ -2525,69 +2525,69 @@ "copied": "Copiado!" }, "ota-update": { - "add": "Añadir paquete", - "assign-firmware": "Firmware asignado", - "assign-firmware-required": "Firmware asignado requerido", - "assign-software": "Software asignado", - "assign-software-required": "Software asignado requerido", - "auto-generate-checksum": "Auto-generar checksum", - "checksum": "Checksum", - "checksum-hint": "Si el checksum está vacío, se generará automáticamente", - "checksum-algorithm": "Algoritmo checksum", - "checksum-copied-message": "El checksum del paquete se ha copiado al portapapeles", - "change-firmware": "El cambio del firmware provocará la actualización de { count, plural, 1 {1 dispositivo} other {# dispositivos} }.", - "change-software": "El cambio del software provocará la actualización de { count, plural, 1 {1 dispositivo} other {# dispositivos} }.", - "chose-compatible-device-profile": "El paquete subido, sólamente estará disponible para los dispositivos con el perfil seleccionado.", - "chose-firmware-distributed-device": "Elige el firmware que se distribuirá a los dispositivos", - "chose-software-distributed-device": "Elige el software que se distribuirá a los dispositivos", - "content-type": "Tipo de contenido", - "copy-checksum": "Copiar checksum", - "copy-direct-url": "Copiar URL directa", - "copyId": "Copiar Id de paquete", - "copied": "Copiado!", - "delete": "Borrar paquete", - "delete-ota-update-text": "Atención, tras la confirmación la actualización OTA será irrecuperable.", - "delete-ota-update-title": "Estás seguro de borrar la actualización OTA '{{title}}'?", - "delete-ota-updates-text": "Atención, tras la confirmación todas las actualizaciones OTA seleccionadas se borrarán.", - "delete-ota-updates-title": "Estás seguro de borrar { count, plural, 1 {1 Actualización OTA} other {# Actualizaciones OTA} }?", - "description": "Descripción", - "direct-url": "URL Directa", - "direct-url-copied-message": "La URL directa del paquete se ha copiado al portapapeles", - "direct-url-required": "URL Directa requerida", - "download": "Descargar paquete", - "drop-file": "Arrastra un fichero o haz click para seleccionar un fichero a subir.", - "drop-package-file-or": "Arrastrar y soltar un fichero o", - "file-name": "Nombre del fichero", - "file-size": "Tamaño del fichero", - "file-size-bytes": "Tamaño del fichero en bytes", - "idCopiedMessage": "El Id del paquete se ha copiado al portapapeles", - "no-firmware-matching": "No se ha encontrado ningún paquete de firmware OTA compatible que coincidan con '{{entity}}'.", - "no-firmware-text": "No hay aprovisionado ningún paquete de firmware OTA.", - "no-packages-text": "No se han encontrado paquetes", - "no-software-matching": "No se ha encontrado ningún paquete de software OTA compatible que coincidan con '{{entity}}'.", - "no-software-text": "No hay aprovisionado ningún paquete de software OTA.", - "ota-update": "Actualización OTA", - "ota-update-details": "Detalles de actualización OTA", - "ota-updates": "Actualizaciones OTA", - "package-type": "Tipo de paquete", - "packages-repository": "Repositorio de paquetes", - "search": "Buscar paquetes", - "selected-package": "{ count, plural, 1 {1 paquete} other {# paquetes} } selected", - "title": "Título", - "title-required": "Título requerido.", - "title-max-length": "El título debe ser menor de 256", - "types": { - "firmware": "Firmware", - "software": "Software" - }, - "upload-binary-file": "Subir fichero binario", - "use-external-url": "Usar una URL externa", - "version": "Versión", - "version-required": "Versión requerida.", - "version-tag": "Tag de Versión", - "version-tag-hint": "El tag de versión debe coincidir con la versión reportada por el dispositivo.", - "version-max-length": "Versión debe ser menor que 256", - "warning-after-save-no-edit": "Una vez que el paquete se haya subido, no se podrá modificar el título, versión, perfil de dispositivo y tipo de paquete." + "add": "Añadir paquete", + "assign-firmware": "Firmware asignado", + "assign-firmware-required": "Firmware asignado requerido", + "assign-software": "Software asignado", + "assign-software-required": "Software asignado requerido", + "auto-generate-checksum": "Auto-generar checksum", + "checksum": "Checksum", + "checksum-hint": "Si el checksum está vacío, se generará automáticamente", + "checksum-algorithm": "Algoritmo checksum", + "checksum-copied-message": "El checksum del paquete se ha copiado al portapapeles", + "change-firmware": "El cambio del firmware provocará la actualización de { count, plural, 1 {1 dispositivo} other {# dispositivos} }.", + "change-software": "El cambio del software provocará la actualización de { count, plural, 1 {1 dispositivo} other {# dispositivos} }.", + "chose-compatible-device-profile": "El paquete subido, sólamente estará disponible para los dispositivos con el perfil seleccionado.", + "chose-firmware-distributed-device": "Elige el firmware que se distribuirá a los dispositivos", + "chose-software-distributed-device": "Elige el software que se distribuirá a los dispositivos", + "content-type": "Tipo de contenido", + "copy-checksum": "Copiar checksum", + "copy-direct-url": "Copiar URL directa", + "copyId": "Copiar Id de paquete", + "copied": "Copiado!", + "delete": "Borrar paquete", + "delete-ota-update-text": "Atención, tras la confirmación la actualización OTA será irrecuperable.", + "delete-ota-update-title": "Estás seguro de borrar la actualización OTA '{{title}}'?", + "delete-ota-updates-text": "Atención, tras la confirmación todas las actualizaciones OTA seleccionadas se borrarán.", + "delete-ota-updates-title": "Estás seguro de borrar { count, plural, 1 {1 Actualización OTA} other {# Actualizaciones OTA} }?", + "description": "Descripción", + "direct-url": "URL Directa", + "direct-url-copied-message": "La URL directa del paquete se ha copiado al portapapeles", + "direct-url-required": "URL Directa requerida", + "download": "Descargar paquete", + "drop-file": "Arrastra un fichero o haz click para seleccionar un fichero a subir.", + "drop-package-file-or": "Arrastrar y soltar un fichero o", + "file-name": "Nombre del fichero", + "file-size": "Tamaño del fichero", + "file-size-bytes": "Tamaño del fichero en bytes", + "idCopiedMessage": "El Id del paquete se ha copiado al portapapeles", + "no-firmware-matching": "No se ha encontrado ningún paquete de firmware OTA compatible que coincidan con '{{entity}}'.", + "no-firmware-text": "No hay aprovisionado ningún paquete de firmware OTA.", + "no-packages-text": "No se han encontrado paquetes", + "no-software-matching": "No se ha encontrado ningún paquete de software OTA compatible que coincidan con '{{entity}}'.", + "no-software-text": "No hay aprovisionado ningún paquete de software OTA.", + "ota-update": "Actualización OTA", + "ota-update-details": "Detalles de actualización OTA", + "ota-updates": "Actualizaciones OTA", + "package-type": "Tipo de paquete", + "packages-repository": "Repositorio de paquetes", + "search": "Buscar paquetes", + "selected-package": "{ count, plural, 1 {1 paquete} other {# paquetes} } selected", + "title": "Título", + "title-required": "Título requerido.", + "title-max-length": "El título debe ser menor de 256", + "types": { + "firmware": "Firmware", + "software": "Software" + }, + "upload-binary-file": "Subir fichero binario", + "use-external-url": "Usar una URL externa", + "version": "Versión", + "version-required": "Versión requerida.", + "version-tag": "Tag de Versión", + "version-tag-hint": "El tag de versión debe coincidir con la versión reportada por el dispositivo.", + "version-max-length": "Versión debe ser menor que 256", + "warning-after-save-no-edit": "Una vez que el paquete se haya subido, no se podrá modificar el título, versión, perfil de dispositivo y tipo de paquete." }, "position": { "top": "Superior", @@ -2606,59 +2606,59 @@ "tokenCopiedWarnMessage": "JWT caducado, por favor actualiza la página." }, "security": { - "security": "Security", + "security": "Seguridad", "2fa": { - "2fa": "Two-factor authentication", - "2fa-description": "Two-factor authentication protects your account from unauthorized access. All you have to do is enter a security code when you log in.", - "authenticate-with": "You can authenticate with:", - "disable-2fa-provider-text": "Disabling {{name}} will make your account less secure", - "disable-2fa-provider-title": "Are you sure you want to disable {{name}}?", - "get-new-code": "Get new code", - "main-2fa-method": "Use as main two-factor authentication method", + "2fa": "Autenticación de doble factor (2FA)", + "2fa-description": "La autenticación de doble factor proteje tu cuenta de accesos no autorizados. Lo único que tienes que hacer es entrar un código de seguridad al hacer login.", + "authenticate-with": "Puedes autenticarte con:", + "disable-2fa-provider-text": "Desactivar {{name}} hará tu cuenta menos segura", + "disable-2fa-provider-title": "Estás seguro de desactivar {{name}}?", + "get-new-code": "Obtener un nuevo código", + "main-2fa-method": "Usar como método de autenticación de doble factor principal", "dialog": { - "activation-step-description-email": "The next time you login in, you will be prompted to enter the security code that will be sent to your email address.", - "activation-step-description-sms": "The next time you login in, you will be prompted to enter the security code that will be sent to the phone number.", - "activation-step-description-totp": "The next time you login in, you will need to provide a two-factor authentication code.", - "activation-step-label": "Activation", - "backup-code-description": "Print out the codes so you have them handy when you need to use them to log in to your account. You can use each backup code once.", - "backup-code-warn": "Once you leave this page, these codes cannot be shown again. Store them safely using the options below.", - "download-txt": "Download (txt)", - "email-step-description": "Enter an email to use as your authenticator.", + "activation-step-description-email": "La próxima vez que hagas log-in, se deberá introducir el código de seguridad que se enviará a tu dirección de email.", + "activation-step-description-sms": "La próxima vez que hagas log-in, se deberá introducir el código de seguridad que se enviará a tu número de teléfono.", + "activation-step-description-totp": "La próxima vez que hagas log-in, se deberá introducir el código de seguridad de doble factor.", + "activation-step-label": "Activación", + "backup-code-description": "Imprime los códigos y guárdalos en un lugar seguro, se necesitarán para hacer login en tu cuenta. Puedes usar cada código de backup sólo una vez.", + "backup-code-warn": "Una vez que salgas de esta página, estos códigos no se volverán a mostrar. Guárdalos en un lugar seguro usando las opciones a continuación.", + "download-txt": "Descargar (txt)", + "email-step-description": "Introduce tu email para usar como autenticador.", "email-step-label": "Email", - "enable-email-title": "Enable email authenticator", - "enable-sms-title": "Enable SMS authenticator", - "enable-totp-title": "Enable authenticator app", - "enter-verification-code": "Enter the 6-digit code here", - "get-backup-code-title": "Get backup code", - "next": "Next", - "scan-qr-code": "Scan this QR code with your verification app", - "send-code": "Send code", - "sms-step-description": "Enter a phone number to use as your authenticator.", - "sms-step-label": "Phone Number", - "success": "Success!", - "totp-step-description-install": "You can install apps like Google Authenticator, Authy, or Duo.", - "totp-step-description-open": "Open the authenticator app on your mobile phone.", - "totp-step-label": "Get app", - "verification-code": "6-digit code", - "verification-code-invalid": "Invalid verification code format", - "verification-code-incorrect": "Verification code is incorrect", - "verification-code-many-request": "Too many requests check verification code", - "verification-step-description": "Enter a 6-digit code we just sent to {{address}}", - "verification-step-label": "Verification" + "enable-email-title": "Activar autenticador por email", + "enable-sms-title": "Activar autenticador por SMS", + "enable-totp-title": "Activar autenticador por app", + "enter-verification-code": "Entra el código de 6-dígitos", + "get-backup-code-title": "Obtener copia de seguridad", + "next": "Siguiente", + "scan-qr-code": "Escanea el código QR con tu aplicación de autenticación", + "send-code": "Enviar código", + "sms-step-description": "Introduce tu número de teléfono para usar como autenticador.", + "sms-step-label": "Número de teléfono", + "success": "Éxito!", + "totp-step-description-install": "Puedes instalar la aplicación Google Authenticator, Authy, o Duo.", + "totp-step-description-open": "Abre la aplicación de autenticación en tu teléfono móvil.", + "totp-step-label": "Obtener app", + "verification-code": "Código de 6-dígitos", + "verification-code-invalid": "Formáto de código inválido", + "verification-code-incorrect": "El código de verificación es incorrecto", + "verification-code-many-request": "Demasiadas solicitudes, revisa el código de verificación", + "verification-step-description": "Introduce el código de 6-dígitos que acabamos de enviar a {{address}}", + "verification-step-label": "Verificación" }, "provider": { "email": "Email", - "email-description": "Use a security code sent to your email address to authenticate.", - "email-hint": "Authentication codes are sent via email to {{ info }}", + "email-description": "Usar un código de seguridad enviado a tu email para autenticarte.", + "email-hint": "Los códigos de autenticación se han enviado por email a {{ info }}", "sms": "SMS", - "sms-description": "Use your phone to authenticate. We'll send you a security code via SMS message when you log in.", - "sms-hint": "Authentication codes are sent by text message to {{ info }}", - "totp": "Authenticator app", - "totp-description": "Use apps like Google Authenticator, Authy, or Duo on your phone to authenticate. It will generate a security code for logging in.", - "totp-hint": "Authenticator app is set up for your account", - "backup_code": "Backup code", - "backup-code-description": "These printable one-time passcodes allow you to sign in when away from your phone, like when you’re traveling.", - "backup-code-hint": "{{ info }} single-use codes are active at this time" + "sms-description": "Usar tu teléfono para autenticarte. Enviaremos un código de seguridad vía SMS.", + "sms-hint": "Los códigos de autenticación se han enviado por mensaje de texto SMS a {{ info }}", + "totp": "App de autenticación", + "totp-description": "Usar aplicaciones como Google Authenticator, Authy, o Duo en tu teléfono para autenticarte. Se generará un código de seguridad para hacer login.", + "totp-hint": "La aplicación de autenticación se ha configurado en tu cuenta", + "backup_code": "Código Backup", + "backup-code-description": "Estos códigos de seguridad imprimibles son de un solo uso, te permiten identificarte cuando no tengas el teléfono a mano, útil cuando se viaja.", + "backup-code-hint": "{{ info }} códigos de un solo uso activos en este momento" } } }, From a87b91b2dfb45b041d3ebf66cfb5f23a7fc70ccf Mon Sep 17 00:00:00 2001 From: Andrii Shvaika Date: Fri, 3 Jun 2022 13:28:31 +0300 Subject: [PATCH 755/798] LwM2M Transport Service --- .../transport/lwm2m/server/DefaultLwM2mTransportService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2mTransportService.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2mTransportService.java index d28ee5ee4c..e4a9a2492f 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2mTransportService.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2mTransportService.java @@ -71,7 +71,7 @@ public class DefaultLwM2mTransportService implements LwM2MTransportService { private LeshanServer server; - @AfterStartUp(order = AfterStartUp.BEFORE_TRANSPORT_SERVICE) + @AfterStartUp(order = AfterStartUp.AFTER_TRANSPORT_SERVICE) public void init() { this.server = getLhServer(); /* From 9382f6928c6d4bfb2eb39c375f3118b827c02687 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20N=C3=BA=C3=B1ez?= Date: Fri, 3 Jun 2022 12:51:19 +0200 Subject: [PATCH 756/798] Add queues tranlations --- .../assets/locale/locale.constant-es_ES.json | 144 +++++++++--------- 1 file changed, 72 insertions(+), 72 deletions(-) diff --git a/ui-ngx/src/assets/locale/locale.constant-es_ES.json b/ui-ngx/src/assets/locale/locale.constant-es_ES.json index ddc8847ce8..ea6196fa8a 100644 --- a/ui-ngx/src/assets/locale/locale.constant-es_ES.json +++ b/ui-ngx/src/assets/locale/locale.constant-es_ES.json @@ -2873,84 +2873,84 @@ "browser-time": "Hora del navegador" }, "queue": { - "queue-name": "Queue", + "queue-name": "Cola", "no-queues-matching": "No se encontraron colas que coincidan con '{{queue}}'.", "select_name": "Selecciona el nombre de la cola", "name": "Nombre Cola", "name_required": "Necesario especificar el nombre de cola", - "name-unique": "Queue name is not unique!", - "queue-required": "Queue is required!", - "topic-required": "Queue topic is required!", - "poll-interval-required": "Poll interval is required!", - "poll-interval-min-value": "Poll interval value can't be less then 1", - "partitions-required": "Partitions is required!", - "partitions-min-value": "Partitions value can't be less then 1", - "pack-processing-timeout-required": "Processing timeout is required", - "pack-processing-timeout-min-value": "Processing timeout value can't be less then 1", - "batch-size-required": "Batch size is required!", - "batch-size-min-value": "Batch size value can't be less then 1", - "retries-required": "Retries is required!", - "retries-min-value": "Retries value can't be negative", - "failure-percentage-required": "Failure percentage is required!", - "failure-percentage-min-value": "Failure percentage value can't be less then 0", - "failure-percentage-max-value": "Failure percentage value can't be more then 100", - "pause-between-retries-required": "Pause between retries is required!", - "pause-between-retries-min-value": "Pause between retries value can't be less then 1", - "max-pause-between-retries-required": "Max pause between retries is required!", - "max-pause-between-retries-min-value": "Max pause between retries value can't be less then 1", - "submit-strategy-type-required": "Submit strategy type is required!", - "processing-strategy-type-required": "Processing strategy type is required!", - "queues": "Queues", - "selected-queues": "{ count, plural, 1 {1 queue} other {# queues} } selected", - "delete-queue-title": "Are you sure you want to delete the queue '{{queueName}}'?", - "delete-queues-title": "Are you sure you want to delete { count, plural, 1 {1 queue} other {# queues} }?", - "delete-queue-text": "Be careful, after the confirmation the queue and all related data will become unrecoverable.", - "delete-queues-text": "After the confirmation all selected queues will be deleted and won't be accessible.", - "search": "Search queue", - "add" : "Add queue", - "details": "Queue details", + "name-unique": "El nombre de cola ya existe!", + "queue-required": "Cola requerida!", + "topic-required": "Topic cola requerido!", + "poll-interval-required": "Intervalo de obtención requerido!", + "poll-interval-min-value": "El intervalo no debe ser menor de 1", + "partitions-required": "Particiones requeridas!", + "partitions-min-value": "El valor de particion no debe ser menor de 1", + "pack-processing-timeout-required": "Timeout de procesamiento", + "pack-processing-timeout-min-value": "Timeout de procesamiento no puede ser menor de 1", + "batch-size-required": "Tamaño del lote requerido!", + "batch-size-min-value": "El valor de tamaño de lote no puede ser menor de 1", + "retries-required": "Reintentos requerido!", + "retries-min-value": "El valor de reintentos no puede ser negativo", + "failure-percentage-required": "Porcentaje de fallos requerido!", + "failure-percentage-min-value": "Porcentaje de fallos no puede ser menor de 0", + "failure-percentage-max-value": "Porcentaje de fallos no puede ser mayor de 100", + "pause-between-retries-required": "Pausa entre reintentos requerido!", + "pause-between-retries-min-value": "Pausa mínima entre reintentos no puede ser menor de 1", + "max-pause-between-retries-required": "Pausa máxima entre reintentos requerido!", + "max-pause-between-retries-min-value": "Pausa máxima entre reintentos no puede ser menor de 1", + "submit-strategy-type-required": "Estrategia de envío requerida!", + "processing-strategy-type-required": "Estrategia de procesamiento requerida!", + "queues": "Colas", + "selected-queues": "{ count, plural, 1 {1 cola} other {# colas} } seleccionadas", + "delete-queue-title": "Estás seguro de borrar la cola '{{queueName}}'?", + "delete-queues-title": "Estás seguro de borrar { count, plural, 1 {1 cola} other {# colas} }?", + "delete-queue-text": "Atención, tras la confirmacion la cola y todos sus datos relacionados serán irrecuperables.", + "delete-queues-text": "Atención, tras la confirmación todas las colas se borrarán y no serán accesibles.", + "search": "Buscar cola", + "add" : "Añadir cola", + "details": "Detalles cola", "topic": "Topic", - "submit-strategy": "Submit Strategy", - "processing-strategy": "Processing Strategy", - "poll-interval": "Poll interval", - "partitions": "Partitions", - "consumer-per-partition": "Consumer per partition", - "consumer-per-partition-hint": "Enable separate consumer(s) per each partition", - "processing-timeout": "Processing timeout, ms", - "batch-size": "Batch size", - "retries": "Retries (0 - unlimited)", - "failure-percentage": "Failure Percentage", - "pause-between-retries": "Pause between retries", - "max-pause-between-retries": "Maximal pause between retries", - "delete": "Delete queue", - "copyId": "Copy queue Id", - "idCopiedMessage": "Queue Id has been copied to clipboard", - "description": "Description", - "description-hint": "This text will be displayed in the Queue description instead of the selected strategy", - "alt-description": "Submit Strategy: {{submitStrategy}}, Processing Strategy: {{processingStrategy}}", + "submit-strategy": "Estrategia de envío", + "processing-strategy": "Estrategia de procesamiento", + "poll-interval": "Intervalo de obtención", + "partitions": "Particiones", + "consumer-per-partition": "Consumidores por partición", + "consumer-per-partition-hint": "Activar consumidores separados para cada partición", + "processing-timeout": "Timeout de procesamiento, ms", + "batch-size": "Tamaño de lote", + "retries": "Reintentos (0 - ilimitados)", + "failure-percentage": "Porcentaje de fallos", + "pause-between-retries": "Pausa entre reintentos", + "max-pause-between-retries": "Pausa máxima entre reintentos", + "delete": "Borrar cola", + "copyId": "Copiar Id de cola", + "idCopiedMessage": "La Id de cola se ha copiado al portapapeles", + "description": "Descripción", + "description-hint": "Este texto se mostrará en la descripción de la cola, en lugar de la estrategia seleccionada", + "alt-description": "Estrategia de envío: {{submitStrategy}}, Estrategia de procesamiento: {{processingStrategy}}", "strategies": { - "sequential-by-originator-label": "Sequential by originator", - "sequential-by-originator-hint": "New message for e.g. device A is not submitted until previous message for device A is acknowledged", - "sequential-by-tenant-label": "Sequential by tenant", - "sequential-by-tenant-hint": "New message for e.g tenant A is not submitted until previous message for tenant A is acknowledged", - "sequential-label": "Sequential", - "sequential-hint": "New message is not submitted until previous message is acknowledged", - "burst-label": "Burst", - "burst-hint": "All messages are submitted to the rule chains in the order they arrive", - "batch-label": "Batch", - "batch-hint": "New batch is not submitted until previous batch is acknowledged", - "skip-all-failures-label": "Skip all failures", - "skip-all-failures-hint": "Ignore all failures", - "skip-all-failures-and-timeouts-label": "Skip all failures and timeouts", - "skip-all-failures-and-timeouts-hint": "Ignore all failures and timeouts", - "retry-all-label": "Retry all", - "retry-all-hint": "Retry all messages from processing pack", - "retry-failed-label": "Retry failed", - "retry-failed-hint": "Retry all failed messages from processing pack", - "retry-timeout-label": "Retry timeout", - "retry-timeout-hint": "Retry all timed-out messages from processing pack", - "retry-failed-and-timeout-label": "Retry failed and timeout", - "retry-failed-and-timeout-hint": "Retry all failed and timed-out messages from processing pack" + "sequential-by-originator-label": "Secuencial por iniciador", + "sequential-by-originator-hint": "El nuevo mensaje por ejemplo dispositivo A, no se enviará hasta que el mensaje anterior del dispositivo A sea admitido/procesado", + "sequential-by-tenant-label": "Secuencial por propietario", + "sequential-by-tenant-hint": "El nuevo mensaje, por ejemplo Propietario A, no se enviará hasta que el mensaje anterior del Propietario A sea admitido/procesado", + "sequential-label": "Secuencial", + "sequential-hint": "El nuevo mensaje no se enviará hasta que el mensaje anterior sea admitido/procesado", + "burst-label": "Ráfaga (Burst)", + "burst-hint": "Todos los mensajes se enviarán hacia las cadenas de reglas en el órden que lleguen", + "batch-label": "Lotes (Batch)", + "batch-hint": "El nuevo lote, no se enviará hasta que el anterior lote sea admitido/procesado", + "skip-all-failures-label": "Omitir todos los fallos", + "skip-all-failures-hint": "Ignorar todos los fallos", + "skip-all-failures-and-timeouts-label": "Omitir todos los fallos y timeouts", + "skip-all-failures-and-timeouts-hint": "Ignorar todos los fallos y timeouts", + "retry-all-label": "Reintentar todos", + "retry-all-hint": "Reintentar todos los mensajes del lote de procesamiento", + "retry-failed-label": "Reintentar fallidos", + "retry-failed-hint": "Reintentar todos los mensajes fallidos del lote de procesamiento", + "retry-timeout-label": "Timeout de reintentos", + "retry-timeout-hint": "Reintentar todos los mensajes que den timeout del lote de procesamiento", + "retry-failed-and-timeout-label": "Reintentar fallidos y timeouts", + "retry-failed-and-timeout-hint": "Reintentar todos los mensajes fallidos y que den timeout del lote de procesamiento" } }, "tenant": { From 1db2fb7d4987976b1a288485e582b01734b48c80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20N=C3=BA=C3=B1ez?= Date: Fri, 3 Jun 2022 19:05:41 +0200 Subject: [PATCH 757/798] Add widgets translation --- .../assets/locale/locale.constant-es_ES.json | 1544 ++++++++--------- 1 file changed, 772 insertions(+), 772 deletions(-) diff --git a/ui-ngx/src/assets/locale/locale.constant-es_ES.json b/ui-ngx/src/assets/locale/locale.constant-es_ES.json index ea6196fa8a..56b85eac62 100644 --- a/ui-ngx/src/assets/locale/locale.constant-es_ES.json +++ b/ui-ngx/src/assets/locale/locale.constant-es_ES.json @@ -3450,145 +3450,145 @@ }, "widgets": { "chart": { - "common-settings": "Common settings", - "enable-stacking-mode": "Enable stacking mode", - "line-shadow-size": "Line shadow size", - "display-smooth-lines": "Display smooth (curved) lines", - "default-bar-width": "Default bar width for non-aggregated data (milliseconds)", - "bar-alignment": "Bar alignment", - "bar-alignment-left": "Left", - "bar-alignment-right": "Right", - "bar-alignment-center": "Center", - "default-font-size": "Default font size", - "default-font-color": "Default font color", - "thresholds-line-width": "Default line width for all thresholds", - "tooltip-settings": "Tooltip settings", - "show-tooltip": "Show tooltip", - "hover-individual-points": "Hover individual points", - "show-cumulative-values": "Show cumulative values in stacking mode", - "hide-zero-false-values": "Hide zero/false values from tooltip", - "tooltip-value-format-function": "Tooltip value format function", - "grid-settings": "Grid settings", - "show-vertical-lines": "Show vertical lines", - "show-horizontal-lines": "Show horizontal lines", - "grid-outline-border-width": "Grid outline/border width (px)", - "primary-color": "Primary color", - "background-color": "Background color", - "ticks-color": "Ticks color", - "xaxis-settings": "X axis settings", - "axis-title": "Axis title", - "xaxis-tick-labels-settings": "X axis tick labels settings", - "show-tick-labels": "Show axis tick labels", - "yaxis-settings": "Y axis settings", - "min-scale-value": "Minimum value on the scale", - "max-scale-value": "Maximum value on the scale", - "yaxis-tick-labels-settings": "Y axis tick labels settings", - "tick-step-size": "Step size between ticks", - "number-of-decimals": "The number of decimals to display", - "ticks-formatter-function": "Ticks formatter function", - "comparison-settings": "Comparison settings", - "enable-comparison": "Enable comparison", - "time-for-comparison": "Comparison period", - "time-for-comparison-previous-interval": "Previous interval (default)", - "time-for-comparison-days": "Day ago", - "time-for-comparison-weeks": "Week ago", - "time-for-comparison-months": "Month ago", - "time-for-comparison-years": "Year ago", - "time-for-comparison-custom-interval": "Custom interval", - "custom-interval-value": "Custom interval value (ms)", - "comparison-x-axis-settings": "Comparison X axis settings", - "axis-position": "Axis position", - "axis-position-top": "Top (default)", - "axis-position-bottom": "Bottom", - "custom-legend-settings": "Custom legend settings", - "enable-custom-legend": "Enable custom legend (this will allow you to use attribute/timeseries values in key labels)", - "key-name": "Key name", - "key-name-required": "Key name is required", - "key-type": "Key type", - "key-type-attribute": "Attribute", + "common-settings": "Ajustes comunes", + "enable-stacking-mode": "Activar modo de apilamiento (stacking)", + "line-shadow-size": "Tamaño de sombra (línea)", + "display-smooth-lines": "Mostrar líneas suaves (curvas)", + "default-bar-width": "Ancho de barra por defecto para datos no agregados (millisegundos)", + "bar-alignment": "Alineación barra", + "bar-alignment-left": "Izquierda", + "bar-alignment-right": "Derecha", + "bar-alignment-center": "Centro", + "default-font-size": "Tamaño de fuente por defecto", + "default-font-color": "Color de fuente por defecto", + "thresholds-line-width": "Ancho de línea por defecto para todos los umbrales", + "tooltip-settings": "Ajustes de sugerencias (tooltip)", + "show-tooltip": "Mostrar sugerencias", + "hover-individual-points": "Hover sobre puntos individuales", + "show-cumulative-values": "Mostrar valores acumulados en modo apilado", + "hide-zero-false-values": "Ocultar valores cero/false en sugerencias", + "tooltip-value-format-function": "Función de formateo de valores en sugerencias (tooltip)", + "grid-settings": "Ajustes de cuadrícula", + "show-vertical-lines": "Mostrar líneas verticales", + "show-horizontal-lines": "Mostrar líneas horizontales", + "grid-outline-border-width": "Ancho de cuadrícula/contorno en px", + "primary-color": "Color primario", + "background-color": "Color de fondo", + "ticks-color": "Color de ticks", + "xaxis-settings": "Ajustes eje X", + "axis-title": "Título de eje", + "xaxis-tick-labels-settings": "Ajustes de etiquetas en ticks eje X", + "show-tick-labels": "Mostrar etiquetas en ticks", + "yaxis-settings": "Ajustes eje Y", + "min-scale-value": "Valor mínimo en la escala", + "max-scale-value": "Valor máximo en la escala", + "yaxis-tick-labels-settings": "Ajustes de etiquetas en ticks eje Y", + "tick-step-size": "Tamaño de paso entre ticks", + "number-of-decimals": "Número de decimales", + "ticks-formatter-function": "Función de formateo de ticks", + "comparison-settings": "Ajustes de comparación", + "enable-comparison": "Activar comparación", + "time-for-comparison": "Período de comparación", + "time-for-comparison-previous-interval": "Intervalo anterior (por defecto)", + "time-for-comparison-days": "Hace un día", + "time-for-comparison-weeks": "Hace una semana", + "time-for-comparison-months": "Hace un mes", + "time-for-comparison-years": "Hace un año", + "time-for-comparison-custom-interval": "Intervalo personalizado", + "custom-interval-value": "Valor de intervalo personalizado (ms)", + "comparison-x-axis-settings": "Ajustes comparación eje X", + "axis-position": "Posición eje", + "axis-position-top": "Superior (por defecto)", + "axis-position-bottom": "Inferior", + "custom-legend-settings": "Ajustes leyenda", + "enable-custom-legend": "Activar leyenda personalizada (habilita poder usar valores de atributos/timeseries en las etiquetas)", + "key-name": "Nombre clave", + "key-name-required": "Nombre clave requerido", + "key-type": "Tipo clave", + "key-type-attribute": "Atributo", "key-type-timeseries": "Timeseries", - "label-keys-list": "Keys list to use in labels", - "no-label-keys": "No keys configured", - "add-label-key": "Add new key", - "line-width": "Line width", + "label-keys-list": "Lista de claves para usar en etiquetas", + "no-label-keys": "No hay claves configuradas", + "add-label-key": "Añadir nueva clave", + "line-width": "Ancho de línea", "color": "Color", - "data-is-hidden-by-default": "Data is hidden by default", - "disable-data-hiding": "Disable data hiding", - "remove-from-legend": "Remove datakey from legend", - "exclude-from-stacking": "Exclude from stacking(available in \"Stacking\" mode)", - "line-settings": "Line settings", - "show-line": "Show line", - "fill-line": "Fill line", - "points-settings": "Points settings", - "show-points": "Show points", - "points-line-width": "Line width of points", - "points-radius": "Radius of points", - "point-shape": "Point shape", - "point-shape-circle": "Circle", - "point-shape-cross": "Cross", - "point-shape-diamond": "Diamond", - "point-shape-square": "Square", - "point-shape-triangle": "Triangle", - "point-shape-custom": "Custom function", - "point-shape-draw-function": "Point shape draw function", - "show-separate-axis": "Show separate axis", - "axis-position-left": "Left", - "axis-position-right": "Right", - "thresholds": "Thresholds", - "no-thresholds": "No thresholds configured", - "add-threshold": "Add new threshold", - "show-values-for-comparison": "Show historical values for comparison", - "comparison-values-label": "Historical values label", - "threshold-settings": "Threshold settings", - "use-as-threshold": "Use key value as threshold", - "threshold-line-width": "Threshold line width", - "threshold-color": "Threshold color", - "common-pie-settings": "Common pie settings", - "radius": "Radius", - "inner-radius": "Inner radius", - "tilt": "Tilt", - "stroke-settings": "Stroke settings", - "width-pixels": "Width (pixels)", - "show-labels": "Show labels", - "animation-settings": "Animation settings", - "animated-pie": "Enable pie animation (experimental)", - "border-settings": "Border settings", - "border-width": "Border width", - "border-color": "Border color", - "legend-settings": "Legend settings", - "display-legend": "Display legend", - "labels-font-color": "Labels font color" + "data-is-hidden-by-default": "Ocultar datos por defecto", + "disable-data-hiding": "Desactivar ocultación de datos", + "remove-from-legend": "Quitar clave de la leyenda", + "exclude-from-stacking": "Excluir del modo apilado(disponible en el modo \"Apilado\")", + "line-settings": "Ajustes de línea", + "show-line": "Mostrar línea", + "fill-line": "Rellenar línea", + "points-settings": "Ajustes de puntos", + "show-points": "Mostrar puntos", + "points-line-width": "Ancho de línea en puntos", + "points-radius": "Radio de los puntos", + "point-shape": "Forma del punto", + "point-shape-circle": "Círculo", + "point-shape-cross": "Cruz", + "point-shape-diamond": "Diamante", + "point-shape-square": "Cuadrado", + "point-shape-triangle": "Triángulo", + "point-shape-custom": "Función personalizada", + "point-shape-draw-function": "Función de forma del punto", + "show-separate-axis": "Mostrar eje separado", + "axis-position-left": "Izquieda", + "axis-position-right": "Derecha", + "thresholds": "Umbrales", + "no-thresholds": "No hay umbrales configurados", + "add-threshold": "Añadir nuevo umbral", + "show-values-for-comparison": "Mostrar valores históricos para su comparación", + "comparison-values-label": "Etiqueta de valores históricos", + "threshold-settings": "Ajustes de umbrales", + "use-as-threshold": "Usar valor de clave como umbral", + "threshold-line-width": "Ancho de línea (para umbral)", + "threshold-color": "Color umbral", + "common-pie-settings": "Ajustes comunes diagrama de sectores", + "radius": "Radio", + "inner-radius": "Radio interior", + "tilt": "Inclinación", + "stroke-settings": "Ajustes de trazo", + "width-pixels": "Ancho (pixels)", + "show-labels": "Mostrar etiquetas", + "animation-settings": "Ajustes de animación", + "animated-pie": "Activar animación (experimental)", + "border-settings": "Ajustes de bordes", + "border-width": "Ancho de borde", + "border-color": "Color de borde", + "legend-settings": "Ajustes de leyenda", + "display-legend": "Mostrar leyenda", + "labels-font-color": "Color de texto en leyenda" }, "dashboard-state": { - "dashboard-state-settings": "Dashboard state settings", - "dashboard-state": "Dashboard state id", - "autofill-state-layout": "Autofill state layout height by default", - "default-margin": "Default widgets margin", - "default-background-color": "Default background color", - "sync-parent-state-params": "Sync state params with parent dashboard" + "dashboard-state-settings": "Ajustes estado de panel", + "dashboard-state": "Id de estado panel", + "autofill-state-layout": "Auto-rellenar altura por defecto (obtenida del estado)", + "default-margin": "Márgen entre widgets por defecto", + "default-background-color": "Color de fondo por defecto", + "sync-parent-state-params": "Sincronizar parámetros de estado con el panel padre" }, "date-range-navigator": { - "date-range-picker-settings": "Date range picker settings", - "hide-date-range-picker": "Hide date range picker", - "picker-one-panel": "Date range picker one panel", - "picker-auto-confirm": "Date range picker auto confirm", - "picker-show-template": "Date range picker show template", - "first-day-of-week": "First day of the week", - "interval-settings": "Interval settings", - "hide-interval": "Hide interval", - "initial-interval": "Initial interval", - "interval-hour": "Hour", - "interval-day": "Day", - "interval-week": "Week", - "interval-two-weeks": "2 weeks", - "interval-month": "Month", - "interval-three-months": "3 months", - "interval-six-months": "6 months", - "step-settings": "Step settings", - "hide-step-size": "Hide step size", - "initial-step-size": "Initial step size", - "hide-labels": "Hide labels", - "use-session-storage": "Use session storage", + "date-range-picker-settings": "Ajustes del selector de fechas", + "hide-date-range-picker": "Ocultar el selector de fechas", + "picker-one-panel": "Selector de fechas para un panel", + "picker-auto-confirm": "Auto-confirmar en selector de fechas", + "picker-show-template": "Mostrar plantilla de selector de fechas", + "first-day-of-week": "Primer día de la semana", + "interval-settings": "Ajustes de intervalo", + "hide-interval": "Ocultar intervalo", + "initial-interval": "Intervalo inicial", + "interval-hour": "Hora", + "interval-day": "Día", + "interval-week": "Semana", + "interval-two-weeks": "2 semanas", + "interval-month": "Mes", + "interval-three-months": "3 meses", + "interval-six-months": "6 meses", + "step-settings": "Ajustes de paso", + "hide-step-size": "Ocultar tamaño de paso", + "initial-step-size": "Tamaño de paso inicial", + "hide-labels": "Ocultar etiquetas", + "use-session-storage": "Usar almacenamiento de sesión", "localizationMap": { "Sun": "Dom.", "Mon": "Lun.", @@ -3646,170 +3646,170 @@ } }, "entities-hierarchy": { - "hierarchy-data-settings": "Hierarchy data settings", - "relations-query-function": "Node relations query function", - "has-children-function": "Node has children function", - "node-state-settings": "Node state settings", - "node-opened-function": "Default node opened function", - "node-disabled-function": "Node disabled function", - "display-settings": "Display settings", - "node-icon-function": "Node icon function", - "node-text-function": "Node text function", - "sort-settings": "Sort settings", - "nodes-sort-function": "Nodes sort function" + "hierarchy-data-settings": "Ajustes de datos de jerarquía", + "relations-query-function": "Función de obtención de relaciones", + "has-children-function": "El nodo tiene una función hija", + "node-state-settings": "Ajustes estado nodo", + "node-opened-function": "Función por defecto al abrir nodo", + "node-disabled-function": "Función con nodo desactivado", + "display-settings": "Mostrar ajustes", + "node-icon-function": "Función de icono nodo", + "node-text-function": "Función de texto nodo", + "sort-settings": "Ajustes de ordenación", + "nodes-sort-function": "Función de ordenación" }, "edge": { - "display-default-title": "Display default title" + "display-default-title": "Mostrar título por defecto" }, "gateway": { - "general-settings": "General settings", - "widget-title": "Widget title", - "default-archive-file-name": "Default archive file name", - "device-type-for-new-gateway": "Device type for new gateway", - "messages-settings": "Messages settings", - "save-config-success-message": "Text message about successfully saved gateway configuration", - "device-name-exists-message": "Text message when device with entered name is already exists", - "gateway-title": "Gateway form", - "read-only": "Read only", - "events-title": "Gateway events form title", - "events-filter": "Events filter", - "event-key-contains": "Event key contains..." + "general-settings": "Ajustes generales", + "widget-title": "Título del widget", + "default-archive-file-name": "Nombre del fichero por defecto", + "device-type-for-new-gateway": "Tipo de dispositivo para nuevo gateway", + "messages-settings": "Ajustes de mensajes", + "save-config-success-message": "Mensaje de éxito grabando configuración", + "device-name-exists-message": "Mensaje de texto cuando el nombre del dispositivo ya exista", + "gateway-title": "Formulario Gateway", + "read-only": "Solo lectura", + "events-title": "Título del formulario de eventos", + "events-filter": "Filtro de eventos", + "event-key-contains": "La clave de evento contiene..." }, "gauge": { - "default-color": "Default color", - "radial-gauge-settings": "Radial gauge settings", - "ticks-settings": "Ticks settings", - "min-value": "Minimum value", - "max-value": "Maximum value", - "start-ticks-angle": "Start ticks angle", - "ticks-angle": "Ticks angle", - "major-ticks-count": "Major ticks count", - "major-ticks-color": "Major ticks color", - "minor-ticks-count": "Minor ticks count", - "minor-ticks-color": "Minor ticks color", - "tick-numbers-font": "Tick numbers font", - "unit-title-settings": "Unit title settings", - "show-unit-title": "Show unit title", - "unit-title": "Unit title", - "title-font": "Title text font", - "units-settings": "Units settings", - "units-font": "Units text font", - "value-box-settings": "Value box settings", - "show-value-box": "Show value box", - "value-int": "Digits count for integer part of value", - "value-font": "Value text font", - "value-box-rect-stroke-color": "Value box rectangle stroke color", - "value-box-rect-stroke-color-end": "Value box rectangle stroke color - end gradient", - "value-box-background-color": "Value box background color", - "value-box-shadow-color": "Value box shadow color", - "plate-settings": "Plate settings", - "show-plate-border": "Show plate border", - "plate-color": "Plate color", - "needle-settings": "Needle settings", - "needle-circle-size": "Needle circle size", - "needle-color": "Needle color", - "needle-color-end": "Needle color - end gradient", - "needle-color-shadow-up": "Upper half of the needle shadow color", - "needle-color-shadow-down": "Drop shadow needle color", - "highlights-settings": "Highlights settings", - "highlights-width": "Highlights width", - "highlights": "Highlights", - "highlight-from": "From", - "highlight-to": "To", + "default-color": "Color por defecto", + "radial-gauge-settings": "Ajustes de indicador radial", + "ticks-settings": "Ajustes de ticks", + "min-value": "Valor mínimo", + "max-value": "Valor máximo", + "start-ticks-angle": "Ángulo de inicio ticks", + "ticks-angle": "Ángulo Ticks", + "major-ticks-count": "Nº de ticks principales", + "major-ticks-color": "Color Nº de ticks principales", + "minor-ticks-count": "Nº de ticks secundarios", + "minor-ticks-color": "Color Nº de ticks secundarios", + "tick-numbers-font": "Fuente del tick", + "unit-title-settings": "Ajustes de unidad (título)", + "show-unit-title": "Mostrar unidades (título)", + "unit-title": "Título de unidad", + "title-font": "Fuente de texto del título", + "units-settings": "Ajustes de unidades", + "units-font": "Fuente de texto de las unidades", + "value-box-settings": "Ajustes de valor", + "show-value-box": "Mostrar valor", + "value-int": "Nº de dígitos para la parte entera del valor", + "value-font": "Fuente de texto del valor", + "value-box-rect-stroke-color": "Color del trazo del rectángulo (valor)", + "value-box-rect-stroke-color-end": "Color del trazo del rectángulo (valor) - gradiente final", + "value-box-background-color": "Color de fondo del valor", + "value-box-shadow-color": "Color de sombra del valor", + "plate-settings": "Ajustes de la placa", + "show-plate-border": "Mostrar borde de la placa", + "plate-color": "Color de la placa", + "needle-settings": "Ajustes de la aguja", + "needle-circle-size": "Tamaño del círculo de la aguja", + "needle-color": "Color de la aguja", + "needle-color-end": "Color de la aguja - gradiente final", + "needle-color-shadow-up": "Color de sombreado de la mitad superior de la aguja", + "needle-color-shadow-down": "Color de sombra de la aguja", + "highlights-settings": "Ajustes de resalto", + "highlights-width": "Ancho del resalto", + "highlights": "Resaltos", + "highlight-from": "De", + "highlight-to": "A", "highlight-color": "Color", - "no-highlights": "No highlights configured", - "add-highlight": "Add highlight", - "animation-settings": "Animation settings", - "enable-animation": "Enable animation", - "animation-duration": "Animation duration", - "animation-rule": "Animation rule", - "animation-linear": "Linear", + "no-highlights": "No hay resaltos configurados", + "add-highlight": "Añadir resalto", + "animation-settings": "Ajustes de animación", + "enable-animation": "Activar animación", + "animation-duration": "Duración de animación", + "animation-rule": "Regla de animación", + "animation-linear": "Lineal", "animation-quad": "Quad", "animation-quint": "Quint", - "animation-cycle": "Cycle", - "animation-bounce": "Bounce", + "animation-cycle": "Ciclo (cycle)", + "animation-bounce": "Rebote (bounce)", "animation-elastic": "Elastic", "animation-dequad": "Dequad", "animation-dequint": "Dequint", "animation-decycle": "Decycle", "animation-debounce": "Debounce", "animation-delastic": "Delastic", - "linear-gauge-settings": "Linear gauge settings", - "bar-stroke-width": "Bar stroke width", - "bar-stroke-color": "Bar stroke color", - "bar-background-color": "Gauge bar background color", - "bar-background-color-end": "Bar background color - end gradient", - "progress-bar-color": "Progress bar color", - "progress-bar-color-end": "Progress bar color - end gradient", - "major-ticks-names": "Major ticks names", - "show-stroke-ticks": "Show ticks stroke", - "major-ticks-font": "Major ticks font", - "border-color": "Border color", - "border-width": "Border width", - "needle-circle-color": "Needle circle color", - "animation-target": "Animation target", - "animation-target-needle": "Needle", - "animation-target-plate": "Plate", - "common-settings": "Common gauge settings", - "gauge-type": "Gauge type", - "gauge-type-arc": "Arc", + "linear-gauge-settings": "Ajustes de indicador lineal", + "bar-stroke-width": "Ancho del trazo", + "bar-stroke-color": "Color del trazo", + "bar-background-color": "Color de fondo del indicador", + "bar-background-color-end": "Color de fondo del indicador - gradiente final", + "progress-bar-color": "Color de la barra de progreso", + "progress-bar-color-end": "Color de la barra de progreso - gradiente final", + "major-ticks-names": "Nombre de ticks principales", + "show-stroke-ticks": "Mostrar trazo de ticks", + "major-ticks-font": "Fuente de ticks principales", + "border-color": "Color del borde", + "border-width": "Ancho del borde", + "needle-circle-color": "Color del círculo de la aguja", + "animation-target": "Destino de la animación", + "animation-target-needle": "Aguja", + "animation-target-plate": "Placa", + "common-settings": "Ajustes comunes del indicador", + "gauge-type": "Tipo de indicador", + "gauge-type-arc": "Arco", "gauge-type-donut": "Donut", - "gauge-type-horizontal-bar": "Horizontal bar", - "gauge-type-vertical-bar": "Vertical bar", - "donut-start-angle": "Angle to start from", - "bar-settings": "Gauge bar settings", - "relative-bar-width": "Relative bar width", - "neon-glow-brightness": "Neon glow effect brightness, (0-100), 0 - disable effect", - "stripes-thickness": "Thickness of the stripes, 0 - no stripes", - "rounded-line-cap": "Display rounded line cap", - "bar-color-settings": "Bar color settings", - "use-precise-level-color-values": "Use precise color levels", - "bar-colors": "Bar colors, from lower to upper", + "gauge-type-horizontal-bar": "Barra horizontal", + "gauge-type-vertical-bar": "Barra vertical", + "donut-start-angle": "Ángulo de inicio", + "bar-settings": "Ajustes de barra indicadora", + "relative-bar-width": "Ancho relativo", + "neon-glow-brightness": "Efecto brillo de neon, (0-100), 0 - desactiva efecto", + "stripes-thickness": "Grosor de las rayas, 0 - sin rayas", + "rounded-line-cap": "Mostrar tapa de línea redondeada", + "bar-color-settings": "Ajustes de color de barra", + "use-precise-level-color-values": "Usar niveles precisos de color", + "bar-colors": "Colores de la barra, del más bajo al más alto", "color": "Color", - "no-bar-colors": "No bar colors configured", - "add-bar-color": "Add bar color", - "from": "From", - "to": "To", - "fixed-level-colors": "Bar colors using boundary values", - "gauge-title-settings": "Gauge title settings", - "show-gauge-title": "Show gauge title", - "gauge-title": "Gauge title", - "gauge-title-font": "Gauge title font", - "unit-title-and-timestamp-settings": "Unit title and timestamp settings", - "show-timestamp": "Show value timestamp", - "timestamp-format": "Timestamp format", - "label-font": "Font of label showing under value", - "value-settings": "Value settings", - "show-value": "Show value text", - "min-max-settings": "Minimum/maximum labels settings", - "show-min-max": "Show min and max values", - "min-max-font": "Font of minimum and maximum labels", - "show-ticks": "Show ticks", - "tick-width": "Tick width", - "tick-color": "Tick color", - "tick-values": "Tick values", - "no-tick-values": "No tick values configured", - "add-tick-value": "Add tick value" + "no-bar-colors": "No hay colores configurados", + "add-bar-color": "Añadir color", + "from": "De", + "to": "A", + "fixed-level-colors": "Colores de barra usando valores límite", + "gauge-title-settings": "Ajustes de título del indicador", + "show-gauge-title": "Mostrar título de indicador", + "gauge-title": "Título de indicador", + "gauge-title-font": "Fuente del título del indicador", + "unit-title-and-timestamp-settings": "Ajustes del título de unidades y timestamp", + "show-timestamp": "Mostrar valor timestamp", + "timestamp-format": "Formato timestamp", + "label-font": "Fuente de la etiqueta que se muestra bajo el valor", + "value-settings": "Ajustes del valor", + "show-value": "Mostrar texto del valor", + "min-max-settings": "Etiqueta mínimo/máximo", + "show-min-max": "Mostrar valores mínimos y máximos", + "min-max-font": "Fuente de los valores mínimo y máximo", + "show-ticks": "Mostrar ticks", + "tick-width": "Ancho de tick", + "tick-color": "Color de tick", + "tick-values": "Valores de tick", + "no-tick-values": "No hay valores configurados", + "add-tick-value": "Añadir valor de tick" }, "gpio": { "pin": "Pin", - "label": "Label", - "row": "Row", - "column": "Column", + "label": "Etiqueta", + "row": "Fila", + "column": "Columna", "color": "Color", - "panel-settings": "Panel settings", - "background-color": "Background color", - "gpio-switches": "GPIO switches", - "no-gpio-switches": "No GPIO switches configured", - "add-gpio-switch": "Add GPIO switch", - "gpio-status-request": "GPIO status request", - "method-name": "Method name", - "method-body": "Method body", - "gpio-status-change-request": "GPIO status change request", - "parse-gpio-status-function": "Parse gpio status function", - "gpio-leds": "GPIO leds", - "no-gpio-leds": "No GPIO leds configured", - "add-gpio-led": "Add GPIO led" + "panel-settings": "Ajustes de panel", + "background-color": "Color de fondo", + "gpio-switches": "switches GPIO", + "no-gpio-switches": "No hay switches GPIO configurados", + "add-gpio-switch": "Añadir switch GPIO", + "gpio-status-request": "Solicitud estado GPIO", + "method-name": "Nombre del método RPC", + "method-body": "Cuerpo del método RPC", + "gpio-status-change-request": "Solicitud de cambio de estado GPIO", + "parse-gpio-status-function": "Función de parseado de estado GPIO", + "gpio-leds": "Leds GPIO", + "no-gpio-leds": "No hay Leds GPIO configurados", + "add-gpio-led": "Añadir led GPIO" }, "html-card": { "html": "HTML", @@ -3860,569 +3860,569 @@ "update-attribute": "Actualizar atributo", "update-timeseries": "Actualizar series de tiempo", "value": "Valor", - "general-settings": "General settings", - "widget-title": "Widget title", - "claim-button-label": "Claiming button label", - "show-secret-key-field": "Show 'Secret key' input field", - "labels-settings": "Labels settings", - "show-labels": "Show labels", - "device-name-label": "Label for device name input field", - "secret-key-label": "Label for secret key input field", - "messages-settings": "Messages settings", - "claim-device-success-message": "Text message of successful device claiming", - "claim-device-not-found-message": "Text message when device not found", - "claim-device-failed-message": "Text message of failed device claiming", - "claim-device-name-required-message": "'Device name required' error message", - "claim-device-secret-key-required-message": "'Secret key required' error message", - "show-label": "Show label", - "label": "Label", - "required": "Required", - "required-error-message": "'Required' error message", - "show-result-message": "Show result message", - "integer-field-settings": "Integer field settings", - "min-value": "Min value", - "max-value": "Max value", - "double-field-settings": "Double field settings", - "text-field-settings": "Text field settings", - "min-length": "Min length", - "max-length": "Max length", - "checkbox-settings": "Checkbox settings", - "true-label": "Checked label", - "false-label": "Unchecked label", - "image-input-settings": "Image input settings", - "display-preview": "Display preview", - "display-clear-button": "Display clear button", - "display-apply-button": "Display apply button", - "display-discard-button": "Display discard button", - "datetime-field-settings": "Date/time field settings", - "display-time-input": "Display time input", - "latitude-key-name": "Latitude key name", - "longitude-key-name": "Longitude key name", - "show-get-location-button": "Show button 'Get current location'", - "use-high-accuracy": "Use high accuracy", - "location-fields-settings": "Location fields settings", - "latitude-label": "Label for latitude", - "longitude-label": "Label for longitude", - "input-fields-alignment": "Input fields alignment", - "input-fields-alignment-column": "Column (default)", - "input-fields-alignment-row": "Row", - "latitude-field-required": "Latitude field required", - "longitude-field-required": "Longitude field required", - "attribute-settings": "Attribute settings", - "widget-mode": "Widget mode", - "widget-mode-update-attribute": "Update attribute", - "widget-mode-update-timeseries": "Update timeseries", - "attribute-scope": "Attribute scope", - "attribute-scope-server": "Server attribute", - "attribute-scope-shared": "Shared attribute", - "value-required": "Value required", - "image-settings": "Image settings", - "image-format": "Image format", + "general-settings": "Ajustes Generales", + "widget-title": "Título del widget", + "claim-button-label": "Etiqueta del botón de reclamar", + "show-secret-key-field": "Mostrar el campo 'Clave Secreta'", + "labels-settings": "Ajustes de etiquetas", + "show-labels": "Mostrar etiquetas", + "device-name-label": "Etiqueta para el campo de entrada 'Nombre de Dispositivo'", + "secret-key-label": "Etiqueta para el campo de entrara 'Clave Secreta'", + "messages-settings": "Ajustes de mensajes", + "claim-device-success-message": "Mensaje a mostrar cuando el dispositivo se haya reclamado ok", + "claim-device-not-found-message": "Mensaje a mostrar cuando el dispositivo no se encuentre", + "claim-device-failed-message": "Mensaje a mostrar cuando ocurra un error reclamando el dispositivo", + "claim-device-name-required-message": "Mensaje de error a mostrar con 'Nombre de dispositivo requerido'", + "claim-device-secret-key-required-message": "Mensaje de error a mostrar con 'Clave Secreta requerida'", + "show-label": "Mostrar etiqueta", + "label": "Etiqueta", + "required": "Requerido", + "required-error-message": "Error a mostrar con campo 'Requerido'", + "show-result-message": "Mensaje para mostrar resultado", + "integer-field-settings": "Ajustes de campos tipo entero", + "min-value": "Valor Mínimo", + "max-value": "Valor Máximo", + "double-field-settings": "Ajustes de campos tipo double", + "text-field-settings": "Ajustes de campos tipo texto", + "min-length": "Longitud mínima", + "max-length": "Longitud máxima", + "checkbox-settings": "Ajustes de campos tipo checkbox", + "true-label": "Etiqueta checked", + "false-label": "Etiqueta unchecked", + "image-input-settings": "Ajustes de campos tipo entrada de imagen", + "display-preview": "Mostrar previsualización", + "display-clear-button": "Mostrar botón de borrar", + "display-apply-button": "Mostrar botón de aplicar", + "display-discard-button": "Mostrar botón de descartar", + "datetime-field-settings": "Ajustes de campos tipo Fecha/Hora", + "display-time-input": "Mostrar entrada de hora", + "latitude-key-name": "Nombre clave latitud", + "longitude-key-name": "Nombre clave longitud", + "show-get-location-button": "Mostrar botón 'Obtener localización actual'", + "use-high-accuracy": "Usar alta precisión", + "location-fields-settings": "Ajustes de campos de localización", + "latitude-label": "Etiqueta para latitud", + "longitude-label": "Etiqueta para longitud", + "input-fields-alignment": "Alineado de campos de entrada", + "input-fields-alignment-column": "Columna (por defecto)", + "input-fields-alignment-row": "Fila", + "latitude-field-required": "Campo latitud requerido", + "longitude-field-required": "Campo longitud requerido", + "attribute-settings": "Ajustes de atributos", + "widget-mode": "Modo del widget", + "widget-mode-update-attribute": "Actualizar atributo", + "widget-mode-update-timeseries": "Actualizar timeseries", + "attribute-scope": "Alcance de atributos", + "attribute-scope-server": "Atributos de servidor", + "attribute-scope-shared": "Atributos compartidos", + "value-required": "Valor requerido", + "image-settings": "Ajustes de imagen", + "image-format": "Formato de imagen", "image-format-jpeg": "JPEG", "image-format-png": "PNG", "image-format-webp": "WEBP", - "image-quality": "Image quality that use lossy compression such as jpeg and webp", - "max-image-width": "Maximum image width", - "max-image-height": "Maximum image height", - "action-buttons": "Action buttons", - "show-action-buttons": "Show action buttons", - "update-all-values": "Update all values, not only modified", - "save-button-label": "'SAVE' button label", - "reset-button-label": "'UNDO' button label", - "group-settings": "Group settings", - "show-group-title": "Show title for group of fields, related to different entities", - "group-title": "Group title", - "fields-alignment": "Fields alignment", - "fields-alignment-row": "Row (default)", - "fields-alignment-column": "Column", - "fields-in-row": "Number of fields in the row", - "option-value": "Value (write 'null' for create empty option)", - "option-label": "Label", - "hide-input-field": "Hide input field", - "datakey-type": "Datakey type", - "datakey-type-server": "Server attribute (default)", - "datakey-type-shared": "Shared attribute", + "image-quality": "Calidad de imagen que usa compresión con pérdida como jpeg y webp", + "max-image-width": "Máximo ancho de imagen", + "max-image-height": "Máximo alto de imagen", + "action-buttons": "Botones de acción", + "show-action-buttons": "Mostrar botones de acción", + "update-all-values": "Actualizar todos los valores, no sólo los modificados", + "save-button-label": "Etiqueta de botón 'GRABAR'", + "reset-button-label": "Etiqueta de botón 'DESHACER'", + "group-settings": "Ajustes de grupo", + "show-group-title": "Mostrar título para el grupo de campos relacionados con diferentes entidades", + "group-title": "Título del grupo", + "fields-alignment": "Alineado de campos", + "fields-alignment-row": "Fila (por defecto)", + "fields-alignment-column": "Columna", + "fields-in-row": "Número de campos en la fila", + "option-value": "Valor (escribe 'null' para crear una opción vacía)", + "option-label": "Etiqueta", + "hide-input-field": "Ocultar campo de entrada", + "datakey-type": "Tipo de clave de datos", + "datakey-type-server": "Atributo de servidor (por defecto)", + "datakey-type-shared": "Atributo compartido", "datakey-type-timeseries": "Timeseries", - "datakey-value-type": "Datakey value type", + "datakey-value-type": "Tipo de valor de la clave de datos", "datakey-value-type-string": "String", "datakey-value-type-double": "Double", - "datakey-value-type-integer": "Integer", + "datakey-value-type-integer": "Entero", "datakey-value-type-boolean-checkbox": "Boolean (Checkbox)", "datakey-value-type-boolean-switch": "Boolean (Switch)", - "datakey-value-type-date-time": "Date & Time", - "datakey-value-type-date": "Date", - "datakey-value-type-time": "Time", - "datakey-value-type-select": "Select", - "value-is-required": "Value is required", - "ability-to-edit-attribute": "Ability to edit attribute", - "ability-to-edit-attribute-editable": "Editable (default)", - "ability-to-edit-attribute-disabled": "Disabled", - "ability-to-edit-attribute-readonly": "Read-only", - "disable-on-datakey-name": "Disable on false value of another datakey (specify datakey name)", - "slide-toggle-settings": "Slide toggle settings", - "slide-toggle-label-position": "Slide toggle label position", - "slide-toggle-label-position-after": "After", - "slide-toggle-label-position-before": "Before", - "select-options": "Select options", - "no-select-options": "No select options configured", - "add-select-option": "Add select option", - "numeric-field-settings": "Numeric field settings", - "step-interval": "Step interval between values", - "error-messages": "Error messages", - "min-value-error-message": "'Min value' error message", - "max-value-error-message": "'Max value' error message", - "invalid-date-error-message": "'Invalid date' error message", - "icon-settings": "Icon settings", - "use-custom-icon": "Use custom icon", - "input-cell-icon": "Icon to show before input cell", - "value-conversion-settings": "Value conversion settings", - "get-value-settings": "Get value settings", - "use-get-value-function": "Use getValue function", - "get-value-function": "getValue function", - "set-value-settings": "Set value settings", - "use-set-value-function": "Use setValue function", - "set-value-function": "setValue function" + "datakey-value-type-date-time": "Fecha & Hora", + "datakey-value-type-date": "Fecha", + "datakey-value-type-time": "Hora", + "datakey-value-type-select": "Selección", + "value-is-required": "Valor requerido", + "ability-to-edit-attribute": "Posibilidad de editar atributo", + "ability-to-edit-attribute-editable": "Editable (por defecto)", + "ability-to-edit-attribute-disabled": "Desactivado", + "ability-to-edit-attribute-readonly": "Sólo lectura", + "disable-on-datakey-name": "Desactivar cuando otra clave de datos sea false (especificar nombre de clave de datos)", + "slide-toggle-settings": "Ajustes de deslizador", + "slide-toggle-label-position": "Posición de etiqueta", + "slide-toggle-label-position-after": "Después", + "slide-toggle-label-position-before": "Antes", + "select-options": "Seleccionar opciones", + "no-select-options": "No hay opciones configuradas", + "add-select-option": "Añadir opción", + "numeric-field-settings": "Ajustes de campos numéricos", + "step-interval": "Pasos entre valores (intervalo)", + "error-messages": "Mensajes de erro", + "min-value-error-message": "Mensaje de error de 'Valor Mínimo'", + "max-value-error-message": "Mensaje de error de 'Valor Máximo'", + "invalid-date-error-message": "Mensaje de erro de 'Fecha Inválida'", + "icon-settings": "Ajustes de iconos", + "use-custom-icon": "Usar icono personalizado", + "input-cell-icon": "Icono a mostrar antes del campo de entrada", + "value-conversion-settings": "Ajustes de conversión de valor", + "get-value-settings": "Ajustes de obtención de valores", + "use-get-value-function": "Usar función getValue", + "get-value-function": "Función getValue", + "set-value-settings": "Ajustes de establecimiento de valores", + "use-set-value-function": "Usar función setValue", + "set-value-function": "Función setValue" }, - "invalid-qr-code-text": "Invalid input text for QR code. Input should have a string type", + "invalid-qr-code-text": "Texto de entrara inválido para el código QR. La entrada debe ser de tipo string", "qr-code": { - "use-qr-code-text-function": "Use QR code text function", - "qr-code-text-pattern": "QR code text pattern (for ex. '${entityName} | ${keyName} - some text.')", - "qr-code-text-pattern-required": "QR code text pattern is required.", - "qr-code-text-function": "QR code text function" + "use-qr-code-text-function": "Usar función de texto QR", + "qr-code-text-pattern": "Patrón del código QR (por ej. '${entityName} | ${keyName} - texto adicional.')", + "qr-code-text-pattern-required": "Se requiere patrón del código QR.", + "qr-code-text-function": "Función del código QR" }, "label-widget": { - "label-pattern": "Pattern", - "label-pattern-hint": "Hint: for ex. 'Text ${keyName} units.' or ${#<key index>} units'", - "label-pattern-required": "Pattern is required", - "label-position": "Position (Percentage relative to background)", + "label-pattern": "Patrón", + "label-pattern-hint": "Ayuda: por ej. 'Texto ${keyName} unidades.' o ${#<key index>} unidades'", + "label-pattern-required": "Se requiere patrón", + "label-position": "Posición (Porcentaje relativo al fondo)", "x-pos": "X", "y-pos": "Y", - "background-color": "Background color", - "font-settings": "Font settings", - "background-image": "Background image", - "labels": "Labels", - "no-labels": "No labels configured", - "add-label": "Add label" + "background-color": "Color de fondo", + "font-settings": "Ajustes de fuente", + "background-image": "Imagen de fondo", + "labels": "Etiquetas", + "no-labels": "No hay etiquetas configuradas", + "add-label": "Añadir etiqueta" }, "navigation": { - "title": "Title", - "navigation-path": "Navigation path", - "filter-type": "Filter type", - "filter-type-all": "All items", - "filter-type-include": "Include items", - "filter-type-exclude": "Exclude items", - "items": "Items", - "enter-urls-to-filter": "Enter urls to filter..." + "title": "Título", + "navigation-path": "Ruta de navegación", + "filter-type": "Tipo de filtro", + "filter-type-all": "Todos los objetos", + "filter-type-include": "Incluir objetos", + "filter-type-exclude": "Excluir objetos", + "items": "Objetos", + "enter-urls-to-filter": "Especificar URLs a filtrar..." }, "persistent-table": { - "rpc-id": "RPC ID", - "message-type": "Message type", - "method": "Method", - "params": "Params", - "created-time": "Created time", - "expiration-time": "Expiration time", - "retries": "Retries", - "status": "Status", - "filter": "Filter", - "refresh": "Refresh", - "add": "Add RPC request", - "details": "Details", - "delete": "Delete", - "delete-request-title": "Delete Persistent RPC request", - "delete-request-text": "Are you sure you want to delete request?", - "details-title": "Details RPC ID: ", - "additional-info": "Additional info", - "response": "Response", - "any-status": "Any status", - "rpc-status-list": "RPC status list", - "no-request-prompt": "No request to display", - "send-request": "Send request", - "add-title": "Create Persistent RPC request", - "method-error": "Method is required.", - "timeout-error": "Min timeout value is 5000 (5 seconds).", - "white-space-error": "White space is not allowed.", + "rpc-id": "ID RPC", + "message-type": "Tipo de mensaje", + "method": "Método", + "params": "Parémetros", + "created-time": "Hora de creación", + "expiration-time": "Hora de expiración", + "retries": "Reintentos", + "status": "Estado", + "filter": "Filtro", + "refresh": "Actualizar", + "add": "Añadir petición RPC", + "details": "Detalles", + "delete": "Borrar", + "delete-request-title": "Borrar petición RPC persistente", + "delete-request-text": "Estas seguro de borrar la petición?", + "details-title": "Detalles ID RPC: ", + "additional-info": "Información adicional", + "response": "Respuesta", + "any-status": "Cualquier estado", + "rpc-status-list": "Lista de estados RPC", + "no-request-prompt": "No hay peticiones a mostrar", + "send-request": "Enviar petición", + "add-title": "Crear una petición RPC persistente", + "method-error": "Se requiere método.", + "timeout-error": "El valor mínimo de timeout es 5000 (5 segundos).", + "white-space-error": "No se permiten espacios en blanco.", "rpc-status": { - "QUEUED": "QUEUED", - "SENT": "SENT", - "DELIVERED": "DELIVERED", - "SUCCESSFUL": "SUCCESSFUL", + "QUEUED": "EN COLA", + "SENT": "ENVIADO", + "DELIVERED": "ENTREGADO", + "SUCCESSFUL": "CORRECTO", "TIMEOUT": "TIMEOUT", - "EXPIRED": "EXPIRED", - "FAILED": "FAILED" + "EXPIRED": "EXPIRADO", + "FAILED": "FALLO" }, - "rpc-search-status-all": "ALL", + "rpc-search-status-all": "TODOS", "message-types": { "false": "Two-way", "true": "One-way" }, - "general-settings": "General settings", - "enable-filter": "Enable filter", - "enable-sticky-header": "Display header while scrolling", - "enable-sticky-action": "Display actions column while scrolling", - "display-request-details": "Display request details", - "allow-send-request": "Allow send RPC request", - "allow-delete-request": "Allow delete request", - "columns-settings": "Columns settings", - "display-columns": "Columns to display", - "column": "Column", - "no-columns-found": "No columns found", - "no-columns-matching": "'{{column}}' not found." + "general-settings": "Ajustes Generales", + "enable-filter": "Activar filtro", + "enable-sticky-header": "Mostrar encabezado mientras se hace scroll", + "enable-sticky-action": "Mostrar columna de acciones mientras se hace scroll", + "display-request-details": "Mostrar detalles de petición", + "allow-send-request": "Permitir enviar petición RPC", + "allow-delete-request": "Permitir borrar petición", + "columns-settings": "Ajustes de columnas", + "display-columns": "Columnas a mostrar", + "column": "Columna", + "no-columns-found": "No se encontraron columnas", + "no-columns-matching": "'{{column}}' no encontrada." }, "rpc": { - "value-settings": "Value settings", - "initial-value": "Initial value", - "retrieve-value-settings": "Retrieve on/off value settings", - "retrieve-value-method": "Retrieve value using method", - "retrieve-value-method-none": "Don't retrieve", - "retrieve-value-method-rpc": "Call RPC get value method", - "retrieve-value-method-attribute": "Subscribe for attribute", - "retrieve-value-method-timeseries": "Subscribe for timeseries", - "attribute-value-key": "Attribute key", - "timeseries-value-key": "Timeseries key", - "get-value-method": "RPC get value method", - "parse-value-function": "Parse value function", - "update-value-settings": "Update value settings", - "set-value-method": "RPC set value method", - "convert-value-function": "Convert value function", - "rpc-settings": "RPC settings", - "request-timeout": "RPC request timeout (ms)", - "persistent-rpc-settings": "Persistent RPC settings", - "request-persistent": "RPC request persistent", - "persistent-polling-interval": "Polling interval (ms) to get persistent RPC command response", - "common-settings": "Common settings", - "switch-title": "Switch title", - "show-on-off-labels": "Show on/off labels", - "slide-toggle-label": "Slide toggle label", - "label-position": "Label position", - "label-position-before": "Before", - "label-position-after": "After", - "slider-color": "Slider color", - "slider-color-primary": "Primary", - "slider-color-accent": "Accent", - "slider-color-warn": "Warn", - "button-style": "Button style", - "button-raised": "Raised button", - "button-primary": "Primary color", - "button-background-color": "Button background color", - "button-text-color": "Button text color", - "widget-title": "Widget title", - "button-label": "Button label", - "device-attribute-scope": "Device attribute scope", - "server-attribute": "Server attribute", - "shared-attribute": "Shared attribute", - "device-attribute-parameters": "Device attribute parameters", - "is-one-way-command": "Is one way command", - "rpc-method": "RPC method", - "rpc-method-params": "RPC method params", - "show-rpc-error": "Show RPC command execution error", - "led-title": "LED title", - "led-color": "LED color", - "check-status-settings": "Check status settings", - "perform-rpc-status-check": "Perform RPC device status check", - "retrieve-led-status-value-method": "Retrieve led status value using method", - "led-status-value-attribute": "Device attribute containing led status value", - "led-status-value-timeseries": "Device timeseries containing led status value", - "check-status-method": "RPC check device status method", - "parse-led-status-value-function": "Parse led status value function", - "knob-title": "Knob title", - "min-value": "Minimum value", - "max-value": "Maximum value" + "value-settings": "Ajustes de valor", + "initial-value": "Valor inicial", + "retrieve-value-settings": "Obtener ajustes valores on/off", + "retrieve-value-method": "Obtener valor usando método", + "retrieve-value-method-none": "No obtener", + "retrieve-value-method-rpc": "Método para llamada RPC de obtención de valor", + "retrieve-value-method-attribute": "Suscribir por atributo", + "retrieve-value-method-timeseries": "Suscribir por timeseries", + "attribute-value-key": "Clave de atributo", + "timeseries-value-key": "Clave de timeseries", + "get-value-method": "Metodo para obtener valor vía RPC", + "parse-value-function": "Función de parseo de valor", + "update-value-settings": "Ajustes de actualización de valor", + "set-value-method": "Método para establecer valor vía RPC", + "convert-value-function": "Función de conversión", + "rpc-settings": "Ajustes RPC", + "request-timeout": "Timeout de petición RPC (ms)", + "persistent-rpc-settings": "Ajustes de RPC persistente", + "request-persistent": "Petición RPC persistente", + "persistent-polling-interval": "Intervalo de sondeo (ms) para obtener respuesta del comando RPC persistente", + "common-settings": "Ajustes comunes", + "switch-title": "Título del switch", + "show-on-off-labels": "Mostrar etiquetas on/off", + "slide-toggle-label": "Etiqueta de interruptor deslizante", + "label-position": "Posición de etiqueta", + "label-position-before": "Antes", + "label-position-after": "Después", + "slider-color": "Color del deslizador", + "slider-color-primary": "Primario", + "slider-color-accent": "Acento", + "slider-color-warn": "Aviso", + "button-style": "Estilo de botón", + "button-raised": "Botón levantado", + "button-primary": "Color primario", + "button-background-color": "Color de fondo", + "button-text-color": "Color de texto", + "widget-title": "Título del widget", + "button-label": "Etiqueta del botón", + "device-attribute-scope": "Alcance de atributos del dispositivo", + "server-attribute": "Atributos de servidor", + "shared-attribute": "Atributos compartidos", + "device-attribute-parameters": "Parámetros de atributos del dispositivo", + "is-one-way-command": "Es un comando de una vía (one way)", + "rpc-method": "Método RPC", + "rpc-method-params": "Parámetros método RPC", + "show-rpc-error": "Mostrar errores de ejecución de RPC", + "led-title": "Título LED", + "led-color": "Color LED", + "check-status-settings": "Ajustes de comprobación de estado", + "perform-rpc-status-check": "Realizar comprobación del dispositivo RPC", + "retrieve-led-status-value-method": "Obtener estado del led usando método", + "led-status-value-attribute": "Atributo del dispositivo que contiene el valor del estado del led", + "led-status-value-timeseries": "Timeseries del dispositivo que contiene el valor del estado del led", + "check-status-method": "Método RPC para chequear el estado del dispositivo", + "parse-led-status-value-function": "Función de parseo para el estado del led", + "knob-title": "Título de mando", + "min-value": "Valor mínimo", + "max-value": "Valor máximo" }, "maps": { - "select-entity": "Select entity", - "select-entity-hint": "Hint: after selection click at the map to set position", + "select-entity": "Seleccionar entidad", + "select-entity-hint": "Ayuda: tras la selección haz click en el mapa para establecer la posición", "tooltips": { - "placeMarker": "Click to place '{{entityName}}' entity", - "firstVertex": "Polygon for '{{entityName}}': click to place first point", - "firstVertex-cut": "Click to place first point", - "continueLine": "Polygon for '{{entityName}}': click to continue drawing", - "continueLine-cut": "Click to continue drawing", - "finishLine": "Click any existing marker to finish", - "finishPoly": "Polygon for '{{entityName}}': click first marker to finish and save", - "finishPoly-cut": "Click first marker to finish and save", - "finishRect": "Polygon for '{{entityName}}': click to finish and save", - "startCircle": "Circle for '{{entityName}}': click to place circle center", - "finishCircle": "Circle for '{{entityName}}': click to finish circle", - "placeCircleMarker": "Click to place circle marker" + "placeMarker": "Click para colocar la entidad '{{entityName}}'", + "firstVertex": "Polígono para la entidad '{{entityName}}': click para colocar el primer punto", + "firstVertex-cut": "Click para colocar el primer punto", + "continueLine": "Polígono para la entidad '{{entityName}}': click para continuar dibujando", + "continueLine-cut": "Click para continuar dibujando", + "finishLine": "Click en cualquier marcador existente para finalizar", + "finishPoly": "Polígono para la entidad '{{entityName}}': click en el primer marcador para finalizar y grabar los cambios", + "finishPoly-cut": "Click en el primer marcador para finalizar y grabar los cambios", + "finishRect": "Polígono para la entidad '{{entityName}}': click para finalizar y grabar los cambios", + "startCircle": "Círculo para la entidad '{{entityName}}': click para establecer el centro del círculo", + "finishCircle": "Círculo para la entidad '{{entityName}}': click para finalizar el círculo", + "placeCircleMarker": "Click para colocar el marcador del círculo" }, "actions": { - "finish": "Finish", - "cancel": "Cancel", - "removeLastVertex": "Remove last point" + "finish": "Finalizar", + "cancel": "Cancelar", + "removeLastVertex": "Quitar último punto" }, "buttonTitles": { - "drawMarkerButton": "Place entity", - "drawPolyButton": "Create polygon", - "drawLineButton": "Create polyline", - "drawCircleButton": "Create circle", - "drawRectButton": "Create rectangle", - "editButton": "Edit mode", - "dragButton": "Drag-drop mode", - "cutButton": "Cut polygon area", - "deleteButton": "Remove", - "drawCircleMarkerButton": "Create circle marker", - "rotateButton": "Rotate polygon" + "drawMarkerButton": "Establecer entidad", + "drawPolyButton": "Crear polígono", + "drawLineButton": "Crear polilínea", + "drawCircleButton": "Crear círculo", + "drawRectButton": "Crear rectángulo", + "editButton": "Modo de edición", + "dragButton": "Modo Arrastrar-soltar", + "cutButton": "Cortar el área del polígono", + "deleteButton": "Borrar", + "drawCircleMarkerButton": "Crear marcador de círculo", + "rotateButton": "Rotar polígono" }, - "map-provider-settings": "Map provider settings", - "map-provider": "Map provider", + "map-provider-settings": "Ajustes proveedor de mapas", + "map-provider": "Proveedor de mapas", "map-provider-google": "Google maps", "map-provider-openstreet": "OpenStreet maps", "map-provider-here": "HERE maps", - "map-provider-image": "Image map", + "map-provider-image": "Mapa de imagen", "map-provider-tencent": "Tencent maps", - "openstreet-provider": "OpenStreet map provider", + "openstreet-provider": "Proveedor OpenStreet map", "openstreet-provider-mapnik": "OpenStreetMap.Mapnik (Default)", "openstreet-provider-hot": "OpenStreetMap.HOT", "openstreet-provider-esri-street": "Esri.WorldStreetMap", "openstreet-provider-esri-topo": "Esri.WorldTopoMap", "openstreet-provider-cartodb-positron": "CartoDB.Positron", "openstreet-provider-cartodb-dark-matter": "CartoDB.DarkMatter", - "use-custom-provider": "Use custom provider", - "custom-provider-tile-url": "Custom provider tile URL", - "google-maps-api-key": "Google Maps API Key", - "default-map-type": "Default map type", - "google-map-type-roadmap": "Roadmap", - "google-map-type-satelite": "Satellite", - "google-map-type-hybrid": "Hybrid", - "google-map-type-terrain": "Terrain", - "map-layer": "Map layer", - "here-map-normal-day": "HERE.normalDay (Default)", + "use-custom-provider": "Usar proveedor personalizado", + "custom-provider-tile-url": "URL de proveedor personalizado", + "google-maps-api-key": "API Key Google Maps", + "default-map-type": "Tipo de mapa por defecto", + "google-map-type-roadmap": "Carretera", + "google-map-type-satelite": "Satelite", + "google-map-type-hybrid": "Híbrido", + "google-map-type-terrain": "Terreno", + "map-layer": "Capa de mapa", + "here-map-normal-day": "HERE.normalDay (Defecto)", "here-map-normal-night": "HERE.normalNight", "here-map-hybrid-day": "HERE.hybridDay", "here-map-terrain-day": "HERE.terrainDay", - "credentials": "Credentials", + "credentials": "Credenciales", "here-app-id": "HERE app id", "here-app-code": "HERE app code", - "tencent-maps-api-key": "Tencent Maps API Key", - "tencent-map-type-roadmap": "Roadmap", - "tencent-map-type-satelite": "Satellite", - "tencent-map-type-hybrid": "Hybrid", - "image-map-background": "Image map background", - "image-map-background-from-entity-attribute": "Take image map background from entity attribute", - "image-url-source-entity-alias": "Image URL source entity alias", - "image-url-source-entity-attribute": "Image URL source entity attribute", - "common-map-settings": "Common map settings", - "x-pos-key-name": "X position key name", - "y-pos-key-name": "Y position key name", - "latitude-key-name": "Latitude key name", - "longitude-key-name": "Longitude key name", - "default-map-zoom-level": "Default map zoom level (0 - 20)", - "default-map-center-position": "Default map center position (0,0)", - "disable-scroll-zooming": "Disable scroll zooming", - "disable-zoom-control-buttons": "Disable zoom control buttons", - "fit-map-bounds": "Fit map bounds to cover all markers", - "use-default-map-center-position": "Use default map center position", - "entities-limit": "Limit of entities to load", - "markers-settings": "Markers settings", - "marker-offset-x": "Marker X offset relative to position multiplied by marker width", - "marker-offset-y": "Marker Y offset relative to position multiplied by marker height", - "position-function": "Position conversion function, should return x,y coordinates as double from 0 to 1 each", - "draggable-marker": "Draggable marker", - "label": "Label", - "show-label": "Show label", - "use-label-function": "Use label function", - "label-pattern": "Label (pattern examples: '${entityName}', '${entityName}: (Text ${keyName} units.)' )", - "label-function": "Label function", - "tooltip": "Tooltip", - "show-tooltip": "Show tooltip", - "show-tooltip-action": "Action for displaying the tooltip", - "show-tooltip-action-click": "Show tooltip on click (Default)", - "show-tooltip-action-hover": "Show tooltip on hover", - "auto-close-tooltips": "Auto-close tooltips", - "use-tooltip-function": "Use tooltip function", - "tooltip-pattern": "Tooltip (for ex. 'Text ${keyName} units.' or Link text')", - "tooltip-function": "Tooltip function", - "tooltip-offset-x": "Tooltip X offset relative to marker anchor multiplied by marker width", - "tooltip-offset-y": "Tooltip Y offset relative to marker anchor multiplied by marker height", + "tencent-maps-api-key": "API Key Tencent Maps", + "tencent-map-type-roadmap": "Carretera", + "tencent-map-type-satelite": "Satelite", + "tencent-map-type-hybrid": "Híbrido", + "image-map-background": "Fondo de mapa de imagen", + "image-map-background-from-entity-attribute": "Obtener imagen de fondo desde un atributo de la entidad", + "image-url-source-entity-alias": "Alias de entidad para URL de imagen", + "image-url-source-entity-attribute": "Atributo de entidad para URL de imagen", + "common-map-settings": "Ajustes comunes de mapas", + "x-pos-key-name": "Clave para posición X", + "y-pos-key-name": "Clave para posición Y", + "latitude-key-name": "Clave para latitud", + "longitude-key-name": "Clave para longitud", + "default-map-zoom-level": "Nivel de zoom por defecto (0 - 20)", + "default-map-center-position": "Posición central del mapa por defecto (0,0)", + "disable-scroll-zooming": "Desactivar zoom en scroll", + "disable-zoom-control-buttons": "Desactivar botones de zoom", + "fit-map-bounds": "Ajustar los límites del mapa para cubrir todos los marcadores", + "use-default-map-center-position": "Usar posición central por defecto", + "entities-limit": "Límite de entidades a cargar", + "markers-settings": "Ajustes de los marcadores", + "marker-offset-x": "Offset X relativo a la posición multiplicado por el ancho del marcador", + "marker-offset-y": "Offset Y relativo a la posición multiplicado por el alto del marcador", + "position-function": "Función de conversión de posición, debe retornar coordenadas x,y como valor double de 0 a 1", + "draggable-marker": "Marcador arrastrable", + "label": "Etiqueta", + "show-label": "Mostrar etiqueta", + "use-label-function": "Usar función de etiqueta", + "label-pattern": "Etiqueta (Ejemplos de patrón: '${entityName}', '${entityName}: (Texto ${keyName} unidades.)' )", + "label-function": "Función de etiqueta", + "tooltip": "Sugerencias (tooltip)", + "show-tooltip": "Mostrar sugerencias", + "show-tooltip-action": "Acción para mostrar las sugerencias", + "show-tooltip-action-click": "Mostrar sugerencias tooltip en click (por defecto)", + "show-tooltip-action-hover": "Mostrar sugerencias on hover", + "auto-close-tooltips": "Auto-cerrar sugerencias", + "use-tooltip-function": "Función de sugerencias", + "tooltip-pattern": "Sugerencia (Por ej. 'Texto ${keyName} unidades.' o Texto Link')", + "tooltip-function": "Función de sugerencia", + "tooltip-offset-x": "Offset X relativo al ancla del marcador multiplicado por el ancho", + "tooltip-offset-y": "Offset Y relativo al ancla del marcador multiplicado por la altura", "color": "Color", - "use-color-function": "Use color function", - "color-function": "Color function", - "marker-image": "Marker image", - "use-marker-image-function": "Use marker image function", - "custom-marker-image": "Custom marker image", - "custom-marker-image-size": "Custom marker image size (px)", - "marker-image-function": "Marker image function", - "marker-images": "Marker images", - "polygon-settings": "Polygon settings", - "show-polygon": "Show polygon", - "polygon-key-name": "Polygon key name", - "enable-polygon-edit": "Enable polygon edit", - "polygon-label": "Polygon label", - "show-polygon-label": "Show polygon label", - "use-polygon-label-function": "Use polygon label function", - "polygon-label-pattern": "Polygon label (pattern examples: '${entityName}', '${entityName}: (Text ${keyName} units.)' )", - "polygon-label-function": "Polygon label function", - "polygon-tooltip": "Polygon tooltip", - "show-polygon-tooltip": "Show polygon tooltip", - "auto-close-polygon-tooltips": "Auto-close polygon tooltips", - "use-polygon-tooltip-function": "Use polygon tooltip function", - "polygon-tooltip-pattern": "Tooltip (for ex. 'Text ${keyName} units.' or Link text')", - "polygon-tooltip-function": "Polygon tooltip function", - "polygon-color": "Polygon color", - "polygon-opacity": "Polygon opacity", - "use-polygon-color-function": "Use polygon color function", - "polygon-color-function": "Polygon color function", - "polygon-stroke": "Polygon stroke", - "stroke-color": "Stroke color", - "stroke-opacity": "Stroke opacity", - "stroke-weight": "Stroke weight", - "use-polygon-stroke-color-function": "Use polygon stroke color function", - "polygon-stroke-color-function": "Polygon stroke color function", - "circle-settings": "Circle settings", - "show-circle": "Show circle", - "circle-key-name": "Circle key name", - "enable-circle-edit": "Enable circle edit", - "circle-label": "Circle label", - "show-circle-label": "Show circle label", - "use-circle-label-function": "Use circle label function", - "circle-label-pattern": "Circle label (pattern examples: '${entityName}', '${entityName}: (Text ${keyName} units.)' )", - "circle-label-function": "Circle label function", - "circle-tooltip": "Circle tooltip", - "show-circle-tooltip": "Show circle tooltip", - "auto-close-circle-tooltips": "Auto-close circle tooltips", - "use-circle-tooltip-function": "Use circle tooltip function", - "circle-tooltip-pattern": "Tooltip (for ex. 'Text ${keyName} units.' or Link text')", - "circle-tooltip-function": "Circle tooltip function", - "circle-fill-color": "Circle fill color", - "circle-fill-color-opacity": "Circle fill color opacity", - "use-circle-fill-color-function": "Use circle fill color function", - "circle-fill-color-function": "Circle fill color function", - "circle-stroke": "Circle stroke", - "use-circle-stroke-color-function": "Use circle stroke color function", - "circle-stroke-color-function": "Circle stroke color function", - "markers-clustering-settings": "Markers clustering settings", - "use-map-markers-clustering": "Use map markers clustering", - "zoom-on-cluster-click": "Zoom when clicking on a cluster", - "max-cluster-zoom": "The maximum zoom level when a marker can be part of a cluster (0 - 18)", - "max-cluster-radius-pixels": "Maximum radius that a cluster will cover in pixels", - "cluster-zoom-animation": "Show animation on markers when zooming", - "show-markers-bounds-on-cluster-mouse-over": "Show the bounds of markers when mouse over a cluster", - "spiderfy-max-zoom-level": "Spiderfy at the max zoom level (to see all cluster markers)", - "load-optimization": "Load optimization", - "cluster-chunked-loading": "Use chunks for adding markers so that the page does not freeze", - "cluster-markers-lazy-load": "Use lazy load for adding markers", - "editor-settings": "Editor settings", - "enable-snapping": "Enable snapping to other vertices for precision drawing", - "init-draggable-mode": "Initialize map in draggable mode", - "hide-all-edit-buttons": "Hide all edit control buttons", - "hide-draw-buttons": "Hide draw buttons", - "hide-edit-buttons": "Hide edit buttons", - "hide-remove-button": "Hide remove button", - "route-map-settings": "Route map settings", - "trip-animation-settings": "Trip animation settings", - "normalization-step": "Normalization data step (ms)", - "tooltip-background-color": "Tooltip background color", - "tooltip-font-color": "Tooltip font color", - "tooltip-opacity": "Tooltip opacity (0-1)", - "auto-close-tooltip": "Auto-close tooltip", - "rotation-angle": "Set additional rotation angle for marker (deg)", - "path-settings": "Path settings", - "path-color": "Path color", - "use-path-color-function": "Use path color function", - "path-color-function": "Path color function", - "path-decorator": "Path decorator", - "use-path-decorator": "Use path decorator", - "decorator-symbol": "Decorator symbol", - "decorator-symbol-arrow-head": "Arrow", - "decorator-symbol-dash": "Dash", - "decorator-symbol-size": "Decorator symbol size (px)", - "use-path-decorator-custom-color": "Use path decorator custom color", - "decorator-custom-color": "Decorator custom color", - "decorator-offset": "Decorator offset", - "end-decorator-offset": "End decorator offset", - "decorator-repeat": "Decorator repeat", - "points-settings": "Points settings", - "show-points": "Show points", - "point-color": "Point color", - "point-size": "Point size (px)", - "use-point-color-function": "Use point color function", - "point-color-function": "Point color function", - "use-point-as-anchor": "Use point as anchor", - "point-as-anchor-function": "Point as anchor function", - "independent-point-tooltip": "Independent point tooltip" + "use-color-function": "Usar función de color", + "color-function": "Función de color", + "marker-image": "Imagen de marcador", + "use-marker-image-function": "Usar función de imagen de marcador", + "custom-marker-image": "Imagen de marcador personalizada", + "custom-marker-image-size": "Tamaño de imagen personalizada (px)", + "marker-image-function": "Función de imagen de marcador", + "marker-images": "Imagenes de marcador", + "polygon-settings": "Ajustes de polígono", + "show-polygon": "Mostrar polígono", + "polygon-key-name": "Clave del polígono", + "enable-polygon-edit": "Polígono editable", + "polygon-label": "Etiqueta del polígono", + "show-polygon-label": "Mostrar etiqueta del polígono", + "use-polygon-label-function": "Usar funciones de etiqueta en el polígono", + "polygon-label-pattern": "Etiqueta del polígono (Ejemplos de patrón: '${entityName}', '${entityName}: (Texto ${keyName} unidades.)' )", + "polygon-label-function": "Función de etiqueta del polígono", + "polygon-tooltip": "Sugerencia polígono", + "show-polygon-tooltip": "Mostrar sugerencias", + "auto-close-polygon-tooltips": "Auto-cerrar sugerencias polígono", + "use-polygon-tooltip-function": "Usar función de sugerencias en el polígono", + "polygon-tooltip-pattern": "Sugerencias (por ej. 'Texto ${keyName} unidades.' o Texto Link')", + "polygon-tooltip-function": "Función de sugerencia de polígono", + "polygon-color": "Color de polígono", + "polygon-opacity": "Opacidad de polígono", + "use-polygon-color-function": "Usar función de color en polígono", + "polygon-color-function": "Función de color de polígono", + "polygon-stroke": "Trazo de polígono", + "stroke-color": "Color de trazo", + "stroke-opacity": "Opacidad de trazo", + "stroke-weight": "Peso de trazo", + "use-polygon-stroke-color-function": "Usar función de color de trazo en polígono", + "polygon-stroke-color-function": "Función de color de trazo", + "circle-settings": "Ajustes de círculo", + "show-circle": "Mostrar círculo", + "circle-key-name": "Clave de círculo", + "enable-circle-edit": "Activar edición de círculo", + "circle-label": "Etiqueta de círculo", + "show-circle-label": "Mostrar etiqueta de círculo", + "use-circle-label-function": "Usar función para etiqueta de círculo", + "circle-label-pattern": "Etiqueta de círculo (Ejemplos patrón: '${entityName}', '${entityName}: (Texto ${keyName} unidades.)' )", + "circle-label-function": "Funcion de etiqueta de círculo", + "circle-tooltip": "Sugerencias de círculo", + "show-circle-tooltip": "Mostrar sugerencias de círculo", + "auto-close-circle-tooltips": "Auto-cerrar sugerencias", + "use-circle-tooltip-function": "Usar función de sugerencias en círculo", + "circle-tooltip-pattern": "Sugerencia (por ej. 'Texto ${keyName} unidades.' o Texto Link')", + "circle-tooltip-function": "Función de sugerencia círculo", + "circle-fill-color": "Color de relleno círculo", + "circle-fill-color-opacity": "Opacidad color de relleno", + "use-circle-fill-color-function": "Usar función de color de relleno", + "circle-fill-color-function": "Función de color de relleno", + "circle-stroke": "Trazo del círculo", + "use-circle-stroke-color-function": "Usar función de color de trazo", + "circle-stroke-color-function": "Función de color de trazo", + "markers-clustering-settings": "Ajustes de agrupación de marcadores", + "use-map-markers-clustering": "Usar agrupación de marcadores en mapa", + "zoom-on-cluster-click": "Zoom cuando se haga click en un grupo", + "max-cluster-zoom": "Nivel máximo de zoom en la que un marcador puede ser parte de un grupo (0 - 18)", + "max-cluster-radius-pixels": "Radio máximo que un grupo cubre en píxeles", + "cluster-zoom-animation": "Mostrar animacion en marcadores cuando se haga zoom", + "show-markers-bounds-on-cluster-mouse-over": "Mostrar los limites de los marcadores cuando el raton pase por encima de un grupo", + "spiderfy-max-zoom-level": "Spiderfy al máximo nivel de zoom (para ver todos los marcadores)", + "load-optimization": "Optimización de carga", + "cluster-chunked-loading": "Usar fragmentos para añadir marcadores para que la página no se congele", + "cluster-markers-lazy-load": "Usar lazy load al añadir marcadores", + "editor-settings": "Ajustes del editor", + "enable-snapping": "Habilitar ajuste a otros vértices para dibujar con precisión", + "init-draggable-mode": "Inicializar mapa en modo arrastre", + "hide-all-edit-buttons": "Ocultar todos los botones de edición", + "hide-draw-buttons": "Ocultar botones de dibujo", + "hide-edit-buttons": "Ocultar botones de edición", + "hide-remove-button": "Ocultar botones de borrado", + "route-map-settings": "Ajustes de mapa de ruta", + "trip-animation-settings": "Ajustes de animación de ruta", + "normalization-step": "Pasos de normalización (ms)", + "tooltip-background-color": "Color de fondo de la sugerencia", + "tooltip-font-color": "Color de fuente de las sugerencias", + "tooltip-opacity": "Opacidad de las sugerencias (0-1)", + "auto-close-tooltip": "Auto-cerrar sugerencias", + "rotation-angle": "Ángulo de rotación adicional para el marcador (grados)", + "path-settings": "Ajustes de ruta", + "path-color": "Color de ruta", + "use-path-color-function": "Usar función de color de ruta", + "path-color-function": "Función de color de ruta", + "path-decorator": "Decorador de ruta", + "use-path-decorator": "Usar decorador de ruta", + "decorator-symbol": "Símbolo del decorador", + "decorator-symbol-arrow-head": "Flecha", + "decorator-symbol-dash": "Estrella", + "decorator-symbol-size": "Tamaño del decorador (px)", + "use-path-decorator-custom-color": "Usar color personalizado en el decorador", + "decorator-custom-color": "Color personalizado del decorador", + "decorator-offset": "Offset decorador", + "end-decorator-offset": "Offset final del decorador", + "decorator-repeat": "Repetición del decorador", + "points-settings": "Ajustes de puntos", + "show-points": "Mostrar puntos", + "point-color": "Color de puntos", + "point-size": "Tamaño de puntos (px)", + "use-point-color-function": "Usar función de color de puntos", + "point-color-function": "Función de color de puntos", + "use-point-as-anchor": "Usar punto como ancla", + "point-as-anchor-function": "Función de punto como ancla", + "independent-point-tooltip": "Sugerencia independiente en punto" }, "markdown": { - "use-markdown-text-function": "Use markdown/HTML value function", - "markdown-text-function": "Markdown/HTML value function", - "markdown-text-pattern": "Markdown/HTML pattern (markdown or HTML with variables, for ex. '${entityName} or ${keyName} - some text.')", + "use-markdown-text-function": "Usar función markdown/HTML", + "markdown-text-function": "Función de valor Markdown/HTML", + "markdown-text-pattern": "Patrón de Markdown/HTML (markdown o HTML con variables, por ej. '${entityName} o ${keyName} - texto.')", "markdown-css": "Markdown/HTML CSS" }, "simple-card": { - "label-position": "Label position", - "label-position-left": "Left", - "label-position-top": "Top" + "label-position": "Posición etiqueta", + "label-position-left": "Izquierda", + "label-position-top": "Superior" }, "table": { - "common-table-settings": "Common Table Settings", - "enable-search": "Enable search", - "enable-sticky-header": "Always display header", - "enable-sticky-action": "Always display actions column", - "hidden-cell-button-display-mode": "Hidden cell button actions display mode", - "show-empty-space-hidden-action": "Show empty space instead of hidden cell button action", - "dont-reserve-space-hidden-action": "Don't reserve space for hidden action buttons", - "display-timestamp": "Display timestamp column", - "display-milliseconds": "Display timestamp milliseconds", - "display-pagination": "Display pagination", - "default-page-size": "Default page size", - "use-entity-label-tab-name": "Use entity label in tab name", - "hide-empty-lines": "Hide empty lines", - "row-style": "Row style", - "use-row-style-function": "Use row style function", - "row-style-function": "Row style function", - "cell-style": "Cell style", - "use-cell-style-function": "Use cell style function", - "cell-style-function": "Cell style function", - "cell-content": "Cell content", - "use-cell-content-function": "Use cell content function", - "cell-content-function": "Cell content function", - "show-latest-data-column": "Show latest data column", - "latest-data-column-order": "Latest data column order", - "entities-table-title": "Entities table title", - "enable-select-column-display": "Enable select columns to display", - "display-entity-name": "Display entity name column", - "entity-name-column-title": "Entity name column title", - "display-entity-label": "Display entity label column", - "entity-label-column-title": "Entity label column title", - "display-entity-type": "Display entity type column", - "default-sort-order": "Default sort order", - "column-width": "Column width (px or %)", - "default-column-visibility": "Default column visibility", + "common-table-settings": "Ajustes comunes en tablas", + "enable-search": "Activar búsqueda", + "enable-sticky-header": "Mostrar siempre el encabezado", + "enable-sticky-action": "Mostrar siempre la columna de acciones", + "hidden-cell-button-display-mode": "Visualización de botones de acción oculta", + "show-empty-space-hidden-action": "Mostrar espacio vacío en lugar de celda oculta", + "dont-reserve-space-hidden-action": "No reservar espacio para los botones en celda oculta", + "display-timestamp": "Mostrar columna timestamp", + "display-milliseconds": "Mostrar milisegundos", + "display-pagination": "Mostrar páginas", + "default-page-size": "Tamaño de página por defecto", + "use-entity-label-tab-name": "Usar etiqueta de entidad en el nombre de la tabla", + "hide-empty-lines": "Ocultar líneas vacías", + "row-style": "Estilo de fila", + "use-row-style-function": "Usar función de estilo de fila", + "row-style-function": "Función de estilo de fila", + "cell-style": "Estilo de celda", + "use-cell-style-function": "Usar función de estilo de celda", + "cell-style-function": "Función de estilo de celda", + "cell-content": "Contenido de celda", + "use-cell-content-function": "Usar función de contenido de celda", + "cell-content-function": "Función de contenido de celda", + "show-latest-data-column": "Mostrar columna de últimos datos", + "latest-data-column-order": "Órden de columna de últimos datos", + "entities-table-title": "Título de tabla de entidades", + "enable-select-column-display": "Activar posibilidad de seleccionar columnas a mostrar", + "display-entity-name": "Mostrar columna de nombre de entidad", + "entity-name-column-title": "Título de columna en nombre de entidad", + "display-entity-label": "Mostrar columna de etiqueta de entidad", + "entity-label-column-title": "Título de columna en etiqueta de entidad", + "display-entity-type": "Mostrar columna de tipo de entidad", + "default-sort-order": "Ordenación por defecto", + "column-width": "Ancho de columna (px o %)", + "default-column-visibility": "Visibilidad por defecto en columna", "column-visibility-visible": "Visible", - "column-visibility-hidden": "Hidden", - "column-selection-to-display": "Column selection in 'Columns to Display'", - "column-selection-to-display-enabled": "Enabled", - "column-selection-to-display-disabled": "Disabled", - "alarms-table-title": "Alarms table title", - "enable-alarms-selection": "Enable alarms selection", - "enable-alarms-search": "Enable alarms search", - "enable-alarm-filter": "Enable alarm filter", - "display-alarm-details": "Display alarm details", - "allow-alarms-ack": "Allow alarms acknowledgment", - "allow-alarms-clear": "Allow alarms clear" + "column-visibility-hidden": "Oculta", + "column-selection-to-display": "Selección de columnas en 'Columnas a Mostrar'", + "column-selection-to-display-enabled": "Activada", + "column-selection-to-display-disabled": "Desactivada", + "alarms-table-title": "Título de tabla de alarmas", + "enable-alarms-selection": "Activar selección de alarmas", + "enable-alarms-search": "Activar búsqueda de alarmas", + "enable-alarm-filter": "Activar filtro de alarmas", + "display-alarm-details": "Mostrar detalles de alarma", + "allow-alarms-ack": "Permitir reconocimiento de alarmas", + "allow-alarms-clear": "Permitir borrado de alarmas" }, "value-source": { - "value-source": "Value source", - "predefined-value": "Predefined value", - "entity-attribute": "Value taken from entity attribute", - "value": "Value", - "source-entity-alias": "Source entity alias", - "source-entity-attribute": "Source entity attribute" + "value-source": "Origen valor", + "predefined-value": "Valor predefinido", + "entity-attribute": "Valor tomado de un atributo de entidad", + "value": "Valor", + "source-entity-alias": "Alias entidad de origen", + "source-entity-attribute": "Atributo entidad de origen" }, "widget-font": { - "font-family": "Font family", - "size": "Size", - "relative-font-size": "Relative font size (percents)", - "font-style": "Style", + "font-family": "Familia (font family)", + "size": "Tamaño", + "relative-font-size": "Tamaño fuente relativo (porcentaje)", + "font-style": "Estilo", "font-style-normal": "Normal", - "font-style-italic": "Italic", - "font-style-oblique": "Oblique", - "font-weight": "Weight", + "font-style-italic": "Cursiva", + "font-style-oblique": "Subrayada", + "font-weight": "Peso", "font-weight-normal": "Normal", - "font-weight-bold": "Bold", - "font-weight-bolder": "Bolder", + "font-weight-bold": "Negrita", + "font-weight-bolder": "Negrita+", "font-weight-lighter": "Lighter", "color": "Color", - "shadow-color": "Shadow color" + "shadow-color": "Color sombra" } }, "icon": { From 9669da0442bb0243c16e8f3651439b6a45d0aac8 Mon Sep 17 00:00:00 2001 From: devaskim Date: Fri, 3 Jun 2022 23:06:34 +0500 Subject: [PATCH 758/798] Add resource service to widget context. --- .../widget/dynamic-widget.component.ts | 2 ++ .../app/modules/home/models/services.map.ts | 4 +++- .../home/models/widget-component.models.ts | 2 ++ .../models/ace/service-completion.models.ts | 19 +++++++++++++++++++ 4 files changed, 26 insertions(+), 1 deletion(-) diff --git a/ui-ngx/src/app/modules/home/components/widget/dynamic-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/dynamic-widget.component.ts index 7aa0f6933e..a76a65ab96 100644 --- a/ui-ngx/src/app/modules/home/components/widget/dynamic-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/dynamic-widget.component.ts @@ -38,6 +38,7 @@ import { EntityRelationService } from '@core/http/entity-relation.service'; import { EntityService } from '@core/http/entity.service'; import { DialogService } from '@core/services/dialog.service'; import { CustomDialogService } from '@home/components/widget/dialog/custom-dialog.service'; +import { ResourceService } from '@core/http/resource.service'; import { DatePipe } from '@angular/common'; import { TranslateService } from '@ngx-translate/core'; import { DomSanitizer } from '@angular/platform-browser'; @@ -76,6 +77,7 @@ export class DynamicWidgetComponent extends PageComponent implements IDynamicWid this.ctx.entityService = $injector.get(EntityService); this.ctx.dialogs = $injector.get(DialogService); this.ctx.customDialog = $injector.get(CustomDialogService); + this.ctx.resourceService = $injector.get(ResourceService); this.ctx.date = $injector.get(DatePipe); this.ctx.translate = $injector.get(TranslateService); this.ctx.http = $injector.get(HttpClient); diff --git a/ui-ngx/src/app/modules/home/models/services.map.ts b/ui-ngx/src/app/modules/home/models/services.map.ts index 1f3a2bf5ae..e2ad5f1ad6 100644 --- a/ui-ngx/src/app/modules/home/models/services.map.ts +++ b/ui-ngx/src/app/modules/home/models/services.map.ts @@ -36,6 +36,7 @@ import { BroadcastService } from '@core/services/broadcast.service'; import { ImportExportService } from '@home/components/import-export/import-export.service'; import { DeviceProfileService } from '@core/http/device-profile.service'; import { OtaPackageService } from '@core/http/ota-package.service'; +import { ResourceService } from '@core/http/resource.service'; export const ServicesMap = new Map>( [ @@ -59,6 +60,7 @@ export const ServicesMap = new Map>( ['router', Router], ['importExport', ImportExportService], ['deviceProfileService', DeviceProfileService], - ['otaPackageService', OtaPackageService] + ['otaPackageService', OtaPackageService], + ['resourceService', ResourceService] ] ); diff --git a/ui-ngx/src/app/modules/home/models/widget-component.models.ts b/ui-ngx/src/app/modules/home/models/widget-component.models.ts index 3bb727f4f5..0d8700145e 100644 --- a/ui-ngx/src/app/modules/home/models/widget-component.models.ts +++ b/ui-ngx/src/app/modules/home/models/widget-component.models.ts @@ -72,6 +72,7 @@ import { EntityRelationService } from '@core/http/entity-relation.service'; import { EntityService } from '@core/http/entity.service'; import { DialogService } from '@core/services/dialog.service'; import { CustomDialogService } from '@home/components/widget/dialog/custom-dialog.service'; +import { ResourceService } from '@core/http/resource.service'; import { DatePipe } from '@angular/common'; import { TranslateService } from '@ngx-translate/core'; import { PageLink } from '@shared/models/page/page-link'; @@ -168,6 +169,7 @@ export class WidgetContext { entityService: EntityService; dialogs: DialogService; customDialog: CustomDialogService; + resourceService: ResourceService; date: DatePipe; translate: TranslateService; http: HttpClient; diff --git a/ui-ngx/src/app/shared/models/ace/service-completion.models.ts b/ui-ngx/src/app/shared/models/ace/service-completion.models.ts index 25c5b5dc7d..bcad0a000c 100644 --- a/ui-ngx/src/app/shared/models/ace/service-completion.models.ts +++ b/ui-ngx/src/app/shared/models/ace/service-completion.models.ts @@ -100,6 +100,8 @@ export const importEntitiesResultInfoHref = 'CustomDialogComponent'; +export const resourceInfoHref = 'Resource info'; + export const pageLinkArg: FunctionArg = { name: 'pageLink', type: 'PageLink', @@ -1300,6 +1302,23 @@ export const serviceCompletions: TbEditorCompletions = { }, } }, + resourceService: { + description: 'Resource Service API
' + + 'See ResourceService for API reference.', + meta: 'service', + type: 'ResourceService', + children: { + getResources: { + description: 'Find resources by search text', + meta: 'function', + args: [ + pageLinkArg, + requestConfigArg + ], + return: observablePageDataReturnType(resourceInfoHref) + }, + } + }, dialogs: { description: 'Dialogs Service API
' + 'See DialogService for API reference.', From ac751b09f90c2f3e27679fa5735ba05ec6a3b957 Mon Sep 17 00:00:00 2001 From: YevhenBondarenko Date: Mon, 6 Jun 2022 09:17:19 +0200 Subject: [PATCH 759/798] fixed hash partition service initialization --- .../service/cluster/routing/HashPartitionServiceTest.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/application/src/test/java/org/thingsboard/server/service/cluster/routing/HashPartitionServiceTest.java b/application/src/test/java/org/thingsboard/server/service/cluster/routing/HashPartitionServiceTest.java index 74738c361f..cea08003bf 100644 --- a/application/src/test/java/org/thingsboard/server/service/cluster/routing/HashPartitionServiceTest.java +++ b/application/src/test/java/org/thingsboard/server/service/cluster/routing/HashPartitionServiceTest.java @@ -85,7 +85,9 @@ public class HashPartitionServiceTest { .addAllServiceTypes(Collections.singletonList(ServiceType.TB_CORE.name())) .build()); } + clusterRoutingService.init(); + clusterRoutingService.partitionsInit(); clusterRoutingService.recalculatePartitions(currentServer, otherServers); } From fa87a2fc8f3f0da9573f24c5e2e0e2e2271e3a5f Mon Sep 17 00:00:00 2001 From: YevhenBondarenko Date: Mon, 6 Jun 2022 09:35:04 +0200 Subject: [PATCH 760/798] fixed Rate limits test --- .../org/thingsboard/server/common/msg/tools/RateLimitsTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/message/src/test/java/org/thingsboard/server/common/msg/tools/RateLimitsTest.java b/common/message/src/test/java/org/thingsboard/server/common/msg/tools/RateLimitsTest.java index b0bbfa3dc6..c8583527f1 100644 --- a/common/message/src/test/java/org/thingsboard/server/common/msg/tools/RateLimitsTest.java +++ b/common/message/src/test/java/org/thingsboard/server/common/msg/tools/RateLimitsTest.java @@ -67,7 +67,7 @@ public class RateLimitsTest { assertThat(rateLimits.tryConsume()).as("new token is available").isFalse(); int expectedRefillTime = period * 1000; - int gap = 300; + int gap = 500; await("tokens refill for rate limit " + rateLimitConfig) .atLeast(expectedRefillTime - gap, TimeUnit.MILLISECONDS) From 6f21de1d4d46f28db76fc46f856fe87e53594dab Mon Sep 17 00:00:00 2001 From: fe-dev Date: Mon, 6 Jun 2022 13:33:01 +0300 Subject: [PATCH 761/798] UI: Fix translation, console error and add height for menu visability --- ui-ngx/src/app/core/services/menu.service.ts | 2 +- .../home/components/profile/tenant-profile-data.component.ts | 2 +- .../app/modules/home/components/queue/queue-form.component.html | 2 +- ui-ngx/src/app/shared/models/queue.models.ts | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ui-ngx/src/app/core/services/menu.service.ts b/ui-ngx/src/app/core/services/menu.service.ts index a655b0d8b1..f042ff6b5e 100644 --- a/ui-ngx/src/app/core/services/menu.service.ts +++ b/ui-ngx/src/app/core/services/menu.service.ts @@ -108,7 +108,7 @@ export class MenuService { name: 'admin.system-settings', type: 'toggle', path: '/settings', - height: '280px', + height: '320px', icon: 'settings', pages: [ { diff --git a/ui-ngx/src/app/modules/home/components/profile/tenant-profile-data.component.ts b/ui-ngx/src/app/modules/home/components/profile/tenant-profile-data.component.ts index dacf3df551..a6b02305d5 100644 --- a/ui-ngx/src/app/modules/home/components/profile/tenant-profile-data.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/tenant-profile-data.component.ts @@ -95,7 +95,7 @@ export class TenantProfileDataComponent implements ControlValueAccessor, OnInit, if (this.tenantProfileDataFormGroup.valid) { tenantProfileData = this.tenantProfileDataFormGroup.getRawValue(); } - this.propagateChange(tenantProfileData.configuration); + this.propagateChange(tenantProfileData?.configuration); } } diff --git a/ui-ngx/src/app/modules/home/components/queue/queue-form.component.html b/ui-ngx/src/app/modules/home/components/queue/queue-form.component.html index 5dee2afa87..fce4a9e499 100644 --- a/ui-ngx/src/app/modules/home/components/queue/queue-form.component.html +++ b/ui-ngx/src/app/modules/home/components/queue/queue-form.component.html @@ -16,7 +16,7 @@ --> -
+ admin.queue-name diff --git a/ui-ngx/src/app/shared/models/queue.models.ts b/ui-ngx/src/app/shared/models/queue.models.ts index dc90781d30..5814a6eeb9 100644 --- a/ui-ngx/src/app/shared/models/queue.models.ts +++ b/ui-ngx/src/app/shared/models/queue.models.ts @@ -75,7 +75,7 @@ export const QueueProcessingStrategyTypesMap = new Map Date: Mon, 6 Jun 2022 15:12:57 +0300 Subject: [PATCH 762/798] Fix backward compatibility for defaultQueueName field of device profile --- .../install/SqlDatabaseUpgradeService.java | 41 +++++++++++++++++++ .../update/DefaultDataUpdateService.java | 38 ----------------- .../server/common/data/DeviceProfile.java | 12 ++++++ .../dao/device/DeviceProfileServiceImpl.java | 11 +++++ 4 files changed, 64 insertions(+), 38 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java b/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java index d0d5f37e00..306102c66c 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java +++ b/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java @@ -571,6 +571,22 @@ public class SqlDatabaseUpgradeService implements DatabaseEntitiesUpgradeService schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "3.3.4", SCHEMA_UPDATE_SQL); loadSql(schemaUpdateFile, conn); + log.info("Loading queues..."); + try { + if (!CollectionUtils.isEmpty(queueConfig.getQueues())) { + queueConfig.getQueues().forEach(queueSettings -> { + Queue queue = queueConfigToQueue(queueSettings); + Queue existing = queueService.findQueueByTenantIdAndName(queue.getTenantId(), queue.getName()); + if (existing == null) { + queueService.saveQueue(queue); + } + }); + } else { + systemDataLoaderService.createQueues(); + } + } catch (Exception e) { + } + log.info("Updating device profiles..."); schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "3.3.4", "schema_update_device_profile.sql"); loadSql(schemaUpdateFile, conn); @@ -628,4 +644,29 @@ public class SqlDatabaseUpgradeService implements DatabaseEntitiesUpgradeService return isOldSchema; } + private Queue queueConfigToQueue(TbRuleEngineQueueConfiguration queueSettings) { + Queue queue = new Queue(); + queue.setTenantId(TenantId.SYS_TENANT_ID); + queue.setName(queueSettings.getName()); + queue.setTopic(queueSettings.getTopic()); + queue.setPollInterval(queueSettings.getPollInterval()); + queue.setPartitions(queueSettings.getPartitions()); + queue.setPackProcessingTimeout(queueSettings.getPackProcessingTimeout()); + SubmitStrategy submitStrategy = new SubmitStrategy(); + submitStrategy.setBatchSize(queueSettings.getSubmitStrategy().getBatchSize()); + submitStrategy.setType(SubmitStrategyType.valueOf(queueSettings.getSubmitStrategy().getType())); + queue.setSubmitStrategy(submitStrategy); + ProcessingStrategy processingStrategy = new ProcessingStrategy(); + processingStrategy.setType(ProcessingStrategyType.valueOf(queueSettings.getProcessingStrategy().getType())); + processingStrategy.setRetries(queueSettings.getProcessingStrategy().getRetries()); + processingStrategy.setFailurePercentage(queueSettings.getProcessingStrategy().getFailurePercentage()); + processingStrategy.setPauseBetweenRetries(queueSettings.getProcessingStrategy().getPauseBetweenRetries()); + processingStrategy.setMaxPauseBetweenRetries(queueSettings.getProcessingStrategy().getMaxPauseBetweenRetries()); + queue.setProcessingStrategy(processingStrategy); + queue.setConsumerPerPartition(queueSettings.isConsumerPerPartition()); + return queue; + } + + + } diff --git a/application/src/main/java/org/thingsboard/server/service/install/update/DefaultDataUpdateService.java b/application/src/main/java/org/thingsboard/server/service/install/update/DefaultDataUpdateService.java index 7b87668319..e27f2e88c2 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/update/DefaultDataUpdateService.java +++ b/application/src/main/java/org/thingsboard/server/service/install/update/DefaultDataUpdateService.java @@ -162,21 +162,6 @@ public class DefaultDataUpdateService implements DataUpdateService { break; case "3.3.4": log.info("Updating data from version 3.3.4 to 3.4.0 ..."); - log.info("Loading queues..."); - try { - if (!CollectionUtils.isEmpty(queueConfig.getQueues())) { - queueConfig.getQueues().forEach(queueSettings -> { - Queue queue = queueConfigToQueue(queueSettings); - Queue existing = queueService.findQueueByTenantIdAndName(queue.getTenantId(), queue.getName()); - if (existing == null) { - queueService.saveQueue(queue); - } - }); - } else { - systemDataLoaderService.createQueues(); - } - } catch (Exception e) { - } tenantsProfileQueueConfigurationUpdater.updateEntities(null); checkPointRuleNodesUpdater.updateEntities(null); break; @@ -649,29 +634,6 @@ public class DefaultDataUpdateService implements DataUpdateService { return mainQueueConfiguration; } - private Queue queueConfigToQueue(TbRuleEngineQueueConfiguration queueSettings) { - Queue queue = new Queue(); - queue.setTenantId(TenantId.SYS_TENANT_ID); - queue.setName(queueSettings.getName()); - queue.setTopic(queueSettings.getTopic()); - queue.setPollInterval(queueSettings.getPollInterval()); - queue.setPartitions(queueSettings.getPartitions()); - queue.setPackProcessingTimeout(queueSettings.getPackProcessingTimeout()); - SubmitStrategy submitStrategy = new SubmitStrategy(); - submitStrategy.setBatchSize(queueSettings.getSubmitStrategy().getBatchSize()); - submitStrategy.setType(SubmitStrategyType.valueOf(queueSettings.getSubmitStrategy().getType())); - queue.setSubmitStrategy(submitStrategy); - ProcessingStrategy processingStrategy = new ProcessingStrategy(); - processingStrategy.setType(ProcessingStrategyType.valueOf(queueSettings.getProcessingStrategy().getType())); - processingStrategy.setRetries(queueSettings.getProcessingStrategy().getRetries()); - processingStrategy.setFailurePercentage(queueSettings.getProcessingStrategy().getFailurePercentage()); - processingStrategy.setPauseBetweenRetries(queueSettings.getProcessingStrategy().getPauseBetweenRetries()); - processingStrategy.setMaxPauseBetweenRetries(queueSettings.getProcessingStrategy().getMaxPauseBetweenRetries()); - queue.setProcessingStrategy(processingStrategy); - queue.setConsumerPerPartition(queueSettings.isConsumerPerPartition()); - return queue; - } - private final PaginatedUpdater checkPointRuleNodesUpdater = new PaginatedUpdater<>() { diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/DeviceProfile.java b/common/data/src/main/java/org/thingsboard/server/common/data/DeviceProfile.java index 36cbda47c3..f432862499 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/DeviceProfile.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/DeviceProfile.java @@ -16,6 +16,7 @@ package org.thingsboard.server.common.data; import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.core.JsonProcessingException; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; @@ -78,6 +79,8 @@ public class DeviceProfile extends SearchTextBased implements H "If present, the specified queue will be used to store all unprocessed messages related to device, including telemetry, attribute updates, etc. " + "Otherwise, the 'Main' queue will be used to store those messages.") private QueueId defaultQueueId; + + private String defaultQueueName; @Valid private transient DeviceProfileData profileData; @JsonIgnore @@ -168,4 +171,13 @@ public class DeviceProfile extends SearchTextBased implements H } } + @JsonIgnore + public String getDefaultQueueName() { + return defaultQueueName; + } + + @JsonProperty + public void setDefaultQueueName(String defaultQueueName) { + this.defaultQueueName = defaultQueueName; + } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileServiceImpl.java index 04084de987..df5a49c1e1 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileServiceImpl.java @@ -37,8 +37,10 @@ import org.thingsboard.server.common.data.id.DeviceProfileId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; +import org.thingsboard.server.common.data.queue.Queue; import org.thingsboard.server.dao.entity.AbstractCachedEntityService; import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.dao.queue.QueueService; import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.service.PaginatedRemover; import org.thingsboard.server.dao.service.Validator; @@ -71,6 +73,9 @@ public class DeviceProfileServiceImpl extends AbstractCachedEntityService deviceProfileValidator; + @Autowired + private QueueService queueService; + private final Lock findOrCreateLock = new ReentrantLock(); @TransactionalEventListener(classes = DeviceProfileEvictEvent.class) @@ -119,6 +124,12 @@ public class DeviceProfileServiceImpl extends AbstractCachedEntityService Date: Mon, 6 Jun 2022 15:18:28 +0300 Subject: [PATCH 763/798] UI: Add translate for table cell --- .../pages/admin/queue/queues-table-config.resolver.ts | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/ui-ngx/src/app/modules/home/pages/admin/queue/queues-table-config.resolver.ts b/ui-ngx/src/app/modules/home/pages/admin/queue/queues-table-config.resolver.ts index 5af2735a49..24f33a8a03 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/queue/queues-table-config.resolver.ts +++ b/ui-ngx/src/app/modules/home/pages/admin/queue/queues-table-config.resolver.ts @@ -17,7 +17,12 @@ import { Injectable } from '@angular/core'; import { ActivatedRouteSnapshot, Resolve, Router } from '@angular/router'; import { EntityTableColumn, EntityTableConfig } from '@home/models/entity/entities-table-config.models'; -import { QueueInfo, ServiceType } from '@shared/models/queue.models'; +import { + QueueInfo, + QueueProcessingStrategyTypesMap, + QueueSubmitStrategyTypesMap, + ServiceType +} from '@shared/models/queue.models'; import { select, Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; import { BroadcastService } from '@core/services/broadcast.service'; @@ -83,14 +88,14 @@ export class QueuesTableConfigResolver implements Resolve('partitions', 'admin.queue-partitions', '25%'), new EntityTableColumn('submitStrategy', 'admin.queue-submit-strategy', '25%', (entity: QueueInfo) => { - return entity.submitStrategy.type; + return this.translate.instant(QueueSubmitStrategyTypesMap.get(entity.submitStrategy.type).label); }, () => ({}), false ), new EntityTableColumn('processingStrategy', 'admin.queue-processing-strategy', '25%', (entity: QueueInfo) => { - return entity.processingStrategy.type; + return this.translate.instant(QueueProcessingStrategyTypesMap.get(entity.processingStrategy.type).label); }, () => ({}), false From c5ff1252f59a6e309d6ce2efd494d56ae3b5f5e8 Mon Sep 17 00:00:00 2001 From: Andrii Shvaika Date: Mon, 6 Jun 2022 15:47:39 +0300 Subject: [PATCH 764/798] Startup sequence optimization --- .../queue/discovery/HashPartitionService.java | 39 +++++++++++++------ 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/discovery/HashPartitionService.java b/common/queue/src/main/java/org/thingsboard/server/queue/discovery/HashPartitionService.java index 8a56a86bcc..3317e75c91 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/discovery/HashPartitionService.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/discovery/HashPartitionService.java @@ -90,20 +90,37 @@ public class HashPartitionService implements PartitionService { @PostConstruct public void init() { this.hashFunction = forName(hashFunctionName); + QueueKey coreKey = new QueueKey(ServiceType.TB_CORE); + partitionSizesMap.put(coreKey, corePartitions); + partitionTopicsMap.put(coreKey, coreTopic); + + if (!isTransport(serviceInfoProvider.getServiceType())) { + doInitRuleEnginePartitions(); + } } @AfterStartUp(order = AfterStartUp.QUEUE_INFO_INITIALIZATION) public void partitionsInit() { - QueueKey coreKey = new QueueKey(ServiceType.TB_CORE); - partitionSizesMap.put(coreKey, corePartitions); - partitionTopicsMap.put(coreKey, coreTopic); + if (isTransport(serviceInfoProvider.getServiceType())) { + doInitRuleEnginePartitions(); + } + } - List queueRoutingInfoList; + private void doInitRuleEnginePartitions() { + List queueRoutingInfoList = getQueueRoutingInfos(); + queueRoutingInfoList.forEach(queue -> { + QueueKey queueKey = new QueueKey(ServiceType.TB_RULE_ENGINE, queue); + partitionTopicsMap.put(queueKey, queue.getQueueTopic()); + partitionSizesMap.put(queueKey, queue.getPartitions()); + queuesById.put(queue.getQueueId(), queue); + }); + } + private List getQueueRoutingInfos() { + List queueRoutingInfoList; String serviceType = serviceInfoProvider.getServiceType(); - - if ("tb-transport".equals(serviceType)) { + if (isTransport(serviceType)) { //If transport started earlier than tb-core int getQueuesRetries = 10; while (true) { @@ -128,13 +145,11 @@ public class HashPartitionService implements PartitionService { } else { queueRoutingInfoList = queueRoutingInfoService.getAllQueuesRoutingInfo(); } + return queueRoutingInfoList; + } - queueRoutingInfoList.forEach(queue -> { - QueueKey queueKey = new QueueKey(ServiceType.TB_RULE_ENGINE, queue); - partitionTopicsMap.put(queueKey, queue.getQueueTopic()); - partitionSizesMap.put(queueKey, queue.getPartitions()); - queuesById.put(queue.getQueueId(), queue); - }); + private boolean isTransport(String serviceType) { + return "tb-transport".equals(serviceType); } @Override From 2fecf128a099b249da8b48851d2d97c3080e17d4 Mon Sep 17 00:00:00 2001 From: fe-dev Date: Mon, 6 Jun 2022 16:13:33 +0300 Subject: [PATCH 765/798] UI: add for expansion panel header fix height --- .../modules/home/components/queue/queue-form.component.scss | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ui-ngx/src/app/modules/home/components/queue/queue-form.component.scss b/ui-ngx/src/app/modules/home/components/queue/queue-form.component.scss index c42c4d714d..13128ad29a 100644 --- a/ui-ngx/src/app/modules/home/components/queue/queue-form.component.scss +++ b/ui-ngx/src/app/modules/home/components/queue/queue-form.component.scss @@ -17,6 +17,10 @@ .queue-strategy { padding-bottom: 16px; + .mat-expansion-panel-header { + height: 50px; + } + .mat-expansion-panel-body { padding-bottom: 0 !important; } From b61ce04420ace1597bf6f6a014d969e06e5d4f7c Mon Sep 17 00:00:00 2001 From: fe-dev Date: Mon, 6 Jun 2022 17:47:02 +0300 Subject: [PATCH 766/798] UI: Fixed description for default created queues --- .../app/modules/home/components/queue/queue-form.component.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ui-ngx/src/app/modules/home/components/queue/queue-form.component.ts b/ui-ngx/src/app/modules/home/components/queue/queue-form.component.ts index bb86b1edcb..b84dfc9985 100644 --- a/ui-ngx/src/app/modules/home/components/queue/queue-form.component.ts +++ b/ui-ngx/src/app/modules/home/components/queue/queue-form.component.ts @@ -158,6 +158,8 @@ export class QueueFormComponent implements ControlValueAccessor, OnInit, OnDestr this.modelValue = value; if (isDefinedAndNotNull(this.modelValue)) { this.queueFormGroup.patchValue(this.modelValue, {emitEvent: false}); + this.queueFormGroup.get('additionalInfo').get('description') + .patchValue(this.modelValue.additionalInfo?.description, {emitEvent: false}); this.submitStrategyTypeChanged(); } if (!this.disabled && !this.queueFormGroup.valid) { From 16891917ee7a7a53793c55668bfa8d20477d6239 Mon Sep 17 00:00:00 2001 From: Viacheslav Klimov Date: Mon, 6 Jun 2022 18:41:58 +0300 Subject: [PATCH 767/798] Minor fixes for 2FA --- .../mfa/config/DefaultTwoFaConfigManager.java | 27 ++++++++++++++++--- .../model/mfa/PlatformTwoFaSettings.java | 2 ++ ui-ngx/src/app/core/services/menu.service.ts | 2 +- .../email-auth-dialog.component.scss | 15 ----------- .../home/pages/security/security.component.ts | 14 +++++----- .../login/two-factor-auth-login.component.ts | 9 +++++++ .../shared/models/two-factor-auth.models.ts | 7 +++-- 7 files changed, 47 insertions(+), 29 deletions(-) delete mode 100644 ui-ngx/src/app/modules/home/pages/profile/authentication-dialog/email-auth-dialog.component.scss diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/mfa/config/DefaultTwoFaConfigManager.java b/application/src/main/java/org/thingsboard/server/service/security/auth/mfa/config/DefaultTwoFaConfigManager.java index 23e6dc065f..ad2ecebd2c 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/mfa/config/DefaultTwoFaConfigManager.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/mfa/config/DefaultTwoFaConfigManager.java @@ -56,12 +56,31 @@ public class DefaultTwoFaConfigManager implements TwoFaConfigManager { @Override public Optional getAccountTwoFaSettings(TenantId tenantId, UserId userId) { + PlatformTwoFaSettings platformTwoFaSettings = getPlatformTwoFaSettings(tenantId, true).orElse(null); return Optional.ofNullable(userAuthSettingsDao.findByUserId(userId)) - .flatMap(userAuthSettings -> Optional.ofNullable(userAuthSettings.getTwoFaSettings())) - .map(twoFaSettings -> { - twoFaSettings.getConfigs().keySet().removeIf(providerType -> { - return getTwoFaProviderConfig(tenantId, providerType).isEmpty(); + .map(userAuthSettings -> { + AccountTwoFaSettings twoFaSettings = userAuthSettings.getTwoFaSettings(); + if (twoFaSettings == null) return null; + boolean updateNeeded; + + Map configs = twoFaSettings.getConfigs(); + updateNeeded = configs.keySet().removeIf(providerType -> { + return platformTwoFaSettings == null || platformTwoFaSettings.getProviderConfig(providerType).isEmpty(); }); + if (configs.size() == 1 && configs.containsKey(TwoFaProviderType.BACKUP_CODE)) { + configs.remove(TwoFaProviderType.BACKUP_CODE); + updateNeeded = true; + } + if (!configs.isEmpty() && configs.values().stream().noneMatch(TwoFaAccountConfig::isUseByDefault)) { + configs.values().stream() + .filter(config -> config.getProviderType() != TwoFaProviderType.BACKUP_CODE) + .findFirst().ifPresent(config -> config.setUseByDefault(true)); + updateNeeded = true; + } + + if (updateNeeded) { + twoFaSettings = saveAccountTwoFaSettings(tenantId, userId, twoFaSettings); + } return twoFaSettings; }); } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/security/model/mfa/PlatformTwoFaSettings.java b/common/data/src/main/java/org/thingsboard/server/common/data/security/model/mfa/PlatformTwoFaSettings.java index fd72e7a027..ec2acf8733 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/security/model/mfa/PlatformTwoFaSettings.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/security/model/mfa/PlatformTwoFaSettings.java @@ -35,12 +35,14 @@ public class PlatformTwoFaSettings { @NotNull private List providers; + @NotNull @Min(value = 5, message = "minimum verification code sent period must be greater than or equal 5") private Integer minVerificationCodeSendPeriod; @Pattern(regexp = "[1-9]\\d*:[1-9]\\d*", message = "verification code check rate limit configuration is invalid") private String verificationCodeCheckRateLimit; @Min(value = 0, message = "maximum number of verification failure before user lockout must be positive") private Integer maxVerificationFailuresBeforeUserLockout; + @NotNull @Min(value = 60, message = "total amount of time allotted for verification must be greater than or equal 60") private Integer totalAllowedTimeForVerification; diff --git a/ui-ngx/src/app/core/services/menu.service.ts b/ui-ngx/src/app/core/services/menu.service.ts index a655b0d8b1..c9ff9f7da7 100644 --- a/ui-ngx/src/app/core/services/menu.service.ts +++ b/ui-ngx/src/app/core/services/menu.service.ts @@ -376,7 +376,7 @@ export class MenuService { name: 'admin.system-settings', type: 'toggle', path: '/settings', - height: '120px', + height: '80px', icon: 'settings', pages: [ { diff --git a/ui-ngx/src/app/modules/home/pages/profile/authentication-dialog/email-auth-dialog.component.scss b/ui-ngx/src/app/modules/home/pages/profile/authentication-dialog/email-auth-dialog.component.scss deleted file mode 100644 index ec6f008a80..0000000000 --- a/ui-ngx/src/app/modules/home/pages/profile/authentication-dialog/email-auth-dialog.component.scss +++ /dev/null @@ -1,15 +0,0 @@ -/** - * Copyright © 2016-2022 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. - */ diff --git a/ui-ngx/src/app/modules/home/pages/security/security.component.ts b/ui-ngx/src/app/modules/home/pages/security/security.component.ts index ab7d470e9e..560ec2b7d2 100644 --- a/ui-ngx/src/app/modules/home/pages/security/security.component.ts +++ b/ui-ngx/src/app/modules/home/pages/security/security.component.ts @@ -29,6 +29,7 @@ import { DatePipe } from '@angular/common'; import { ClipboardService } from 'ngx-clipboard'; import { TwoFactorAuthenticationService } from '@core/http/two-factor-authentication.service'; import { + AccountTwoFaSettingProviders, AccountTwoFaSettings, BackupCodeTwoFactorAuthAccountConfig, EmailTwoFactorAuthAccountConfig, @@ -49,7 +50,7 @@ import { isDefinedAndNotNull } from '@core/utils'; export class SecurityComponent extends PageComponent implements OnInit, OnDestroy { private readonly destroy$ = new Subject(); - private accountConfig: AccountTwoFaSettings; + private accountConfig: AccountTwoFaSettingProviders; twoFactorAuth: FormGroup; user: User; @@ -128,12 +129,11 @@ export class SecurityComponent extends PageComponent implements OnInit, OnDestro } private processTwoFactorAuthConfig(setting: AccountTwoFaSettings) { - this.accountConfig = setting; - const configs = this.accountConfig.configs; + this.accountConfig = setting?.configs || {}; Object.values(TwoFactorAuthProviderType).forEach(provider => { - if (configs[provider]) { + if (this.accountConfig[provider]) { this.twoFactorAuth.get(provider).setValue(true); - if (configs[provider].useByDefault) { + if (this.accountConfig[provider].useByDefault) { this.useByDefault = provider; } } else { @@ -216,7 +216,7 @@ export class SecurityComponent extends PageComponent implements OnInit, OnDestro } generateNewBackupCode() { - const codeLeft = this.accountConfig.configs[TwoFactorAuthProviderType.BACKUP_CODE].codesLeft; + const codeLeft = (this.accountConfig[TwoFactorAuthProviderType.BACKUP_CODE] as BackupCodeTwoFactorAuthAccountConfig).codesLeft; let subscription: Observable; if (codeLeft) { subscription = this.dialogService.confirm( @@ -240,7 +240,7 @@ export class SecurityComponent extends PageComponent implements OnInit, OnDestro providerDataInfo(provider: TwoFactorAuthProviderType) { const info = {info: null}; - const providerConfig = this.accountConfig.configs[provider]; + const providerConfig = this.accountConfig[provider]; if (isDefinedAndNotNull(providerConfig)) { switch (provider) { case TwoFactorAuthProviderType.EMAIL: diff --git a/ui-ngx/src/app/modules/login/pages/login/two-factor-auth-login.component.ts b/ui-ngx/src/app/modules/login/pages/login/two-factor-auth-login.component.ts index 73401f2a0c..89105cd18e 100644 --- a/ui-ngx/src/app/modules/login/pages/login/two-factor-auth-login.component.ts +++ b/ui-ngx/src/app/modules/login/pages/login/two-factor-auth-login.component.ts @@ -29,6 +29,7 @@ import { import { TranslateService } from '@ngx-translate/core'; import { interval, Subscription } from 'rxjs'; import { isEqual } from '@core/utils'; +import {ActionNotificationShow} from "@core/notification/notification.actions"; @Component({ selector: 'tb-two-factor-auth-login', @@ -118,6 +119,14 @@ export class TwoFactorAuthLoginComponent extends PageComponent implements OnInit } this.verificationForm.get('verificationCode').setErrors(errors); }, 5000); + } else { + this.store.dispatch(new ActionNotificationShow( + { + message: error.error.message, + type: 'error', + verticalPosition: 'top', + horizontalPosition: 'left' + })); } } ); diff --git a/ui-ngx/src/app/shared/models/two-factor-auth.models.ts b/ui-ngx/src/app/shared/models/two-factor-auth.models.ts index 10040e00e7..d7961050ed 100644 --- a/ui-ngx/src/app/shared/models/two-factor-auth.models.ts +++ b/ui-ngx/src/app/shared/models/two-factor-auth.models.ts @@ -88,9 +88,12 @@ export interface BackupCodeTwoFactorAuthAccountConfig extends GeneralTwoFactorAu export type TwoFactorAuthAccountConfig = TotpTwoFactorAuthAccountConfig | SmsTwoFactorAuthAccountConfig | EmailTwoFactorAuthAccountConfig | BackupCodeTwoFactorAuthAccountConfig; - export interface AccountTwoFaSettings { - configs: {TwoFactorAuthProviderType: TwoFactorAuthAccountConfig}; + configs: AccountTwoFaSettingProviders; +} + +export type AccountTwoFaSettingProviders = { + [key in TwoFactorAuthProviderType]?: TwoFactorAuthAccountConfig; } export interface TwoFaProviderInfo { From 4082d901d020f6a3d75dc674d0fb68394b87fb23 Mon Sep 17 00:00:00 2001 From: kalutkaz Date: Tue, 7 Jun 2022 11:27:06 +0300 Subject: [PATCH 768/798] update Pie widgets --- .../data/json/system/widget_bundles/charts.json | 4 ++-- .../home/components/widget/lib/flot-widget.ts | 16 ++++++++++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/application/src/main/data/json/system/widget_bundles/charts.json b/application/src/main/data/json/system/widget_bundles/charts.json index 2772c696bf..de32604dbb 100644 --- a/application/src/main/data/json/system/widget_bundles/charts.json +++ b/application/src/main/data/json/system/widget_bundles/charts.json @@ -54,7 +54,7 @@ }, { "alias": "pie", - "name": "Pie- Flot", + "name": "Pie - Flot", "image": "data:image/png;base64,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", "description": "Displays latest values of the attributes or timeseries data for multiple entities in a pie chart. Supports numeric values only.", "descriptor": { @@ -69,7 +69,7 @@ "dataKeySettingsSchema": "{}\n", "settingsDirective": "tb-flot-pie-widget-settings", "dataKeySettingsDirective": "tb-flot-pie-key-settings", - "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"First\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = (prevValue-50) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+50;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Second\",\"color\":\"#4caf50\",\"settings\":{},\"_hash\":0.6114638304362894,\"funcBody\":\"var value = (prevValue-20) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+20;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Third\",\"color\":\"#f44336\",\"settings\":{},\"_hash\":0.9955906536344441,\"funcBody\":\"var value = (prevValue-40) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+40;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Fourth\",\"color\":\"#ffc107\",\"settings\":{},\"_hash\":0.9430835931647599,\"funcBody\":\"var value = (prevValue-50) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+50;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"radius\":1,\"fontColor\":\"#545454\",\"fontSize\":10,\"decimals\":1,\"legend\":{\"show\":true,\"position\":\"nw\",\"labelBoxBorderColor\":\"#CCCCCC\",\"backgroundColor\":\"#F0F0F0\",\"backgroundOpacity\":0.85},\"innerRadius\":0,\"showLabels\":true,\"showPercentages\":true,\"stroke\":{\"width\":5},\"tilt\":1,\"animatedPie\":false},\"title\":\"Pie- Flot\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400}}" + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"First\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = (prevValue-50) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+50;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Second\",\"color\":\"#4caf50\",\"settings\":{},\"_hash\":0.6114638304362894,\"funcBody\":\"var value = (prevValue-20) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+20;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Third\",\"color\":\"#f44336\",\"settings\":{},\"_hash\":0.9955906536344441,\"funcBody\":\"var value = (prevValue-40) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+40;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Fourth\",\"color\":\"#ffc107\",\"settings\":{},\"_hash\":0.9430835931647599,\"funcBody\":\"var value = (prevValue-50) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+50;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"radius\":1,\"fontColor\":\"#545454\",\"fontSize\":10,\"decimals\":1,\"legend\":{\"show\":true,\"position\":\"nw\",\"labelBoxBorderColor\":\"#CCCCCC\",\"backgroundColor\":\"#F0F0F0\",\"backgroundOpacity\":0.85},\"innerRadius\":0,\"showLabels\":true,\"showPercentages\":true,\"stroke\":{\"width\":5},\"tilt\":1,\"animatedPie\":false},\"title\":\"Pie - Flot\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400}}" } }, { diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/flot-widget.ts b/ui-ngx/src/app/modules/home/components/widget/lib/flot-widget.ts index 979d60bb5b..fef3e487e9 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/flot-widget.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/flot-widget.ts @@ -312,6 +312,7 @@ export class TbFlot { if (this.settings.stroke) { this.options.series.pie.stroke.color = this.settings.stroke.color || '#fff'; this.options.series.pie.stroke.width = this.settings.stroke.width || 0; + this.scalingPieRadius(); } if (this.options.series.pie.label.show) { @@ -690,6 +691,16 @@ export class TbFlot { } } + private scalingPieRadius() { + // if (this.options.series.pie.stroke?.color !== '#fff' && this.options.series.pie.stroke?.width !== 0) { + let scalingLine; + this.ctx.width > this.ctx.height ? scalingLine = this.ctx.height : scalingLine = this.ctx.width; + let changeRadius = this.options.series.pie.stroke.width / scalingLine; + this.options.series.pie.radius = changeRadius < 1 ? this.settings.radius - changeRadius : 0; + console.log(this.options.series.pie.radius); + // } + } + public resize() { if (this.resizeTimeoutHandle) { clearTimeout(this.resizeTimeoutHandle); @@ -698,10 +709,15 @@ export class TbFlot { if (this.plot && this.plotInited) { const width = this.$element.width(); const height = this.$element.height(); + // if (this.chartType === 'pie') { + // this.scalingPieRadius(); + // } if (width && height) { this.plot.resize(); if (this.chartType !== 'pie') { this.plot.setupGrid(); + } else { + this.scalingPieRadius(); } this.plot.draw(); } else { From 3b70c334ccb63fa17626128674f89146bde25481 Mon Sep 17 00:00:00 2001 From: Viacheslav Klimov Date: Tue, 7 Jun 2022 13:04:37 +0300 Subject: [PATCH 769/798] Update 2fa tests --- .../server/controller/TwoFactorAuthConfigTest.java | 7 ++++++- .../thingsboard/server/controller/TwoFactorAuthTest.java | 6 ++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/application/src/test/java/org/thingsboard/server/controller/TwoFactorAuthConfigTest.java b/application/src/test/java/org/thingsboard/server/controller/TwoFactorAuthConfigTest.java index ee93cadaa4..db15e6fd82 100644 --- a/application/src/test/java/org/thingsboard/server/controller/TwoFactorAuthConfigTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/TwoFactorAuthConfigTest.java @@ -96,6 +96,7 @@ public abstract class TwoFactorAuthConfigTest extends AbstractControllerTest { PlatformTwoFaSettings twoFaSettings = new PlatformTwoFaSettings(); twoFaSettings.setProviders(List.of(totpTwoFaProviderConfig, smsTwoFaProviderConfig)); + twoFaSettings.setMinVerificationCodeSendPeriod(5); twoFaSettings.setVerificationCodeCheckRateLimit("3:900"); twoFaSettings.setMaxVerificationFailuresBeforeUserLockout(10); twoFaSettings.setTotalAllowedTimeForVerification(3600); @@ -117,6 +118,7 @@ public abstract class TwoFactorAuthConfigTest extends AbstractControllerTest { twoFaSettings.setVerificationCodeCheckRateLimit("0:12"); twoFaSettings.setMaxVerificationFailuresBeforeUserLockout(-1); twoFaSettings.setTotalAllowedTimeForVerification(0); + twoFaSettings.setMinVerificationCodeSendPeriod(5); String errorMessage = getErrorMessage(doPost("/api/2fa/settings", twoFaSettings) .andExpect(status().isBadRequest())); @@ -156,6 +158,8 @@ public abstract class TwoFactorAuthConfigTest extends AbstractControllerTest { private String savePlatformTwoFaSettingsAndGetError(TwoFaProviderConfig invalidTwoFaProviderConfig) throws Exception { PlatformTwoFaSettings twoFaSettings = new PlatformTwoFaSettings(); twoFaSettings.setProviders(Collections.singletonList(invalidTwoFaProviderConfig)); + twoFaSettings.setMinVerificationCodeSendPeriod(5); + twoFaSettings.setTotalAllowedTimeForVerification(100); return getErrorMessage(doPost("/api/2fa/settings", twoFaSettings) .andExpect(status().isBadRequest())); @@ -432,8 +436,9 @@ public abstract class TwoFactorAuthConfigTest extends AbstractControllerTest { private void saveProvidersConfigs(TwoFaProviderConfig... providerConfigs) throws Exception { PlatformTwoFaSettings twoFaSettings = new PlatformTwoFaSettings(); - twoFaSettings.setProviders(Arrays.stream(providerConfigs).collect(Collectors.toList())); + twoFaSettings.setMinVerificationCodeSendPeriod(5); + twoFaSettings.setTotalAllowedTimeForVerification(100); doPost("/api/2fa/settings", twoFaSettings).andExpect(status().isOk()); } diff --git a/application/src/test/java/org/thingsboard/server/controller/TwoFactorAuthTest.java b/application/src/test/java/org/thingsboard/server/controller/TwoFactorAuthTest.java index b825571694..9c30ebbbd0 100644 --- a/application/src/test/java/org/thingsboard/server/controller/TwoFactorAuthTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/TwoFactorAuthTest.java @@ -355,6 +355,8 @@ public abstract class TwoFactorAuthTest extends AbstractControllerTest { emailTwoFaProviderConfig.setVerificationCodeLifetime(60); platformTwoFaSettings.setProviders(List.of(totpTwoFaProviderConfig, smsTwoFaProviderConfig, emailTwoFaProviderConfig)); + platformTwoFaSettings.setMinVerificationCodeSendPeriod(5); + platformTwoFaSettings.setTotalAllowedTimeForVerification(100); twoFaConfigManager.savePlatformTwoFaSettings(TenantId.SYS_TENANT_ID, platformTwoFaSettings); User twoFaUser = new User(); @@ -409,6 +411,8 @@ public abstract class TwoFactorAuthTest extends AbstractControllerTest { PlatformTwoFaSettings twoFaSettings = new PlatformTwoFaSettings(); twoFaSettings.setProviders(Arrays.stream(new TwoFaProviderConfig[]{totpTwoFaProviderConfig}).collect(Collectors.toList())); + twoFaSettings.setMinVerificationCodeSendPeriod(5); + twoFaSettings.setTotalAllowedTimeForVerification(100); Arrays.stream(customizer).forEach(c -> c.accept(twoFaSettings)); twoFaConfigManager.savePlatformTwoFaSettings(TenantId.SYS_TENANT_ID, twoFaSettings); @@ -425,6 +429,8 @@ public abstract class TwoFactorAuthTest extends AbstractControllerTest { PlatformTwoFaSettings twoFaSettings = new PlatformTwoFaSettings(); twoFaSettings.setProviders(Arrays.stream(new TwoFaProviderConfig[]{smsTwoFaProviderConfig}).collect(Collectors.toList())); + twoFaSettings.setMinVerificationCodeSendPeriod(5); + twoFaSettings.setTotalAllowedTimeForVerification(100); twoFaConfigManager.savePlatformTwoFaSettings(TenantId.SYS_TENANT_ID, twoFaSettings); SmsTwoFaAccountConfig smsTwoFaAccountConfig = new SmsTwoFaAccountConfig(); From c2c59114dbea1f7a9c63ed309872a529773a51a0 Mon Sep 17 00:00:00 2001 From: fe-dev Date: Tue, 7 Jun 2022 13:07:57 +0300 Subject: [PATCH 770/798] UI: add phone input with country flags for sms authenticator --- .../sms-auth-dialog.component.html | 15 +-------------- .../shared/components/phone-input.component.html | 6 ++---- .../shared/components/phone-input.component.scss | 1 + .../shared/components/phone-input.component.ts | 16 ++++++---------- .../src/assets/locale/locale.constant-en_US.json | 3 ++- 5 files changed, 12 insertions(+), 29 deletions(-) diff --git a/ui-ngx/src/app/modules/home/pages/security/authentication-dialog/sms-auth-dialog.component.html b/ui-ngx/src/app/modules/home/pages/security/authentication-dialog/sms-auth-dialog.component.html index d4fb60095a..bb547f56a3 100644 --- a/ui-ngx/src/app/modules/home/pages/security/authentication-dialog/sms-auth-dialog.component.html +++ b/ui-ngx/src/app/modules/home/pages/security/authentication-dialog/sms-auth-dialog.component.html @@ -37,20 +37,7 @@

security.2fa.dialog.sms-step-description

- - - - - {{ 'admin.number-to-required' | translate }} - - - {{ 'admin.phone-number-pattern' | translate }} - - - +
- + phone-input.phone-input-label + {{ 'phone-input.phone-input-required' | translate }} {{ 'phone-input.phone-input-validation' | translate }} - - {{ 'phone-input.phone-input-pattern' | translate }} -
diff --git a/ui-ngx/src/app/shared/components/phone-input.component.scss b/ui-ngx/src/app/shared/components/phone-input.component.scss index 0145a13d62..872c686af0 100644 --- a/ui-ngx/src/app/shared/components/phone-input.component.scss +++ b/ui-ngx/src/app/shared/components/phone-input.component.scss @@ -21,6 +21,7 @@ .phone-input { width: 100%; + max-width: 290px; } } diff --git a/ui-ngx/src/app/shared/components/phone-input.component.ts b/ui-ngx/src/app/shared/components/phone-input.component.ts index 536b1cc723..bbee8098f6 100644 --- a/ui-ngx/src/app/shared/components/phone-input.component.ts +++ b/ui-ngx/src/app/shared/components/phone-input.component.ts @@ -33,6 +33,7 @@ import examples from 'libphonenumber-js/examples.mobile.json'; import { CountryCode, getExampleNumber, parsePhoneNumberFromString } from 'libphonenumber-js'; import { phoneNumberPattern } from '@shared/models/settings.models'; import { Subscription } from 'rxjs'; +import { FloatLabelType, MatFormFieldAppearance } from '@angular/material/form-field/form-field'; @Component({ selector: 'tb-phone-input', @@ -58,15 +59,8 @@ export class PhoneInputComponent implements OnInit, ControlValueAccessor, Valida @Input() defaultCountry: CountryCode = 'US'; @Input() enableFlagsSelect = true; @Input() required = true; - - private floatLabel = 'always'; - get showLabel(): string { - return this.floatLabel; - } - @Input() - set showLabel(value) { - this.floatLabel = value ? 'always' : 'never'; - } + @Input() floatLabel: FloatLabelType = 'always'; + @Input() appearance: MatFormFieldAppearance = 'legacy'; allCountries: Array = []; phonePlaceholder: string; @@ -134,6 +128,8 @@ export class PhoneInputComponent implements OnInit, ControlValueAccessor, Valida focus() { const phoneNumber = this.phoneFormGroup.get('phoneNumber'); + this.phoneFormGroup.markAsPristine(); + this.phoneFormGroup.markAsUntouched(); if (!phoneNumber.value) { phoneNumber.patchValue(this.countryCallingCode); } @@ -220,7 +216,7 @@ export class PhoneInputComponent implements OnInit, ControlValueAccessor, Valida private updateModel() { const phoneNumber = this.phoneFormGroup.get('phoneNumber'); - if (phoneNumber.valid && this.modelValue !== phoneNumber.value) { + if (phoneNumber.valid && phoneNumber.value) { this.modelValue = phoneNumber.value; this.propagateChange(this.modelValue); } else { diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index b4bd223ed4..d8a08a5677 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -4435,7 +4435,8 @@ "phone-input-label": "Phone number", "phone-input-required": "Phone number is required", "phone-input-validation": "Phone number is invalid or not possible", - "phone-input-pattern": "Invalid phone number. Should be in E.164 format, ex. {{phoneNumber}}" + "phone-input-pattern": "Invalid phone number. Should be in E.164 format, ex. {{phoneNumber}}", + "phone-input-hint": "Phone Number in E.164 format, ex. {{phoneNumber}}" }, "custom": { "widget-action": { From 676a2b645ade72c30f537424397ac1b1f9e35344 Mon Sep 17 00:00:00 2001 From: kalutkaz Date: Tue, 7 Jun 2022 13:26:04 +0300 Subject: [PATCH 771/798] deleteMinWidth --- .../date-range-navigator-panel.component.scss | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/date-range-navigator/date-range-navigator-panel.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/date-range-navigator/date-range-navigator-panel.component.scss index 036398d48e..9ed640d33d 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/date-range-navigator/date-range-navigator-panel.component.scss +++ b/ui-ngx/src/app/modules/home/components/widget/lib/date-range-navigator/date-range-navigator-panel.component.scss @@ -29,7 +29,6 @@ .mat-padding { padding: 16px; - min-width: 560px; } - + } From 25f3cf640a3774ac7d5b62c05122c4a1e14d9c82 Mon Sep 17 00:00:00 2001 From: kalutkaz Date: Thu, 9 Jun 2022 13:25:19 +0300 Subject: [PATCH 772/798] updated scaling radius object --- .../home/components/widget/lib/flot-widget.ts | 33 +++++++++---------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/flot-widget.ts b/ui-ngx/src/app/modules/home/components/widget/lib/flot-widget.ts index fef3e487e9..3c93d03e84 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/flot-widget.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/flot-widget.ts @@ -312,7 +312,9 @@ export class TbFlot { if (this.settings.stroke) { this.options.series.pie.stroke.color = this.settings.stroke.color || '#fff'; this.options.series.pie.stroke.width = this.settings.stroke.width || 0; - this.scalingPieRadius(); + if (this.options.series.pie.stroke.width) { + this.scalingPieRadius(); + } } if (this.options.series.pie.label.show) { @@ -692,13 +694,10 @@ export class TbFlot { } private scalingPieRadius() { - // if (this.options.series.pie.stroke?.color !== '#fff' && this.options.series.pie.stroke?.width !== 0) { let scalingLine; this.ctx.width > this.ctx.height ? scalingLine = this.ctx.height : scalingLine = this.ctx.width; let changeRadius = this.options.series.pie.stroke.width / scalingLine; this.options.series.pie.radius = changeRadius < 1 ? this.settings.radius - changeRadius : 0; - console.log(this.options.series.pie.radius); - // } } public resize() { @@ -707,21 +706,21 @@ export class TbFlot { this.resizeTimeoutHandle = null; } if (this.plot && this.plotInited) { - const width = this.$element.width(); - const height = this.$element.height(); - // if (this.chartType === 'pie') { - // this.scalingPieRadius(); - // } - if (width && height) { - this.plot.resize(); - if (this.chartType !== 'pie') { - this.plot.setupGrid(); - } else { + if (this.chartType === 'pie' && this.settings.stroke?.width) { this.scalingPieRadius(); - } - this.plot.draw(); + this.redrawPlot(); } else { - this.resizeTimeoutHandle = setTimeout(this.resize.bind(this), 30); + const width = this.$element.width(); + const height = this.$element.height(); + if (width && height) { + this.plot.resize(); + if (this.chartType !== 'pie') { + this.plot.setupGrid(); + } + this.plot.draw(); + } else { + this.resizeTimeoutHandle = setTimeout(this.resize.bind(this), 30); + } } } } From 863200d6e7b7ddffa0a6e42feaf3dcddccb739f1 Mon Sep 17 00:00:00 2001 From: fe-dev Date: Thu, 9 Jun 2022 18:02:31 +0300 Subject: [PATCH 773/798] UI: refactoring --- .../home/components/entity/entities-table.component.html | 2 +- .../home/components/entity/entities-table.component.ts | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/entity/entities-table.component.html b/ui-ngx/src/app/modules/home/components/entity/entities-table.component.html index c3350c63a4..5f023b2efb 100644 --- a/ui-ngx/src/app/modules/home/components/entity/entities-table.component.html +++ b/ui-ngx/src/app/modules/home/components/entity/entities-table.component.html @@ -262,7 +262,7 @@ class="no-data-found">{{ 'common.loading' | translate }}
- { - this.paginator.pageIndex = Number(params.page) || 0; - this.paginator.pageSize = Number(params.pageSize) || this.defaultPageSize; + if (this.displayPagination) { + this.paginator.pageIndex = Number(params.page) || 0; + this.paginator.pageSize = Number(params.pageSize) || this.defaultPageSize; + } this.sort.active = params.property || this.entitiesTableConfig.defaultSortOrder.property; this.sort.direction = (params.direction || this.entitiesTableConfig.defaultSortOrder.direction).toLowerCase() as SortDirection; if (params.hasOwnProperty('textSearch') && !isEmptyStr(params.textSearch)) { From fedd644516e58c1215640154e65ae6e904f385e6 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Thu, 9 Jun 2022 18:20:34 +0300 Subject: [PATCH 774/798] UI: move change password with profile to security --- ui-ngx/src/app/core/auth/auth.service.ts | 11 +- .../change-password-dialog.component.html | 64 --------- .../change-password-dialog.component.scss | 17 --- .../change-password-dialog.component.ts | 70 ---------- .../home/pages/profile/profile.component.html | 18 --- .../home/pages/profile/profile.component.scss | 6 - .../home/pages/profile/profile.component.ts | 55 +------- .../home/pages/profile/profile.module.ts | 4 +- .../pages/security/security.component.html | 115 +++++++++++++--- .../pages/security/security.component.scss | 55 ++++++-- .../home/pages/security/security.component.ts | 127 +++++++++++++++++- .../assets/locale/locale.constant-en_US.json | 21 ++- .../assets/locale/locale.constant-ru_RU.json | 1 - .../assets/locale/locale.constant-uk_UA.json | 1 - .../assets/locale/locale.constant-zh_CN.json | 1 - ui-ngx/src/theme.scss | 7 + 16 files changed, 305 insertions(+), 268 deletions(-) delete mode 100644 ui-ngx/src/app/modules/home/pages/profile/change-password-dialog.component.html delete mode 100644 ui-ngx/src/app/modules/home/pages/profile/change-password-dialog.component.scss delete mode 100644 ui-ngx/src/app/modules/home/pages/profile/change-password-dialog.component.ts diff --git a/ui-ngx/src/app/core/auth/auth.service.ts b/ui-ngx/src/app/core/auth/auth.service.ts index 527c4cb6de..e26d7813da 100644 --- a/ui-ngx/src/app/core/auth/auth.service.ts +++ b/ui-ngx/src/app/core/auth/auth.service.ts @@ -23,7 +23,7 @@ import { catchError, map, mergeMap, tap } from 'rxjs/operators'; import { LoginRequest, LoginResponse, PublicLoginRequest } from '@shared/models/login.models'; import { ActivatedRoute, Router, UrlTree } from '@angular/router'; -import { defaultHttpOptions } from '../http/http-utils'; +import { defaultHttpOptions, defaultHttpOptionsFromConfig, RequestConfig } from '../http/http-utils'; import { UserService } from '../http/user.service'; import { Store } from '@ngrx/store'; import { AppState } from '../core.state'; @@ -47,6 +47,7 @@ import { AlertDialogComponent } from '@shared/components/dialog/alert-dialog.com import { OAuth2ClientInfo, PlatformType } from '@shared/models/oauth2.models'; import { isMobileApp } from '@core/utils'; import { TwoFactorAuthProviderType, TwoFaProviderInfo } from '@shared/models/two-factor-auth.models'; +import { UserPasswordPolicy } from '@shared/models/settings.models'; @Injectable({ providedIn: 'root' @@ -163,14 +164,18 @@ export class AuthService { )); } - public changePassword(currentPassword: string, newPassword: string) { - return this.http.post('/api/auth/changePassword', {currentPassword, newPassword}, defaultHttpOptions()).pipe( + public changePassword(currentPassword: string, newPassword: string, config?: RequestConfig) { + return this.http.post('/api/auth/changePassword', {currentPassword, newPassword}, defaultHttpOptionsFromConfig(config)).pipe( tap((loginResponse: LoginResponse) => { this.setUserFromJwtToken(loginResponse.token, loginResponse.refreshToken, false); } )); } + public getUserPasswordPolicy() { + return this.http.get(`/api/noauth/userPasswordPolicy`, defaultHttpOptions()); + } + public activateByEmailCode(emailCode: string): Observable { return this.http.post(`/api/noauth/activateByEmailCode?emailCode=${emailCode}`, null, defaultHttpOptions()); diff --git a/ui-ngx/src/app/modules/home/pages/profile/change-password-dialog.component.html b/ui-ngx/src/app/modules/home/pages/profile/change-password-dialog.component.html deleted file mode 100644 index b5f6d81459..0000000000 --- a/ui-ngx/src/app/modules/home/pages/profile/change-password-dialog.component.html +++ /dev/null @@ -1,64 +0,0 @@ - -
- -

profile.change-password

- - -
- - -
-
- - profile.current-password - - lock - - - - login.new-password - - lock - - - - login.new-password-again - - lock - - -
-
- - -
- diff --git a/ui-ngx/src/app/modules/home/pages/profile/change-password-dialog.component.scss b/ui-ngx/src/app/modules/home/pages/profile/change-password-dialog.component.scss deleted file mode 100644 index 2d8406456a..0000000000 --- a/ui-ngx/src/app/modules/home/pages/profile/change-password-dialog.component.scss +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Copyright © 2016-2022 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. - */ -:host { -} diff --git a/ui-ngx/src/app/modules/home/pages/profile/change-password-dialog.component.ts b/ui-ngx/src/app/modules/home/pages/profile/change-password-dialog.component.ts deleted file mode 100644 index 782bcb0f4d..0000000000 --- a/ui-ngx/src/app/modules/home/pages/profile/change-password-dialog.component.ts +++ /dev/null @@ -1,70 +0,0 @@ -/// -/// Copyright © 2016-2022 The Thingsboard Authors -/// -/// Licensed under the Apache License, Version 2.0 (the "License"); -/// you may not use this file except in compliance with the License. -/// You may obtain a copy of the License at -/// -/// http://www.apache.org/licenses/LICENSE-2.0 -/// -/// Unless required by applicable law or agreed to in writing, software -/// distributed under the License is distributed on an "AS IS" BASIS, -/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -/// See the License for the specific language governing permissions and -/// limitations under the License. -/// - -import { Component, OnInit } from '@angular/core'; -import { MatDialogRef } from '@angular/material/dialog'; -import { Store } from '@ngrx/store'; -import { AppState } from '@core/core.state'; -import { FormBuilder, FormGroup } from '@angular/forms'; -import { ActionNotificationShow } from '@core/notification/notification.actions'; -import { TranslateService } from '@ngx-translate/core'; -import { AuthService } from '@core/auth/auth.service'; -import { DialogComponent } from '@shared/components/dialog.component'; -import { Router } from '@angular/router'; - -@Component({ - selector: 'tb-change-password-dialog', - templateUrl: './change-password-dialog.component.html', - styleUrls: ['./change-password-dialog.component.scss'] -}) -export class ChangePasswordDialogComponent extends DialogComponent implements OnInit { - - changePassword: FormGroup; - - constructor(protected store: Store, - protected router: Router, - private translate: TranslateService, - private authService: AuthService, - public dialogRef: MatDialogRef, - public fb: FormBuilder) { - super(store, router, dialogRef); - } - - ngOnInit(): void { - this.buildChangePasswordForm(); - } - - buildChangePasswordForm() { - this.changePassword = this.fb.group({ - currentPassword: [''], - newPassword: [''], - newPassword2: [''] - }); - } - - onChangePassword(): void { - if (this.changePassword.get('newPassword').value !== this.changePassword.get('newPassword2').value) { - this.store.dispatch(new ActionNotificationShow({ message: this.translate.instant('login.passwords-mismatch-error'), - type: 'error' })); - } else { - this.authService.changePassword( - this.changePassword.get('currentPassword').value, - this.changePassword.get('newPassword').value).subscribe(() => { - this.dialogRef.close(true); - }); - } - } -} diff --git a/ui-ngx/src/app/modules/home/pages/profile/profile.component.html b/ui-ngx/src/app/modules/home/pages/profile/profile.component.html index 7300f7ea7c..3a15616055 100644 --- a/ui-ngx/src/app/modules/home/pages/profile/profile.component.html +++ b/ui-ngx/src/app/modules/home/pages/profile/profile.component.html @@ -78,24 +78,6 @@ {{ 'dashboard.home-dashboard-hide-toolbar' | translate }} -
-
- -
-
- -
{{ expirationJwtData }}
-
-
-
{{ expirationJwtData }}
+ + +
+
+
+

profile.change-password

+ + profile.current-password + + + + {{ 'security.password-requirement.incorrect-password-try-again' | translate }} + + + + login.new-password + + + + {{ 'security.password-requirement.password-not-meet-requirements' | translate }} + + + {{ changePassword.get('newPassword').getError('alreadyUsed') }} + + + {{ 'security.password-requirement.password-should-difference' | translate }} + + + {{ 'security.password-requirement.password-should-not-contain-spaces' | translate }} + + +
+ +
+ + login.new-password-again + + + + {{ 'security.password-requirement.new-passwords-not-match' | translate }} + + +
+ +
+ +
+
+ +
+

security.password-requirement.password-requirements

+

security.password-requirement.at-least

+

+ + {{ 'security.password-requirement.uppercase-letter' | translate : {count: passwordPolicy.minimumUppercaseLetters} }} +

+

+ + {{ 'security.password-requirement.lowercase-letter' | translate : {count: passwordPolicy.minimumLowercaseLetters} }} +

+

+ + {{ 'security.password-requirement.digit' | translate : {count: passwordPolicy.minimumDigits} }} +

+

+ + {{ 'security.password-requirement.special-character' | translate : {count: passwordPolicy.minimumSpecialCharacters} }} +

+

+ + {{ 'security.password-requirement.character' | translate : {count: passwordPolicy.minimumLength} }} +

+
+
+
+ + +
+ +
+
- admin.2fa.2fa + admin.2fa.2fa
security.2fa.2fa-description
diff --git a/ui-ngx/src/app/modules/home/pages/security/security.component.scss b/ui-ngx/src/app/modules/home/pages/security/security.component.scss index 7dad927038..16f19e09bf 100644 --- a/ui-ngx/src/app/modules/home/pages/security/security.component.scss +++ b/ui-ngx/src/app/modules/home/pages/security/security.component.scss @@ -21,6 +21,7 @@ } mat-card.profile-card { + padding: 24px; @media #{$mat-gt-sm} { width: 70%; } @@ -31,23 +32,54 @@ width: 45%; } - .mat-subheader { - line-height: 24px; - color: rgba(0, 0, 0, 0.54); + .card-title { + font: 500 18px / 24px Roboto, "Helvetica Neue", sans-serif; + letter-spacing: 0.15px; + margin-top: 0; + } + + .mat-h4 { + font-weight: 500; font-size: 14px; - font-weight: 400; + letter-spacing: 0.25px; + margin: 0 0 4px; + } + + .change-password { + margin: 0; + + .mat-divider.mat-divider-vertical { + margin-bottom: 25px; + } + + .mat-form-field { + margin-bottom: 4px; + } + + .password-requirements > p { + margin: 0 0 8px; + letter-spacing: 0.25px; + color: rgba(0, 0, 0, 0.87); + } + + .mat-icon[data-mat-icon-name="check"] { + color: #24A148; + } } - .profile-last-login-ts { - font-size: 16px; - font-weight: 400; + .auth-title { + font-weight: 500; + margin: 0; } - .profile-btn-subtext { + .token-text { font: 400 14px / 16px Roboto, "Helvetica Neue", sans-serif; letter-spacing: 0.25px; - opacity: 0.6; padding: 8px 0; + + > .date { + opacity: .7; + } } } @@ -91,3 +123,8 @@ } } } +:host ::ng-deep { + .mat-form-field-appearance-fill .mat-form-field-underline::before { + background-color: transparent; + } +} diff --git a/ui-ngx/src/app/modules/home/pages/security/security.component.ts b/ui-ngx/src/app/modules/home/pages/security/security.component.ts index ab7d470e9e..8b723a0ab5 100644 --- a/ui-ngx/src/app/modules/home/pages/security/security.component.ts +++ b/ui-ngx/src/app/modules/home/pages/security/security.component.ts @@ -19,7 +19,15 @@ import { User } from '@shared/models/user.model'; import { PageComponent } from '@shared/components/page.component'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; -import { FormBuilder, FormGroup } from '@angular/forms'; +import { + AbstractControl, + FormBuilder, + FormGroup, FormGroupDirective, + NgForm, + ValidationErrors, + ValidatorFn, + Validators +} from '@angular/forms'; import { TranslateService } from '@ngx-translate/core'; import { MatDialog } from '@angular/material/dialog'; import { DialogService } from '@core/services/dialog.service'; @@ -39,7 +47,9 @@ import { import { authenticationDialogMap } from '@home/pages/security/authentication-dialog/authentication-dialog.map'; import { takeUntil, tap } from 'rxjs/operators'; import { Observable, of, Subject } from 'rxjs'; -import { isDefinedAndNotNull } from '@core/utils'; +import { isDefinedAndNotNull, isEqual } from '@core/utils'; +import { AuthService } from '@core/auth/auth.service'; +import { UserPasswordPolicy } from '@shared/models/settings.models'; @Component({ selector: 'tb-security', @@ -52,7 +62,11 @@ export class SecurityComponent extends PageComponent implements OnInit, OnDestro private accountConfig: AccountTwoFaSettings; twoFactorAuth: FormGroup; + changePassword: FormGroup; + user: User; + passwordPolicy: UserPasswordPolicy; + allowTwoFactorProviders: TwoFactorAuthProviderType[] = []; providersData = twoFactorAuthProvidersData; twoFactorAuthProviderType = TwoFactorAuthProviderType; @@ -80,6 +94,7 @@ export class SecurityComponent extends PageComponent implements OnInit, OnDestro public dialogService: DialogService, public fb: FormBuilder, private datePipe: DatePipe, + private authService: AuthService, private clipboardService: ClipboardService) { super(store); } @@ -88,6 +103,8 @@ export class SecurityComponent extends PageComponent implements OnInit, OnDestro this.buildTwoFactorForm(); this.user = this.route.snapshot.data.user; this.twoFactorLoad(this.route.snapshot.data.providers); + this.buildChangePasswordForm(); + this.loadPasswordPolicy(); } ngOnDestroy() { @@ -142,6 +159,75 @@ export class SecurityComponent extends PageComponent implements OnInit, OnDestro }); } + private buildChangePasswordForm() { + this.changePassword = this.fb.group({ + currentPassword: [''], + newPassword: ['', Validators.required], + newPassword2: ['', this.samePasswordValidation(false, 'newPassword')] + }); + } + + private loadPasswordPolicy() { + this.authService.getUserPasswordPolicy().subscribe(policy => { + this.passwordPolicy = policy; + this.changePassword.get('newPassword').setValidators([ + this.passwordStrengthValidator(), + this.samePasswordValidation(true, 'currentPassword'), + Validators.required + ]); + this.changePassword.get('newPassword').updateValueAndValidity({emitEvent: false}); + }); + } + + private passwordStrengthValidator(): ValidatorFn { + return (control: AbstractControl): ValidationErrors | null => { + const value: string = control.value; + const errors: any = {}; + + if (this.passwordPolicy.minimumUppercaseLetters > 0 && + !new RegExp(`(?:.*?[A-Z]){${this.passwordPolicy.minimumUppercaseLetters}}`).test(value)) { + errors.notUpperCase = true; + } + + if (this.passwordPolicy.minimumLowercaseLetters > 0 && + !new RegExp(`(?:.*?[a-z]){${this.passwordPolicy.minimumLowercaseLetters}}`).test(value)) { + errors.notLowerCase = true; + } + + if (this.passwordPolicy.minimumDigits > 0 + && !new RegExp(`(?:.*?\\d){${this.passwordPolicy.minimumDigits}}`).test(value)) { + errors.notNumeric = true; + } + + if (this.passwordPolicy.minimumSpecialCharacters > 0 && + !new RegExp(`(?:.*?[\\W_]){${this.passwordPolicy.minimumSpecialCharacters}}`).test(value)) { + errors.notSpecial = true; + } + + if (!this.passwordPolicy.allowWhitespaces && /\s/.test(value)) { + errors.hasWhitespaces = true; + } + + if (this.passwordPolicy.minimumLength > 0 && value.length < this.passwordPolicy.minimumLength) { + errors.minLength = true; + } + + return isEqual(errors, {}) ? null : errors; + }; + } + + private samePasswordValidation(isSame: boolean, key: string): ValidatorFn { + return (control: AbstractControl): ValidationErrors | null => { + const value: string = control.value; + const keyValue = control.parent?.value[key]; + + if (isSame) { + return value === keyValue ? {samePassword: true} : null; + } + return value !== keyValue ? {differencePassword: true} : null; + }; + } + trackByProvider(i: number, provider: TwoFactorAuthProviderType) { return provider; } @@ -256,4 +342,41 @@ export class SecurityComponent extends PageComponent implements OnInit, OnDestro } return info; } + + onChangePassword(form: FormGroupDirective): void { + if (this.changePassword.valid) { + this.authService.changePassword(this.changePassword.get('currentPassword').value, + this.changePassword.get('newPassword').value, {ignoreErrors: true}).subscribe(() => { + this.discardChanges(form); + }, + (error) => { + if (error.status === 400 && error.error.message === 'Current password doesn\'t match!') { + this.changePassword.get('currentPassword').setErrors({differencePassword: true}); + } else if (error.status === 400 && error.error.message.startsWith('Password must')) { + this.loadPasswordPolicy(); + } else if (error.status === 400 && error.error.message.startsWith('Password was already used')) { + this.changePassword.get('newPassword').setErrors({alreadyUsed: error.error.message}); + } else { + this.store.dispatch(new ActionNotificationShow({ + message: error.error.message, + type: 'error', + target: 'changePassword' + })); + } + }); + } else { + this.changePassword.markAllAsTouched(); + } + } + + discardChanges(form: FormGroupDirective, event?: MouseEvent) { + if (event) { + event.stopPropagation(); + } + form.resetForm({ + currentPassword: '', + newPassword: '', + newPassword2: '' + }); + } } diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index 2ae0b55916..31d5229fa6 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -2498,7 +2498,7 @@ "password-reset": "Password reset", "expired-password-reset-message": "Your credentials has been expired! Please create new password.", "new-password": "New password", - "new-password-again": "New password again", + "new-password-again": "Confirm new password", "password-link-sent-message": "Reset link has been sent", "email": "Email", "login-with": "Login with {{name}}", @@ -2601,12 +2601,13 @@ "change-password": "Change Password", "current-password": "Current password", "copy-jwt-token": "Copy JWT token", - "valid-till": "Valid till {{expirationData}}", + "jwt-token": "JWT token", + "token-valid-till": "Token is valid till", "tokenCopiedSuccessMessage": "JWT token has been copied to clipboard", "tokenCopiedWarnMessage": "JWT token is expired! Please, refresh the page." }, "security": { - "security": "Security", + "security": "Password and authentication", "2fa": { "2fa": "Two-factor authentication", "2fa-description": "Two-factor authentication protects your account from unauthorized access. All you have to do is enter a security code when you log in.", @@ -2660,6 +2661,20 @@ "backup-code-description": "These printable one-time passcodes allow you to sign in when away from your phone, like when you’re traveling.", "backup-code-hint": "{{ info }} single-use codes are active at this time" } + }, + "password-requirement": { + "at-least": "At least:", + "character": "{ count, plural, 1 {1 character} other {# characters} }", + "digit": "{ count, plural, 1 {1 digit} other {# digits} }", + "incorrect-password-try-again": "Incorrect password. Try again", + "lowercase-letter": "{ count, plural, 1 {1 lowercase letter} other {# lowercase letters} }", + "new-passwords-not-match": "New password didn't match", + "password-should-not-contain-spaces": "Your password should not contain spaces", + "password-not-meet-requirements": "Password didn't meet requirements", + "password-requirements": "Password requirements", + "password-should-difference": "New password should be different from current", + "special-character": "{ count, plural, 1 {1 special character} other {# special characters} }", + "uppercase-letter": "{ count, plural, 1 {1 uppercase letter} other {# uppercase letters} }" } }, "relation": { diff --git a/ui-ngx/src/assets/locale/locale.constant-ru_RU.json b/ui-ngx/src/assets/locale/locale.constant-ru_RU.json index a4e894e59e..b576974b9f 100644 --- a/ui-ngx/src/assets/locale/locale.constant-ru_RU.json +++ b/ui-ngx/src/assets/locale/locale.constant-ru_RU.json @@ -1289,7 +1289,6 @@ "change-password": "Изменить пароль", "current-password": "Текущий пароль", "copy-jwt-token": "Копировать JWT токен", - "valid-till": "Действителен до {{expirationData}}", "tokenCopiedMessage": "JWT токен скопирован в буфер обмена", "tokenCopiedWarnMessage": "JWT токен недействителен! Перезагрузите страницу." }, diff --git a/ui-ngx/src/assets/locale/locale.constant-uk_UA.json b/ui-ngx/src/assets/locale/locale.constant-uk_UA.json index eff83577fd..c624175bec 100644 --- a/ui-ngx/src/assets/locale/locale.constant-uk_UA.json +++ b/ui-ngx/src/assets/locale/locale.constant-uk_UA.json @@ -1704,7 +1704,6 @@ "change-password": "Змінити пароль", "current-password": "Поточний пароль", "copy-jwt-token": "Копіювати JWT токен", - "valid-till": "Дійсний до {{expirationData}}", "tokenCopiedMessage": "JWT токен скопійовано в буфер обміну", "tokenCopiedWarnMessage": "JWT токен не є дійсним! Перезавантажте сторінку." }, diff --git a/ui-ngx/src/assets/locale/locale.constant-zh_CN.json b/ui-ngx/src/assets/locale/locale.constant-zh_CN.json index 11e34c9102..f4b6dd420f 100644 --- a/ui-ngx/src/assets/locale/locale.constant-zh_CN.json +++ b/ui-ngx/src/assets/locale/locale.constant-zh_CN.json @@ -2009,7 +2009,6 @@ "last-login-time": "最后登录", "profile": "属性", "copy-jwt-token": "复制 JWT 令牌", - "valid-till": "有效期至 {{expirationData}}", "tokenCopiedSuccessMessage": "JWT 令牌已复制到剪贴板", "tokenCopiedWarnMessage": "JWT 令牌已过期!请刷新页面。" }, diff --git a/ui-ngx/src/theme.scss b/ui-ngx/src/theme.scss index e0aac57674..cdf4d611cd 100644 --- a/ui-ngx/src/theme.scss +++ b/ui-ngx/src/theme.scss @@ -221,6 +221,7 @@ $tb-dark-theme: get-tb-dark-theme( @mixin tb-components-theme($theme) { $primary: map-get($theme, primary); + $warn: map-get($theme, warn); mat-toolbar{ &.mat-hue-3 { @@ -233,6 +234,12 @@ $tb-dark-theme: get-tb-dark-theme( div.tb-dashboard-page.mobile-app { @include mat-fab-toolbar-inverse-theme($tb-theme); } + + .same-color.mat-form-field-invalid { + .mat-form-field-suffix { + color: mat.get-color-from-palette($warn, text); + } + } } .tb-default { From dbbd8cf9ce37320a1aeaf0bb4e4979f5573be173 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Fri, 10 Jun 2022 10:09:59 +0300 Subject: [PATCH 775/798] UI: Imporvement mobile view security page --- .../app/modules/home/pages/security/security.component.scss | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui-ngx/src/app/modules/home/pages/security/security.component.scss b/ui-ngx/src/app/modules/home/pages/security/security.component.scss index 16f19e09bf..b593be3a2a 100644 --- a/ui-ngx/src/app/modules/home/pages/security/security.component.scss +++ b/ui-ngx/src/app/modules/home/pages/security/security.component.scss @@ -23,10 +23,10 @@ mat-card.profile-card { padding: 24px; @media #{$mat-gt-sm} { - width: 70%; + width: 80%; } @media #{$mat-gt-md} { - width: 50%; + width: 55%; } @media #{$mat-gt-xl} { width: 45%; From eeb6ab6a6076c5f2481f00fa01d5897c781daba0 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Fri, 10 Jun 2022 10:34:31 +0300 Subject: [PATCH 776/798] UI: Refactoring code style --- .../login/two-factor-auth-login.component.ts | 15 +++++++-------- .../app/shared/models/two-factor-auth.models.ts | 4 ++-- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/ui-ngx/src/app/modules/login/pages/login/two-factor-auth-login.component.ts b/ui-ngx/src/app/modules/login/pages/login/two-factor-auth-login.component.ts index 89105cd18e..2a98f0b530 100644 --- a/ui-ngx/src/app/modules/login/pages/login/two-factor-auth-login.component.ts +++ b/ui-ngx/src/app/modules/login/pages/login/two-factor-auth-login.component.ts @@ -29,7 +29,7 @@ import { import { TranslateService } from '@ngx-translate/core'; import { interval, Subscription } from 'rxjs'; import { isEqual } from '@core/utils'; -import {ActionNotificationShow} from "@core/notification/notification.actions"; +import { ActionNotificationShow } from '@core/notification/notification.actions'; @Component({ selector: 'tb-two-factor-auth-login', @@ -120,13 +120,12 @@ export class TwoFactorAuthLoginComponent extends PageComponent implements OnInit this.verificationForm.get('verificationCode').setErrors(errors); }, 5000); } else { - this.store.dispatch(new ActionNotificationShow( - { - message: error.error.message, - type: 'error', - verticalPosition: 'top', - horizontalPosition: 'left' - })); + this.store.dispatch(new ActionNotificationShow({ + message: error.error.message, + type: 'error', + verticalPosition: 'top', + horizontalPosition: 'left' + })); } } ); diff --git a/ui-ngx/src/app/shared/models/two-factor-auth.models.ts b/ui-ngx/src/app/shared/models/two-factor-auth.models.ts index d7961050ed..d3b2a74fbd 100644 --- a/ui-ngx/src/app/shared/models/two-factor-auth.models.ts +++ b/ui-ngx/src/app/shared/models/two-factor-auth.models.ts @@ -93,8 +93,8 @@ export interface AccountTwoFaSettings { } export type AccountTwoFaSettingProviders = { - [key in TwoFactorAuthProviderType]?: TwoFactorAuthAccountConfig; -} + [key in TwoFactorAuthProviderType]?: TwoFactorAuthAccountConfig; +}; export interface TwoFaProviderInfo { type: TwoFactorAuthProviderType; From fd53c24346f84d05b7114f918d7664afab4d0408 Mon Sep 17 00:00:00 2001 From: dlandiak Date: Fri, 10 Jun 2022 13:24:20 +0300 Subject: [PATCH 777/798] fixing slow queries logging --- .../server/dao/sql/query/DefaultEntityQueryRepository.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/query/DefaultEntityQueryRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/query/DefaultEntityQueryRepository.java index f4fa052648..23b5b12d7c 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/query/DefaultEntityQueryRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/query/DefaultEntityQueryRepository.java @@ -369,7 +369,7 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { try { return jdbcTemplate.queryForObject(countQuery, ctx, Long.class); } finally { - queryLog.logQuery(ctx, ctx.getQuery(), System.currentTimeMillis() - startTs); + queryLog.logQuery(ctx, countQuery, System.currentTimeMillis() - startTs); } }); } @@ -481,7 +481,7 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { try { rows = jdbcTemplate.queryForList(dataQuery, ctx); } finally { - queryLog.logQuery(ctx, countQuery, System.currentTimeMillis() - startTs); + queryLog.logQuery(ctx, dataQuery, System.currentTimeMillis() - startTs); } return EntityDataAdapter.createEntityData(pageLink, selectionMapping, rows, totalElements); }); From 9c3febf6641c9c613846e5f4edace20816ec7cfd Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Fri, 10 Jun 2022 13:49:51 +0300 Subject: [PATCH 778/798] UI: Fixed not correct work vertical resize in knob widget --- .../home/components/widget/lib/rpc/knob.component.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/knob.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/knob.component.html index 0e396d1b07..58b7d7880c 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/knob.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/knob.component.html @@ -15,8 +15,8 @@ limitations under the License. --> -
-
+
+
{{ value }} From db840bf4d88f28e3b8adaf18da49bd8d6caaaa94 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Fri, 10 Jun 2022 15:45:26 +0300 Subject: [PATCH 779/798] UI: Fixed incorrect show edit widget panel when active add widget panel in dashboard page --- .../components/dashboard-page/dashboard-page.component.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-page.component.ts b/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-page.component.ts index c360846941..f9ec50504f 100644 --- a/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-page.component.ts +++ b/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-page.component.ts @@ -1127,6 +1127,13 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC editWidget($event: Event, layoutCtx: DashboardPageLayoutContext, widget: Widget) { $event.stopPropagation(); + + if (this.isAddingWidget) { + this.onAddWidgetClosed(); + this.isAddingWidgetClosed = true; + this.isEditingWidgetClosed = false; + } + if (this.editingWidgetOriginal === widget) { this.onEditWidgetClosed(); } else { From bb8e6a04439bbb0ac56ac99279f2b799e376d450 Mon Sep 17 00:00:00 2001 From: Volodymyr Babak Date: Fri, 10 Jun 2022 17:32:12 +0300 Subject: [PATCH 780/798] Fixed bug with incorrect display of delayed status --- .../modules/home/components/edge/edge-downlink-table-config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui-ngx/src/app/modules/home/components/edge/edge-downlink-table-config.ts b/ui-ngx/src/app/modules/home/components/edge/edge-downlink-table-config.ts index 2ddc6637b3..2e3b55f32d 100644 --- a/ui-ngx/src/app/modules/home/components/edge/edge-downlink-table-config.ts +++ b/ui-ngx/src/app/modules/home/components/edge/edge-downlink-table-config.ts @@ -138,7 +138,7 @@ export class EdgeDownlinkTableConfig extends EntityTableConfig Date: Fri, 10 Jun 2022 17:38:18 +0300 Subject: [PATCH 781/798] UI: Fix bug with filter and with entity name --- .../widget/lib/alarms-table-widget.component.ts | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/alarms-table-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/alarms-table-widget.component.ts index 03379a54e0..2412f59f5f 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/alarms-table-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/alarms-table-widget.component.ts @@ -290,9 +290,7 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit, debounceTime(150), distinctUntilChanged(), tap(() => { - if (this.displayPagination) { - this.paginator.pageIndex = 0; - } + this.resetPageIndex(); this.updateData(); }) ) @@ -556,6 +554,12 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit, this.ctx.detectChanges(); } + private resetPageIndex(): void { + if (this.displayPagination) { + this.paginator.pageIndex = 0; + } + } + private editAlarmFilter($event: Event) { if ($event) { $event.stopPropagation(); @@ -600,6 +604,7 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit, this.pageLink.statusList = result.statusList; this.pageLink.severityList = result.severityList; this.pageLink.typeList = result.typeList; + this.resetPageIndex(); this.updateData(); } }); @@ -620,9 +625,7 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit, exitFilterMode() { this.textSearchMode = false; this.pageLink.textSearch = null; - if (this.displayPagination) { - this.paginator.pageIndex = 0; - } + this.resetPageIndex(); this.updateData(); this.ctx.hideTitlePanel = false; this.ctx.detectChanges(true); @@ -959,7 +962,7 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit, } else if (alarmField.value === alarmFields.severity.value) { return this.translate.instant(alarmSeverityTranslations.get(value)); } else if (alarmField.value === alarmFields.status.value) { - return this.translate.instant(alarmStatusTranslations.get(value)); + return alarmStatusTranslations.get(value) ? this.translate.instant(alarmStatusTranslations.get(value)) : value; } else if (alarmField.value === alarmFields.originatorType.value) { return this.translate.instant(entityTypeTranslations.get(value).type); } else { From 0cd24bb16e26e22691f21e391f5634f4f27ac2bb Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Sun, 12 Jun 2022 20:28:12 +0300 Subject: [PATCH 782/798] refactoring: alarm - test start --- .../server/controller/AlarmController.java | 2 +- .../DefaultTbNotificationEntityService.java | 26 +--- .../entitiy/alarm/DefaultTbAlarmService.java | 10 +- .../service/entitiy/alarm/TbAlarmService.java | 3 +- .../controller/AbstractControllerTest.java | 2 +- .../controller/AbstractNotifyEntityTest.java | 132 ++++++++++++++++++ .../server/controller/AbstractWebTest.java | 11 +- .../controller/BaseAlarmControllerTest.java | 119 +++++++++++++++- .../profile/TbDeviceProfileNodeTest.java | 8 +- 9 files changed, 269 insertions(+), 44 deletions(-) create mode 100644 application/src/test/java/org/thingsboard/server/controller/AbstractNotifyEntityTest.java diff --git a/application/src/main/java/org/thingsboard/server/controller/AlarmController.java b/application/src/main/java/org/thingsboard/server/controller/AlarmController.java index 939e9da8a0..2651277ccc 100644 --- a/application/src/main/java/org/thingsboard/server/controller/AlarmController.java +++ b/application/src/main/java/org/thingsboard/server/controller/AlarmController.java @@ -147,7 +147,7 @@ public class AlarmController extends BaseController { try { AlarmId alarmId = new AlarmId(toUUID(strAlarmId)); Alarm alarm = checkAlarmId(alarmId, Operation.WRITE); - return tbAlarmService.delete(alarm, getCurrentUser()); + return tbAlarmService.delete(alarm, getCurrentUser().getCustomerId(), getCurrentUser()); } catch (Exception e) { throw handleException(e); } diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/DefaultTbNotificationEntityService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/DefaultTbNotificationEntityService.java index 3361b75d23..707747ae91 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/DefaultTbNotificationEntityService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/DefaultTbNotificationEntityService.java @@ -52,6 +52,7 @@ import org.thingsboard.server.service.gateway_device.GatewayNotificationsService import org.thingsboard.server.service.security.model.SecurityUser; import java.util.List; +import java.util.Locale; @Slf4j @Service @@ -335,28 +336,7 @@ public class DefaultTbNotificationEntityService implements TbNotificationEntityS return null; } - private EdgeEventActionType edgeTypeByActionType(ActionType actionType) { - switch (actionType) { - case ADDED: - return EdgeEventActionType.ADDED; - case UPDATED: - return EdgeEventActionType.UPDATED; - case ALARM_ACK: - return EdgeEventActionType.ALARM_ACK; - case ALARM_CLEAR: - return EdgeEventActionType.ALARM_CLEAR; - case DELETED: - return EdgeEventActionType.DELETED; - case RELATION_ADD_OR_UPDATE: - return EdgeEventActionType.RELATION_ADD_OR_UPDATE; - case RELATION_DELETED: - return EdgeEventActionType.RELATION_DELETED; - case ASSIGNED_TO_EDGE: - return EdgeEventActionType.ASSIGNED_TO_EDGE; - case UNASSIGNED_FROM_EDGE: - return EdgeEventActionType.UNASSIGNED_FROM_EDGE; - default: - return null; - } + public static EdgeEventActionType edgeTypeByActionType(ActionType actionType) { + return EdgeEventActionType.valueOf(actionType.toString().toUpperCase(Locale.ENGLISH)); } } diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/alarm/DefaultTbAlarmService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/alarm/DefaultTbAlarmService.java index 4930f54a5d..305fda2b2b 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/alarm/DefaultTbAlarmService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/alarm/DefaultTbAlarmService.java @@ -23,6 +23,7 @@ import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.alarm.AlarmStatus; import org.thingsboard.server.common.data.audit.ActionType; 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.TenantId; import org.thingsboard.server.queue.util.TbCoreComponent; @@ -77,12 +78,13 @@ public class DefaultTbAlarmService extends AbstractTbEntityService implements Tb } @Override - public Boolean delete(Alarm alarm, SecurityUser user) throws ThingsboardException { + public Boolean delete(Alarm alarm, CustomerId customerId, SecurityUser user) throws ThingsboardException { + TenantId tenantId = alarm.getTenantId(); try { - List relatedEdgeIds = findRelatedEdgeIds(user.getTenantId(), alarm.getOriginator()); - notificationEntityService.notifyDeleteAlarm(user.getTenantId(), alarm, alarm.getOriginator(), user.getCustomerId(), + List relatedEdgeIds = findRelatedEdgeIds(tenantId, alarm.getOriginator()); + notificationEntityService.notifyDeleteAlarm(tenantId, alarm, alarm.getOriginator(), customerId, relatedEdgeIds, user, JacksonUtil.OBJECT_MAPPER.writeValueAsString(alarm)); - return alarmService.deleteAlarm(user.getTenantId(), alarm.getId()).isSuccessful(); + return alarmService.deleteAlarm(tenantId, alarm.getId()).isSuccessful(); } catch (Exception e) { throw handleException(e); } diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/alarm/TbAlarmService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/alarm/TbAlarmService.java index 7a6d7f6a81..f17fcb3b70 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/alarm/TbAlarmService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/alarm/TbAlarmService.java @@ -17,6 +17,7 @@ package org.thingsboard.server.service.entitiy.alarm; import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.exception.ThingsboardException; +import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.service.security.model.SecurityUser; public interface TbAlarmService { @@ -27,5 +28,5 @@ public interface TbAlarmService { void clear(Alarm alarm, SecurityUser user) throws ThingsboardException; - Boolean delete(Alarm alarm, SecurityUser user) throws ThingsboardException; + Boolean delete(Alarm alarm, CustomerId customerId, SecurityUser user) throws ThingsboardException; } diff --git a/application/src/test/java/org/thingsboard/server/controller/AbstractControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/AbstractControllerTest.java index 98e4c12759..ed8d8632da 100644 --- a/application/src/test/java/org/thingsboard/server/controller/AbstractControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/AbstractControllerTest.java @@ -45,7 +45,7 @@ import static org.assertj.core.api.Assertions.assertThat; @EnableWebSocket @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @Slf4j -public abstract class AbstractControllerTest extends AbstractWebTest { +public abstract class AbstractControllerTest extends AbstractNotifyEntityTest { public static final String WS_URL = "ws://localhost:"; diff --git a/application/src/test/java/org/thingsboard/server/controller/AbstractNotifyEntityTest.java b/application/src/test/java/org/thingsboard/server/controller/AbstractNotifyEntityTest.java new file mode 100644 index 0000000000..a5827d6f6d --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/controller/AbstractNotifyEntityTest.java @@ -0,0 +1,132 @@ +/** + * Copyright © 2016-2022 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.controller; + +import org.mockito.Mockito; +import org.springframework.boot.test.mock.mockito.SpyBean; +import org.thingsboard.server.cluster.TbClusterService; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.HasName; +import org.thingsboard.server.common.data.audit.ActionType; +import org.thingsboard.server.common.data.id.CustomerId; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.EntityIdFactory; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.id.UserId; +import org.thingsboard.server.dao.audit.AuditLogService; +import org.thingsboard.server.dao.model.ModelConstants; + +import java.util.Locale; + +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.thingsboard.server.service.entitiy.DefaultTbNotificationEntityService.edgeTypeByActionType; + +public abstract class AbstractNotifyEntityTest extends AbstractWebTest { + + @SpyBean + protected TbClusterService tbClusterService; + + @SpyBean + protected AuditLogService auditLogService; + + protected void testNotifyEntityOk(HasName entity, EntityId entityId, EntityId originatorId, + TenantId tenantId, CustomerId customerId, UserId userId, String userName, + ActionType actionType) { + testSendNotificationMsgToEdgeServiceOk(entityId, tenantId, actionType); + testLogEntityActionOk(entity, originatorId, tenantId, customerId, userId, userName, actionType); + testPushMsgToRuleEngineOk(entity, originatorId, tenantId, customerId, userId, userName, actionType); + Mockito.reset(tbClusterService, auditLogService); + } + + protected void testNotifyEntityDeleteOk(HasName entity, EntityId entityId, EntityId originatorId, + TenantId tenantId, CustomerId customerId, UserId userId, String userName, + ActionType actionType) { + Mockito.verify(tbClusterService, never()).sendNotificationMsgToEdgeService(Mockito.any(), + Mockito.any(), Mockito.any(entityId.getClass()), Mockito.any(), Mockito.any(), Mockito.any()); + testLogEntityActionOk(entity, originatorId, tenantId, customerId, userId, userName, actionType); + testPushMsgToRuleEngineOk(entity, originatorId, tenantId, customerId, userId, userName, actionType); + } + + private void testNotifyEntityError(EntityId entityId, HasName entity, TenantId tenantId, + UserId userId, String userName, ActionType actionType, Exception exp, + Object... additionalInfo) { + CustomerId customer_NULL_UUID = (CustomerId) EntityIdFactory.getByTypeAndUuid(EntityType.CUSTOMER, ModelConstants.NULL_UUID); + EntityId entity_NULL_UUID = EntityIdFactory.getByTypeAndUuid(EntityType.valueOf(entity.getClass().toString().substring(entity.getClass().toString().lastIndexOf(".") + 1).toUpperCase(Locale.ENGLISH)), + ModelConstants.NULL_UUID); + testNotificationMsgToEdgeServiceNever(entityId); + if (additionalInfo.length > 0) { + Mockito.verify(auditLogService, times(1)).logEntityAction(Mockito.eq(tenantId), + Mockito.eq(customer_NULL_UUID), Mockito.eq(userId), Mockito.eq(userName), + Mockito.eq(entity_NULL_UUID), Mockito.any(entity.getClass()), Mockito.eq(actionType), + Mockito.any(exp.getClass()), Mockito.eq(additionalInfo)); + } else { + Mockito.verify(auditLogService, times(1)).logEntityAction(Mockito.eq(tenantId), + Mockito.eq(customer_NULL_UUID), Mockito.eq(userId), Mockito.eq(userName), + Mockito.eq(entity_NULL_UUID), Mockito.any(entity.getClass()), Mockito.eq(actionType), + Mockito.any(exp.getClass()), Mockito.isNull()); + } + Mockito.verify(tbClusterService, never()).pushMsgToRuleEngine(Mockito.any(), Mockito.any(entityId.getClass()), + Mockito.any(), Mockito.any()); + Mockito.reset(tbClusterService, auditLogService); + } + + protected void testNotifyEntityNever(EntityId entityId, HasName entity) { + testNotificationMsgToEdgeServiceNever(entityId); + testLogEntityActionNever(entityId, entity); + testPushMsgToRuleEngineNever(entityId); + } + + protected void testNotificationMsgToEdgeServiceNever(EntityId entityId) { + Mockito.verify(tbClusterService, never()).sendNotificationMsgToEdgeService(Mockito.any(), + Mockito.any(), Mockito.any(entityId.getClass()), Mockito.any(), Mockito.any(), Mockito.any()); + } + + protected void testLogEntityActionNever(EntityId entityId, HasName entity) { + Mockito.verify(auditLogService, never()).logEntityAction(Mockito.any(), Mockito.any(), + Mockito.any(), Mockito.any(), Mockito.any(entityId.getClass()), Mockito.any(entity.getClass()), + Mockito.any(), Mockito.any()); + } + + protected void testPushMsgToRuleEngineNever(EntityId entityId) { + Mockito.verify(tbClusterService, never()).pushMsgToRuleEngine(Mockito.any(), + Mockito.any(entityId.getClass()), Mockito.any(), Mockito.any()); + } + + private void testLogEntityActionOk(HasName entity, EntityId originatorId, + TenantId tenantId, CustomerId customerId, UserId userId, String userName, + ActionType actionType) { + Mockito.verify(auditLogService, times(1)).logEntityAction(Mockito.eq(tenantId), Mockito.eq(customerId), + Mockito.eq(userId), Mockito.eq(userName), Mockito.eq(originatorId), + Mockito.eq(entity), Mockito.eq(actionType), Mockito.isNull()); + } + + private void testPushMsgToRuleEngineOk(HasName entity, EntityId originatorId, + TenantId tenantId, CustomerId customerId, UserId userId, String userName, + ActionType actionType) { + + Mockito.verify(auditLogService, times(1)).logEntityAction(Mockito.eq(tenantId), Mockito.eq(customerId), + Mockito.eq(userId), Mockito.eq(userName), Mockito.eq(originatorId), + Mockito.eq(entity), Mockito.eq(actionType), Mockito.isNull()); + } + + private void testSendNotificationMsgToEdgeServiceOk(EntityId entityId, TenantId tenantId, ActionType actionType) { + Mockito.verify(tbClusterService, times(1)).sendNotificationMsgToEdgeService(Mockito.eq(tenantId), + Mockito.isNull(), Mockito.eq(entityId), Mockito.isNull(), Mockito.isNull(), + Mockito.eq(edgeTypeByActionType(actionType))); + } + +} diff --git a/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java b/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java index b52c3028d6..2e5012851a 100644 --- a/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java @@ -75,11 +75,6 @@ import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.page.TimePageLink; -import org.thingsboard.server.common.data.queue.ProcessingStrategy; -import org.thingsboard.server.common.data.queue.ProcessingStrategyType; -import org.thingsboard.server.common.data.queue.Queue; -import org.thingsboard.server.common.data.queue.SubmitStrategy; -import org.thingsboard.server.common.data.queue.SubmitStrategyType; import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.config.ThingsboardSecurityConfiguration; import org.thingsboard.server.dao.tenant.TenantProfileService; @@ -137,7 +132,9 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest { protected TenantId tenantId; protected UserId tenantAdminUserId; + protected CustomerId tenantAdminCustomerId; protected CustomerId customerId; + protected UserId customerUserId; @SuppressWarnings("rawtypes") private HttpMessageConverter mappingJackson2HttpMessageConverter; @@ -202,6 +199,7 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest { tenantAdmin = createUserAndLogin(tenantAdmin, TENANT_ADMIN_PASSWORD); tenantAdminUserId = tenantAdmin.getId(); + tenantAdminCustomerId = tenantAdmin.getCustomerId(); Customer customer = new Customer(); customer.setTitle("Customer"); @@ -215,7 +213,8 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest { customerUser.setCustomerId(savedCustomer.getId()); customerUser.setEmail(CUSTOMER_USER_EMAIL); - createUserAndLogin(customerUser, CUSTOMER_USER_PASSWORD); + customerUser = createUserAndLogin(customerUser, CUSTOMER_USER_PASSWORD); + customerUserId = customerUser.getId(); logout(); diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseAlarmControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseAlarmControllerTest.java index ce9b6916fc..16a9f993c3 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseAlarmControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseAlarmControllerTest.java @@ -15,17 +15,21 @@ */ package org.thingsboard.server.controller; +import lombok.extern.slf4j.Slf4j; import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; +import org.mockito.Mockito; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.alarm.AlarmSeverity; import org.thingsboard.server.common.data.alarm.AlarmStatus; +import org.thingsboard.server.common.data.audit.ActionType; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +@Slf4j public abstract class BaseAlarmControllerTest extends AbstractControllerTest { public static final String TEST_ALARM_TYPE = "Test"; @@ -56,36 +60,68 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest { @Test public void testCreateAlarmViaCustomer() throws Exception { loginCustomerUser(); - createAlarm(TEST_ALARM_TYPE); + + Mockito.reset(tbClusterService, auditLogService); + + Alarm alarm = createAlarm(TEST_ALARM_TYPE); + + testNotifyEntityOk(alarm, alarm.getId(), alarm.getOriginator(), + tenantId, customerId, customerUserId, CUSTOMER_USER_EMAIL, ActionType.ADDED); logout(); } @Test public void testCreateAlarmViaTenant() throws Exception { loginTenantAdmin(); - createAlarm(TEST_ALARM_TYPE); + + Mockito.reset(tbClusterService, auditLogService); + + Alarm alarm = createAlarm(TEST_ALARM_TYPE); + + testNotifyEntityOk(alarm, alarm.getId(), alarm.getOriginator(), + tenantId, customerId, tenantAdminUserId, TENANT_ADMIN_EMAIL, ActionType.ADDED); logout(); } @Test public void testUpdateAlarmViaCustomer() throws Exception { loginCustomerUser(); + + Mockito.reset(tbClusterService, auditLogService); + Alarm alarm = createAlarm(TEST_ALARM_TYPE); + + testNotifyEntityOk(alarm, alarm.getId(), alarm.getOriginator(), + tenantId, customerId, customerUserId, CUSTOMER_USER_EMAIL, ActionType.ADDED); + alarm.setSeverity(AlarmSeverity.MAJOR); Alarm updatedAlarm = doPost("/api/alarm", alarm, Alarm.class); Assert.assertNotNull(updatedAlarm); Assert.assertEquals(AlarmSeverity.MAJOR, updatedAlarm.getSeverity()); + + testNotifyEntityOk(alarm, alarm.getId(), alarm.getOriginator(), + tenantId, customerId, customerUserId, CUSTOMER_USER_EMAIL, ActionType.UPDATED); logout(); } @Test public void testUpdateAlarmViaTenant() throws Exception { loginTenantAdmin(); + + Mockito.reset(tbClusterService, auditLogService); + Alarm alarm = createAlarm(TEST_ALARM_TYPE); + + testNotifyEntityOk(alarm, alarm.getId(), alarm.getOriginator(), + tenantId, customerId, tenantAdminUserId, TENANT_ADMIN_EMAIL, ActionType.ADDED); + alarm.setSeverity(AlarmSeverity.MAJOR); Alarm updatedAlarm = doPost("/api/alarm", alarm, Alarm.class); Assert.assertNotNull(updatedAlarm); Assert.assertEquals(AlarmSeverity.MAJOR, updatedAlarm.getSeverity()); + + testNotifyEntityOk(updatedAlarm, updatedAlarm.getId(), updatedAlarm.getOriginator(), + tenantId, customerId, tenantAdminUserId, TENANT_ADMIN_EMAIL, ActionType.UPDATED); logout(); } @@ -93,9 +129,15 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest { public void testUpdateAlarmViaDifferentTenant() throws Exception { loginTenantAdmin(); Alarm alarm = createAlarm(TEST_ALARM_TYPE); + alarm.setSeverity(AlarmSeverity.MAJOR); loginDifferentTenant(); + + Mockito.reset(tbClusterService, auditLogService); + doPost("/api/alarm", alarm).andExpect(status().isForbidden()); + + testNotifyEntityNever(alarm.getId(), alarm); logout(); } @@ -103,9 +145,15 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest { public void testUpdateAlarmViaDifferentCustomer() throws Exception { loginCustomerUser(); Alarm alarm = createAlarm(TEST_ALARM_TYPE); + loginDifferentCustomer(); alarm.setSeverity(AlarmSeverity.MAJOR); + + Mockito.reset(tbClusterService, auditLogService); + doPost("/api/alarm", alarm).andExpect(status().isForbidden()); + + testNotifyEntityNever(alarm.getId(), alarm); logout(); } @@ -113,7 +161,13 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest { public void testDeleteAlarmViaCustomer() throws Exception { loginCustomerUser(); Alarm alarm = createAlarm(TEST_ALARM_TYPE); + + Mockito.reset(tbClusterService, auditLogService); + doDelete("/api/alarm/" + alarm.getId()).andExpect(status().isOk()); + + testNotifyEntityDeleteOk(alarm, alarm.getId(), alarm.getOriginator(), + tenantId, customerId, customerUserId, CUSTOMER_USER_EMAIL, ActionType.DELETED); logout(); } @@ -121,7 +175,13 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest { public void testDeleteAlarmViaTenant() throws Exception { loginTenantAdmin(); Alarm alarm = createAlarm(TEST_ALARM_TYPE); + + Mockito.reset(tbClusterService, auditLogService); + doDelete("/api/alarm/" + alarm.getId()).andExpect(status().isOk()); + + testNotifyEntityDeleteOk(alarm, alarm.getId(), alarm.getOriginator(), + tenantId, tenantAdminCustomerId, tenantAdminUserId, TENANT_ADMIN_EMAIL, ActionType.DELETED); logout(); } @@ -129,8 +189,15 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest { public void testDeleteAlarmViaDifferentTenant() throws Exception { loginTenantAdmin(); Alarm alarm = createAlarm(TEST_ALARM_TYPE); + loginDifferentTenant(); + + Mockito.reset(tbClusterService, auditLogService); + doDelete("/api/alarm/" + alarm.getId()).andExpect(status().isForbidden()); + + testNotifyEntityNever(alarm.getId(), alarm); + logout(); } @@ -139,7 +206,13 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest { loginCustomerUser(); Alarm alarm = createAlarm(TEST_ALARM_TYPE); loginDifferentCustomer(); + + Mockito.reset(tbClusterService, auditLogService); + doDelete("/api/alarm/" + alarm.getId()).andExpect(status().isForbidden()); + + testNotifyEntityNever(alarm.getId(), alarm); + logout(); } @@ -147,10 +220,17 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest { public void testClearAlarmViaCustomer() throws Exception { loginCustomerUser(); Alarm alarm = createAlarm(TEST_ALARM_TYPE); + + Mockito.reset(tbClusterService, auditLogService); + doPost("/api/alarm/" + alarm.getId() + "/clear").andExpect(status().isOk()); + Alarm foundAlarm = doGet("/api/alarm/" + alarm.getId(), Alarm.class); Assert.assertNotNull(foundAlarm); Assert.assertEquals(AlarmStatus.CLEARED_UNACK, foundAlarm.getStatus()); + + testNotifyEntityOk(foundAlarm, foundAlarm.getId(), foundAlarm.getOriginator(), + tenantId, customerId, customerUserId, CUSTOMER_USER_EMAIL, ActionType.ALARM_CLEAR); logout(); } @@ -158,10 +238,16 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest { public void testClearAlarmViaTenant() throws Exception { loginTenantAdmin(); Alarm alarm = createAlarm(TEST_ALARM_TYPE); + + Mockito.reset(tbClusterService, auditLogService); + doPost("/api/alarm/" + alarm.getId() + "/clear").andExpect(status().isOk()); Alarm foundAlarm = doGet("/api/alarm/" + alarm.getId(), Alarm.class); Assert.assertNotNull(foundAlarm); Assert.assertEquals(AlarmStatus.CLEARED_UNACK, foundAlarm.getStatus()); + + testNotifyEntityOk(foundAlarm, foundAlarm.getId(), foundAlarm.getOriginator(), + tenantId, customerId, tenantAdminUserId, TENANT_ADMIN_EMAIL, ActionType.ALARM_CLEAR); logout(); } @@ -169,10 +255,17 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest { public void testAcknowledgeAlarmViaCustomer() throws Exception { loginCustomerUser(); Alarm alarm = createAlarm(TEST_ALARM_TYPE); + + Mockito.reset(tbClusterService, auditLogService); + doPost("/api/alarm/" + alarm.getId() + "/ack").andExpect(status().isOk()); + Alarm foundAlarm = doGet("/api/alarm/" + alarm.getId(), Alarm.class); Assert.assertNotNull(foundAlarm); Assert.assertEquals(AlarmStatus.ACTIVE_ACK, foundAlarm.getStatus()); + + testNotifyEntityOk(foundAlarm, foundAlarm.getId(), foundAlarm.getOriginator(), + tenantId, customerId, customerUserId, CUSTOMER_USER_EMAIL, ActionType.ALARM_ACK); logout(); } @@ -181,7 +274,12 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest { loginCustomerUser(); Alarm alarm = createAlarm(TEST_ALARM_TYPE); loginDifferentCustomer(); + + Mockito.reset(tbClusterService, auditLogService); + doPost("/api/alarm/" + alarm.getId() + "/clear").andExpect(status().isForbidden()); + + testNotifyEntityNever(alarm.getId(), alarm); logout(); } @@ -190,7 +288,12 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest { loginTenantAdmin(); Alarm alarm = createAlarm(TEST_ALARM_TYPE); loginDifferentTenant(); + + Mockito.reset(tbClusterService, auditLogService); + doPost("/api/alarm/" + alarm.getId() + "/clear").andExpect(status().isForbidden()); + + testNotifyEntityNever(alarm.getId(), alarm); logout(); } @@ -199,7 +302,12 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest { loginCustomerUser(); Alarm alarm = createAlarm(TEST_ALARM_TYPE); loginDifferentCustomer(); + + Mockito.reset(tbClusterService, auditLogService); + doPost("/api/alarm/" + alarm.getId() + "/ack").andExpect(status().isForbidden()); + + testNotifyEntityNever(alarm.getId(), alarm); logout(); } @@ -208,7 +316,12 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest { loginTenantAdmin(); Alarm alarm = createAlarm(TEST_ALARM_TYPE); loginDifferentTenant(); + + Mockito.reset(tbClusterService, auditLogService); + doPost("/api/alarm/" + alarm.getId() + "/ack").andExpect(status().isForbidden()); + + testNotifyEntityNever(alarm.getId(), alarm); logout(); } @@ -221,10 +334,8 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest { .severity(AlarmSeverity.CRITICAL) .type(type) .build(); - alarm = doPost("/api/alarm", alarm, Alarm.class); Assert.assertNotNull(alarm); - return alarm; } } diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/profile/TbDeviceProfileNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/profile/TbDeviceProfileNodeTest.java index 4be8372df9..9aa53b41f7 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/profile/TbDeviceProfileNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/profile/TbDeviceProfileNodeTest.java @@ -40,12 +40,12 @@ import org.thingsboard.server.common.data.device.profile.AlarmConditionFilter; import org.thingsboard.server.common.data.device.profile.AlarmConditionFilterKey; import org.thingsboard.server.common.data.device.profile.AlarmConditionKeyType; import org.thingsboard.server.common.data.device.profile.AlarmRule; +import org.thingsboard.server.common.data.device.profile.CustomTimeSchedule; +import org.thingsboard.server.common.data.device.profile.CustomTimeScheduleItem; import org.thingsboard.server.common.data.device.profile.DeviceProfileAlarm; import org.thingsboard.server.common.data.device.profile.DeviceProfileData; import org.thingsboard.server.common.data.device.profile.DurationAlarmConditionSpec; import org.thingsboard.server.common.data.device.profile.RepeatingAlarmConditionSpec; -import org.thingsboard.server.common.data.device.profile.CustomTimeSchedule; -import org.thingsboard.server.common.data.device.profile.CustomTimeScheduleItem; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.DeviceProfileId; @@ -70,10 +70,10 @@ import org.thingsboard.server.dao.timeseries.TimeseriesService; import java.math.BigDecimal; import java.math.RoundingMode; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; -import java.util.ArrayList; import java.util.Optional; import java.util.TreeMap; import java.util.UUID; @@ -1157,7 +1157,7 @@ public class TbDeviceProfileNodeTest { .thenReturn(listListenableFutureActiveSchedule); TbMsg theMsg = TbMsg.newMsg("ALARM", deviceId, new TbMsgMetaData(), ""); - Mockito.when(ctx.newMsg(Mockito.anyString(), Mockito.anyString(), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.anyString())) + Mockito.when(ctx.newMsg(Mockito.any(), Mockito.anyString(), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.anyString())) .thenReturn(theMsg); ObjectNode data = mapper.createObjectNode(); From 6351366aed3c6a20daa4d3ad64e5dbc754e287a6 Mon Sep 17 00:00:00 2001 From: YevhenBondarenko Date: Mon, 13 Jun 2022 10:57:28 +0200 Subject: [PATCH 783/798] fixed device profile node --- .../rule/engine/profile/TbDeviceProfileNodeTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/profile/TbDeviceProfileNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/profile/TbDeviceProfileNodeTest.java index 4be8372df9..a8c38ce33f 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/profile/TbDeviceProfileNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/profile/TbDeviceProfileNodeTest.java @@ -49,6 +49,7 @@ import org.thingsboard.server.common.data.device.profile.CustomTimeScheduleItem; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.DeviceProfileId; +import org.thingsboard.server.common.data.id.QueueId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.kv.AttributeKvEntry; import org.thingsboard.server.common.data.query.BooleanFilterPredicate; @@ -1157,7 +1158,7 @@ public class TbDeviceProfileNodeTest { .thenReturn(listListenableFutureActiveSchedule); TbMsg theMsg = TbMsg.newMsg("ALARM", deviceId, new TbMsgMetaData(), ""); - Mockito.when(ctx.newMsg(Mockito.anyString(), Mockito.anyString(), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.anyString())) + Mockito.when(ctx.newMsg(Mockito.any(), Mockito.anyString(), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.anyString())) .thenReturn(theMsg); ObjectNode data = mapper.createObjectNode(); From b5f052cbc5761e4138b186e45be0daa3b07dd117 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Mon, 13 Jun 2022 12:17:15 +0300 Subject: [PATCH 784/798] Fix typo --- .../home/components/profile/alarm/alarm-schedule.component.html | 2 +- ...ustom_schedule_format.md => alarm_custom_schedule_format.md} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename ui-ngx/src/assets/help/en_US/device-profile/{alarm_сustom_schedule_format.md => alarm_custom_schedule_format.md} (100%) diff --git a/ui-ngx/src/app/modules/home/components/profile/alarm/alarm-schedule.component.html b/ui-ngx/src/app/modules/home/components/profile/alarm/alarm-schedule.component.html index d859cd9201..65fe9c9636 100644 --- a/ui-ngx/src/app/modules/home/components/profile/alarm/alarm-schedule.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/alarm/alarm-schedule.component.html @@ -74,7 +74,7 @@
- +
device-profile.schedule-days
diff --git a/ui-ngx/src/assets/help/en_US/device-profile/alarm_сustom_schedule_format.md b/ui-ngx/src/assets/help/en_US/device-profile/alarm_custom_schedule_format.md similarity index 100% rename from ui-ngx/src/assets/help/en_US/device-profile/alarm_сustom_schedule_format.md rename to ui-ngx/src/assets/help/en_US/device-profile/alarm_custom_schedule_format.md From eb105d0859c5a1e2510f6e39a4d268fb8c73d391 Mon Sep 17 00:00:00 2001 From: fe-dev Date: Mon, 13 Jun 2022 16:37:40 +0300 Subject: [PATCH 785/798] UI: Refactoring phone input --- .../components/phone-input.component.html | 4 +- .../components/phone-input.component.scss | 5 - .../components/phone-input.component.ts | 64 +- .../src/app/shared/models/country.models.ts | 1573 +++-------------- 4 files changed, 268 insertions(+), 1378 deletions(-) diff --git a/ui-ngx/src/app/shared/components/phone-input.component.html b/ui-ngx/src/app/shared/components/phone-input.component.html index d52d7202d0..4b81391b5f 100644 --- a/ui-ngx/src/app/shared/components/phone-input.component.html +++ b/ui-ngx/src/app/shared/components/phone-input.component.html @@ -22,8 +22,8 @@ {{ flagIcon }} - {{country.flag}} - {{country.name + ' +' + country.dialCode }} + {{country.flag}} + {{' ' + country.name + ' +' + country.dialCode }}
diff --git a/ui-ngx/src/app/shared/components/phone-input.component.scss b/ui-ngx/src/app/shared/components/phone-input.component.scss index 872c686af0..d8bd9df461 100644 --- a/ui-ngx/src/app/shared/components/phone-input.component.scss +++ b/ui-ngx/src/app/shared/components/phone-input.component.scss @@ -44,11 +44,6 @@ width: 45px; height: 30px; - .country-flag { - font-size: 22px; - padding-right: 10px; - } - .mat-select-trigger { height: 100%; width: 100%; diff --git a/ui-ngx/src/app/shared/components/phone-input.component.ts b/ui-ngx/src/app/shared/components/phone-input.component.ts index bbee8098f6..f5fdf62f34 100644 --- a/ui-ngx/src/app/shared/components/phone-input.component.ts +++ b/ui-ngx/src/app/shared/components/phone-input.component.ts @@ -62,15 +62,15 @@ export class PhoneInputComponent implements OnInit, ControlValueAccessor, Valida @Input() floatLabel: FloatLabelType = 'always'; @Input() appearance: MatFormFieldAppearance = 'legacy'; - allCountries: Array = []; + allCountries: Array = this.countryCodeData.allCountries; phonePlaceholder: string; - countryCallingCode: string; flagIcon: string; - + phoneFormGroup: FormGroup; phoneNumberPattern = phoneNumberPattern; + private baseCode = 127397; + private countryCallingCode: string; private modelValue: string; - public phoneFormGroup: FormGroup; private valueChange$: Subscription = null; private propagateChange = (v: any) => { }; @@ -80,17 +80,24 @@ export class PhoneInputComponent implements OnInit, ControlValueAccessor, Valida } ngOnInit(): void { - if (this.enableFlagsSelect) { - this.fetchCountryData(); + const validators: ValidatorFn[] = [Validators.pattern(phoneNumberPattern), this.validatePhoneNumber()]; + if (this.required) { + validators.push(Validators.required); } this.phoneFormGroup = this.fb.group({ country: [this.defaultCountry, []], - phoneNumber: [null, this.required ? [Validators.required] : []] + phoneNumber: [null, validators] }); - this.phoneFormGroup.get('phoneNumber').setValidators([Validators.pattern(phoneNumberPattern), this.validatePhoneNumber()]); - this.valueChange$ = this.phoneFormGroup.get('phoneNumber').valueChanges.subscribe(() => { + this.valueChange$ = this.phoneFormGroup.get('phoneNumber').valueChanges.subscribe(value => { this.updateModel(); + if (value) { + const parsedPhoneNumber = parsePhoneNumberFromString(value); + const country = this.phoneFormGroup.get('country').value; + if (parsedPhoneNumber?.country && parsedPhoneNumber?.country !== country) { + this.phoneFormGroup.get('country').patchValue(parsedPhoneNumber.country, {emitEvent: true}); + } + } }); this.phoneFormGroup.get('country').valueChanges.subscribe(value => { @@ -101,20 +108,8 @@ export class PhoneInputComponent implements OnInit, ControlValueAccessor, Valida if (phoneNumber) { if (code !== this.countryCallingCode && phoneNumber.includes(code)) { phoneNumber = phoneNumber.replace(code, this.countryCallingCode); - } else { - phoneNumber = this.countryCallingCode; + this.phoneFormGroup.get('phoneNumber').patchValue(phoneNumber); } - this.phoneFormGroup.get('phoneNumber').patchValue(phoneNumber); - } - } - }); - - this.phoneFormGroup.get('phoneNumber').valueChanges.subscribe(value => { - if (value) { - const parsedPhoneNumber = parsePhoneNumberFromString(value); - const country = this.phoneFormGroup.get('country').value; - if (parsedPhoneNumber?.country && parsedPhoneNumber?.country !== country) { - this.phoneFormGroup.get('country').patchValue(parsedPhoneNumber.country, {emitEvent: true}); } } }); @@ -135,22 +130,21 @@ export class PhoneInputComponent implements OnInit, ControlValueAccessor, Valida } } - getFlagAndPhoneNumberData(country) { + private getFlagAndPhoneNumberData(country) { if (this.enableFlagsSelect) { this.flagIcon = this.getFlagIcon(country); } this.getPhoneNumberData(country); } - getPhoneNumberData(country): void { + private getPhoneNumberData(country): void { const phoneData = getExampleNumber(country, examples); this.phonePlaceholder = phoneData.number; this.countryCallingCode = '+' + phoneData.countryCallingCode; } - getFlagIcon(countryCode) { - const base = 127462 - 65; - return String.fromCodePoint(...countryCode.split('').map(country => base + country.charCodeAt(0))); + private getFlagIcon(countryCode) { + return String.fromCodePoint(...countryCode.split('').map(country => this.baseCode + country.charCodeAt(0))); } validatePhoneNumber(): ValidatorFn { @@ -192,21 +186,6 @@ export class PhoneInputComponent implements OnInit, ControlValueAccessor, Valida } } - protected fetchCountryData(): void { - this.allCountries = []; - this.countryCodeData.allCountries.forEach((c) => { - const cc = c[1]; - const country: Country = { - name: c[0].toString(), - iso2: c[1].toString(), - dialCode: c[2].toString(), - flag: this.getFlagIcon(cc), - }; - - this.allCountries.push(country); - }); - } - writeValue(phoneNumber): void { this.modelValue = phoneNumber; const country = phoneNumber ? parsePhoneNumberFromString(phoneNumber)?.country : this.defaultCountry; @@ -223,5 +202,4 @@ export class PhoneInputComponent implements OnInit, ControlValueAccessor, Valida this.propagateChange(null); } } - } diff --git a/ui-ngx/src/app/shared/models/country.models.ts b/ui-ngx/src/app/shared/models/country.models.ts index 10612de015..d4895fada7 100644 --- a/ui-ngx/src/app/shared/models/country.models.ts +++ b/ui-ngx/src/app/shared/models/country.models.ts @@ -24,15 +24,7 @@ export interface Country { flag: string; } -export enum SearchCountryField { - DialCode = 'dialCode', - Iso2 = 'iso2', - Name = 'name', - All = 'all' -} - - -export enum CountryISO{ +export enum CountryISO { Afghanistan = 'AF', Albania = 'AL', Algeria = 'DZ', @@ -280,1324 +272,249 @@ export enum CountryISO{ @Injectable() export class CountryData { - public allCountries = [ - [ - 'Afghanistan', - CountryISO.Afghanistan, - '93' - ], - [ - 'Albania', - CountryISO.Albania, - '355' - ], - [ - 'Algeria', - CountryISO.Algeria, - '213' - ], - [ - 'American Samoa', - CountryISO.AmericanSamoa, - '1', - 1, - [ - '684', - ] - ], - [ - 'Andorra', - CountryISO.Andorra, - '376' - ], - [ - 'Angola', - CountryISO.Angola, - '244' - ], - [ - 'Anguilla', - CountryISO.Anguilla, - '1', - 1, - [ - '264', - ] - ], - [ - 'Antigua and Barbuda', - CountryISO.AntiguaAndBarbuda, - '1', - 1, - [ - '268', - ] - ], - [ - 'Argentina', - CountryISO.Argentina, - '54' - ], - [ - 'Armenia', - CountryISO.Armenia, - '374' - ], - [ - 'Aruba', - CountryISO.Aruba, - '297' - ], - [ - 'Australia', - CountryISO.Australia, - '61', - 0 - ], - [ - 'Austria', - CountryISO.Austria, - '43' - ], - [ - 'Azerbaijan', - CountryISO.Azerbaijan, - '994' - ], - [ - 'Bahamas', - CountryISO.Bahamas, - '1', - 1, - [ - '242', - ] - ], - [ - 'Bahrain', - CountryISO.Bahrain, - '973' - ], - [ - 'Bangladesh', - CountryISO.Bangladesh, - '880' - ], - [ - 'Barbados', - CountryISO.Barbados, - '1', - 1, - [ - '246', - ] - ], - [ - 'Belarus', - CountryISO.Belarus, - '375' - ], - [ - 'Belgium', - CountryISO.Belgium, - '32' - ], - [ - 'Belize', - CountryISO.Belize, - '501' - ], - [ - 'Benin', - CountryISO.Benin, - '229' - ], - [ - 'Bermuda', - CountryISO.Bermuda, - '1', - 1, - [ - '441', - ] - ], - [ - 'Bhutan', - CountryISO.Bhutan, - '975' - ], - [ - 'Bolivia', - CountryISO.Bolivia, - '591' - ], - [ - 'Bosnia and Herzegovina', - CountryISO.BosniaAndHerzegovina, - '387' - ], - [ - 'Botswana', - CountryISO.Botswana, - '267' - ], - [ - 'Brazil', - CountryISO.Brazil, - '55' - ], - [ - 'British Indian Ocean Territory', - CountryISO.BritishIndianOceanTerritory, - '246' - ], - [ - 'British Virgin Islands', - CountryISO.BritishVirginIslands, - '1', - 1, - [ - '284', - ] - ], - [ - 'Brunei', - CountryISO.Brunei, - '673' - ], - [ - 'Bulgaria', - CountryISO.Bulgaria, - '359' - ], - [ - 'Burkina Faso', - CountryISO.BurkinaFaso, - '226' - ], - [ - 'Burundi', - CountryISO.Burundi, - '257' - ], - [ - 'Cambodia', - CountryISO.Cambodia, - '855' - ], - [ - 'Cameroon', - CountryISO.Cameroon, - '237' - ], - [ - 'Canada', - CountryISO.Canada, - '1', - 1, - [ - '204', '226', '236', '249', '250', '289', '306', '343', '365', '387', '403', '416', - '418', '431', '437', '438', '450', '506', '514', '519', '548', '579', '581', '587', - '604', '613', '639', '647', '672', '705', '709', '742', '778', '780', '782', '807', - '819', '825', '867', '873', '902', '905' - ] - ], - [ - 'Cape Verde', - CountryISO.CapeVerde, - '238' - ], - [ - 'Caribbean Netherlands', - CountryISO.CaribbeanNetherlands, - '599', - 1 - ], - [ - 'Cayman Islands', - CountryISO.CaymanIslands, - '1', - 1, - [ - '345', - ] - ], - [ - 'Central African Republic', - CountryISO.CentralAfricanRepublic, - '236' - ], - [ - 'Chad', - CountryISO.Chad, - '235' - ], - [ - 'Chile', - CountryISO.Chile, - '56' - ], - [ - 'China', - CountryISO.China, - '86' - ], - [ - 'Christmas Island', - CountryISO.ChristmasIsland, - '61', - 2 - ], - [ - 'Cocos Islands', - CountryISO.Cocos, - '61', - 1 - ], - [ - 'Colombia', - CountryISO.Colombia, - '57' - ], - [ - 'Comoros', - CountryISO.Comoros, - '269' - ], - [ - 'Congo-Kinshasa', - CountryISO.CongoDRCJamhuriYaKidemokrasiaYaKongo, - '243' - ], - [ - 'Congo-Brazzaville', - CountryISO.CongoRepublicCongoBrazzaville, - '242' - ], - [ - 'Cook Islands', - CountryISO.CookIslands, - '682' - ], - [ - 'Costa Rica', - CountryISO.CostaRica, - '506' - ], - [ - 'Côte d’Ivoire', - CountryISO.CôteDIvoire, - '225' - ], - [ - 'Croatia', - CountryISO.Croatia, - '385' - ], - [ - 'Cuba', - CountryISO.Cuba, - '53' - ], - [ - 'Curaçao', - CountryISO.Curaçao, - '599', - 0 - ], - [ - 'Cyprus', - CountryISO.Cyprus, - '357' - ], - [ - 'Czech Republic', - CountryISO.CzechRepublic, - '420' - ], - [ - 'Denmark', - CountryISO.Denmark, - '45' - ], - [ - 'Djibouti', - CountryISO.Djibouti, - '253' - ], - [ - 'Dominica', - CountryISO.Dominica, - '1767' - ], - [ - 'Dominican Republic', - CountryISO.DominicanRepublic, - '1', - 2, - ['809', '829', '849'] - ], - [ - 'Ecuador', - CountryISO.Ecuador, - '593' - ], - [ - 'Egypt', - CountryISO.Egypt, - '20' - ], - [ - 'El Salvador', - CountryISO.ElSalvador, - '503' - ], - [ - 'Equatorial Guinea', - CountryISO.EquatorialGuinea, - '240' - ], - [ - 'Eritrea', - CountryISO.Eritrea, - '291' - ], - [ - 'Estonia', - CountryISO.Estonia, - '372' - ], - [ - 'Ethiopia', - CountryISO.Ethiopia, - '251' - ], - [ - 'Falkland Islands', - CountryISO.FalklandIslands, - '500' - ], - [ - 'Faroe Islands', - CountryISO.FaroeIslands, - '298' - ], - [ - 'Fiji', - CountryISO.Fiji, - '679' - ], - [ - 'Finland', - CountryISO.Finland, - '358', - 0 - ], - [ - 'France', - CountryISO.France, - '33' - ], - [ - 'French Guiana', - CountryISO.FrenchGuiana, - '594' - ], - [ - 'French Polynesia', - CountryISO.FrenchPolynesia, - '689' - ], - [ - 'Gabon', - CountryISO.Gabon, - '241' - ], - [ - 'Gambia', - CountryISO.Gambia, - '220' - ], - [ - 'Georgia', - CountryISO.Georgia, - '995' - ], - [ - 'Germany', - CountryISO.Germany, - '49' - ], - [ - 'Ghana', - CountryISO.Ghana, - '233' - ], - [ - 'Gibraltar', - CountryISO.Gibraltar, - '350' - ], - [ - 'Greece', - CountryISO.Greece, - '30' - ], - [ - 'Greenland', - CountryISO.Greenland, - '299' - ], - [ - 'Grenada', - CountryISO.Grenada, - '1473' - ], - [ - 'Guadeloupe', - CountryISO.Guadeloupe, - '590', - 0 - ], - [ - 'Guam', - CountryISO.Guam, - '1', - 1, - [ - '671', - ] - ], - [ - 'Guatemala', - CountryISO.Guatemala, - '502' - ], - [ - 'Guernsey', - CountryISO.Guernsey, - '44', - 1, - [1481] - ], - [ - 'Guinea', - CountryISO.Guinea, - '224' - ], - [ - 'Guinea-Bissau', - CountryISO.GuineaBissau, - '245' - ], - [ - 'Guyana', - CountryISO.Guyana, - '592' - ], - [ - 'Haiti', - CountryISO.Haiti, - '509' - ], - [ - 'Honduras', - CountryISO.Honduras, - '504' - ], - [ - 'Hong Kong', - CountryISO.HongKong, - '852' - ], - [ - 'Hungary', - CountryISO.Hungary, - '36' - ], - [ - 'Iceland', - CountryISO.Iceland, - '354' - ], - [ - 'India', - CountryISO.India, - '91' - ], - [ - 'Indonesia', - CountryISO.Indonesia, - '62' - ], - [ - 'Iran', - CountryISO.Iran, - '98' - ], - [ - 'Iraq', - CountryISO.Iraq, - '964' - ], - [ - 'Ireland', - CountryISO.Ireland, - '353' - ], - [ - 'Isle of Man', - CountryISO.IsleOfMan, - '44', - 2, - [1624] - ], - [ - 'Israel', - CountryISO.Israel, - '972' - ], - [ - 'Italy', - CountryISO.Italy, - '39', - 0 - ], - [ - 'Jamaica', - CountryISO.Jamaica, - '1', - 1, - [ - '876', - ] - ], - [ - 'Japan', - CountryISO.Japan, - '81' - ], - [ - 'Jersey', - CountryISO.Jersey, - '44', - 3, - [1534] - ], - [ - 'Jordan', - CountryISO.Jordan, - '962' - ], - [ - 'Kazakhstan', - CountryISO.Kazakhstan, - '7', - 1 - ], - [ - 'Kenya', - CountryISO.Kenya, - '254' - ], - [ - 'Kiribati', - CountryISO.Kiribati, - '686' - ], - [ - 'Kosovo', - CountryISO.Kosovo, - '383' - ], - [ - 'Kuwait', - CountryISO.Kuwait, - '965' - ], - [ - 'Kyrgyzstan', - CountryISO.Kyrgyzstan, - '996' - ], - [ - 'Laos', - CountryISO.Laos, - '856' - ], - [ - 'Latvia', - CountryISO.Latvia, - '371' - ], - [ - 'Lebanon', - CountryISO.Lebanon, - '961' - ], - [ - 'Lesotho', - CountryISO.Lesotho, - '266' - ], - [ - 'Liberia', - CountryISO.Liberia, - '231' - ], - [ - 'Libya', - CountryISO.Libya, - '218' - ], - [ - 'Liechtenstein', - CountryISO.Liechtenstein, - '423' - ], - [ - 'Lithuania', - CountryISO.Lithuania, - '370' - ], - [ - 'Luxembourg', - CountryISO.Luxembourg, - '352' - ], - [ - 'Macau', - CountryISO.Macau, - '853' - ], - [ - 'Macedonia', - CountryISO.Macedonia, - '389' - ], - [ - 'Madagascar', - CountryISO.Madagascar, - '261' - ], - [ - 'Malawi', - CountryISO.Malawi, - '265' - ], - [ - 'Malaysia', - CountryISO.Malaysia, - '60' - ], - [ - 'Maldives', - CountryISO.Maldives, - '960' - ], - [ - 'Mali', - CountryISO.Mali, - '223' - ], - [ - 'Malta', - CountryISO.Malta, - '356' - ], - [ - 'Marshall Islands', - CountryISO.MarshallIslands, - '692' - ], - [ - 'Martinique', - CountryISO.Martinique, - '596' - ], - [ - 'Mauritania', - CountryISO.Mauritania, - '222' - ], - [ - 'Mauritius', - CountryISO.Mauritius, - '230' - ], - [ - 'Mayotte', - CountryISO.Mayotte, - '262', - 1 - ], - [ - 'Mexico', - CountryISO.Mexico, - '52' - ], - [ - 'Micronesia', - CountryISO.Micronesia, - '691' - ], - [ - 'Moldova', - CountryISO.Moldova, - '373' - ], - [ - 'Monaco', - CountryISO.Monaco, - '377' - ], - [ - 'Mongolia', - CountryISO.Mongolia, - '976' - ], - [ - 'Montenegro', - CountryISO.Montenegro, - '382' - ], - [ - 'Montserrat', - CountryISO.Montserrat, - '1', - 1, - [ - '664', - ] - ], - [ - 'Morocco', - CountryISO.Morocco, - '212', - 0 - ], - [ - 'Mozambique', - CountryISO.Mozambique, - '258' - ], - [ - 'Myanmar', - CountryISO.Myanmar, - '95' - ], - [ - 'Namibia', - CountryISO.Namibia, - '264' - ], - [ - 'Nauru', - CountryISO.Nauru, - '674' - ], - [ - 'Nepal', - CountryISO.Nepal, - '977' - ], - [ - 'Netherlands', - CountryISO.Netherlands, - '31' - ], - [ - 'New Caledonia', - CountryISO.NewCaledonia, - '687' - ], - [ - 'New Zealand', - CountryISO.NewZealand, - '64' - ], - [ - 'Nicaragua', - CountryISO.Nicaragua, - '505' - ], - [ - 'Niger', - CountryISO.Niger, - '227' - ], - [ - 'Nigeria', - CountryISO.Nigeria, - '234' - ], - [ - 'Niue', - CountryISO.Niue, - '683' - ], - [ - 'Norfolk Island', - CountryISO.NorfolkIsland, - '672' - ], - [ - 'North Korea', - CountryISO.NorthKorea, - '850' - ], - [ - 'Northern Mariana Islands', - CountryISO.NorthernMarianaIslands, - '1670' - ], - [ - 'Norway', - CountryISO.Norway, - '47', - 0 - ], - [ - 'Oman', - CountryISO.Oman, - '968' - ], - [ - 'Pakistan', - CountryISO.Pakistan, - '92' - ], - [ - 'Palau', - CountryISO.Palau, - '680' - ], - [ - 'Palestine', - CountryISO.Palestine, - '970' - ], - [ - 'Panama', - CountryISO.Panama, - '507' - ], - [ - 'Papua New Guinea', - CountryISO.PapuaNewGuinea, - '675' - ], - [ - 'Paraguay', - CountryISO.Paraguay, - '595' - ], - [ - 'Peru', - CountryISO.Peru, - '51' - ], - [ - 'Philippines', - CountryISO.Philippines, - '63' - ], - [ - 'Poland', - CountryISO.Poland, - '48' - ], - [ - 'Portugal', - CountryISO.Portugal, - '351' - ], - [ - 'Puerto Rico', - CountryISO.PuertoRico, - '1', - 3, - ['787', '939'] - ], - [ - 'Qatar', - CountryISO.Qatar, - '974' - ], - [ - 'Réunion', - CountryISO.Réunion, - '262', - 0 - ], - [ - 'Romania', - CountryISO.Romania, - '40' - ], - [ - 'Russia', - CountryISO.Russia, - '7', - 0 - ], - [ - 'Rwanda', - CountryISO.Rwanda, - '250' - ], - [ - 'Saint Barthélemy', - CountryISO.SaintBarthélemy, - '590', - 1 - ], - [ - 'Saint Helena', - CountryISO.SaintHelena, - '290' - ], - [ - 'Saint Kitts and Nevis', - CountryISO.SaintKittsAndNevis, - '1869' - ], - [ - 'Saint Lucia', - CountryISO.SaintLucia, - '1', - 1, - [ - '758', - ] - ], - [ - 'Saint Martin', - CountryISO.SaintMartin, - '590', - 2 - ], - [ - 'Saint Pierre and Miquelon', - CountryISO.SaintPierreAndMiquelon, - '508' - ], - [ - 'Saint Vincent and the Grenadines', - CountryISO.SaintVincentAndTheGrenadines, - '1', - 1, - [ - '784', - ] - ], - [ - 'Samoa', - CountryISO.Samoa, - '685' - ], - [ - 'San Marino', - CountryISO.SanMarino, - '378' - ], - [ - 'São Tomé and Príncipe', - CountryISO.SãoToméAndPríncipe, - '239' - ], - [ - 'Saudi Arabia', - CountryISO.SaudiArabia, - '966' - ], - [ - 'Senegal', - CountryISO.Senegal, - '221' - ], - [ - 'Serbia', - CountryISO.Serbia, - '381' - ], - [ - 'Seychelles', - CountryISO.Seychelles, - '248' - ], - [ - 'Sierra Leone', - CountryISO.SierraLeone, - '232' - ], - [ - 'Singapore', - CountryISO.Singapore, - '65' - ], - [ - 'Sint Maarten', - CountryISO.SintMaarten, - '1', - 1, - [ - '721', - ] - ], - [ - 'Slovakia', - CountryISO.Slovakia, - '421' - ], - [ - 'Slovenia', - CountryISO.Slovenia, - '386' - ], - [ - 'Solomon Islands', - CountryISO.SolomonIslands, - '677' - ], - [ - 'Somalia', - CountryISO.Somalia, - '252' - ], - [ - 'South Africa', - CountryISO.SouthAfrica, - '27' - ], - [ - 'South Korea', - CountryISO.SouthKorea, - '82' - ], - [ - 'South Sudan', - CountryISO.SouthSudan, - '211' - ], - [ - 'Spain', - CountryISO.Spain, - '34' - ], - [ - 'Sri Lanka', - CountryISO.SriLanka, - '94' - ], - [ - 'Sudan', - CountryISO.Sudan, - '249' - ], - [ - 'Suriname', - CountryISO.Suriname, - '597' - ], - [ - 'Svalbard and Jan Mayen', - CountryISO.SvalbardAndJanMayen, - '47', - 1 - ], - [ - 'Swaziland', - CountryISO.Swaziland, - '268' - ], - [ - 'Sweden', - CountryISO.Sweden, - '46' - ], - [ - 'Switzerland', - CountryISO.Switzerland, - '41' - ], - [ - 'Syria', - CountryISO.Syria, - '963' - ], - [ - 'Taiwan', - CountryISO.Taiwan, - '886' - ], - [ - 'Tajikistan', - CountryISO.Tajikistan, - '992' - ], - [ - 'Tanzania', - CountryISO.Tanzania, - '255' - ], - [ - 'Thailand', - CountryISO.Thailand, - '66' - ], - [ - 'Timor-Leste', - CountryISO.TimorLeste, - '670' - ], - [ - 'Togo', - CountryISO.Togo, - '228' - ], - [ - 'Tokelau', - CountryISO.Tokelau, - '690' - ], - [ - 'Tonga', - CountryISO.Tonga, - '676' - ], - [ - 'Trinidad and Tobago', - CountryISO.TrinidadAndTobago, - '1', - 1, - [ - '868', - ] - ], - [ - 'Tunisia', - CountryISO.Tunisia, - '216' - ], - [ - 'Turkey', - CountryISO.Turkey, - '90' - ], - [ - 'Turkmenistan', - CountryISO.Turkmenistan, - '993' - ], - [ - 'Turks and Caicos Islands', - CountryISO.TurksAndCaicosIslands, - '1649' - ], - [ - 'Tuvalu', - CountryISO.Tuvalu, - '688' - ], - [ - 'U.S. Virgin Islands', - CountryISO.USVirginIslands, - '1', - 1, - [ - '340', - ] - ], - [ - 'Uganda', - CountryISO.Uganda, - '256' - ], - [ - 'Ukraine', - CountryISO.Ukraine, - '380' - ], - [ - 'United Arab Emirates', - CountryISO.UnitedArabEmirates, - '971' - ], - [ - 'United Kingdom', - CountryISO.UnitedKingdom, - '44', - 0 - ], - [ - 'United States', - CountryISO.UnitedStates, - '1', - 0 - ], - [ - 'Uruguay', - CountryISO.Uruguay, - '598' - ], - [ - 'Uzbekistan', - CountryISO.Uzbekistan, - '998' - ], - [ - 'Vanuatu', - CountryISO.Vanuatu, - '678' - ], - [ - 'Vatican City', - CountryISO.VaticanCity, - '39', - 1 - ], - [ - 'Venezuela', - CountryISO.Venezuela, - '58' - ], - [ - 'Vietnam', - CountryISO.Vietnam, - '84' - ], - [ - 'Wallis and Futuna', - CountryISO.WallisAndFutuna, - '681' - ], - [ - 'Western Sahara', - CountryISO.WesternSahara, - '212', - 1 - ], - [ - 'Yemen', - CountryISO.Yemen, - '967' - ], - [ - 'Zambia', - CountryISO.Zambia, - '260' - ], - [ - 'Zimbabwe', - CountryISO.Zimbabwe, - '263' - ], - [ - 'Åland Islands', - CountryISO.ÅlandIslands, - '358', - 1 - ] + public allCountries: Array = [ + {name: 'Afghanistan', iso2: CountryISO.Afghanistan, dialCode: '93', flag: '🇦🇫'}, + {name: 'Albania', iso2: CountryISO.Albania, dialCode: '355', flag: '🇦🇱'}, + {name: 'Algeria', iso2: CountryISO.Algeria, dialCode: '213', flag: '🇩🇿'}, + {name: 'American Samoa', iso2: CountryISO.AmericanSamoa, dialCode: '1', flag: '🇦🇸'}, + {name: 'Andorra', iso2: CountryISO.Andorra, dialCode: '376', flag: '🇦🇩'}, + {name: 'Angola', iso2: CountryISO.Angola, dialCode: '244', flag: '🇦🇴'}, + {name: 'Anguilla', iso2: CountryISO.Anguilla, dialCode: '1', flag: '🇦🇮'}, + {name: 'Antigua and Barbuda', iso2: CountryISO.AntiguaAndBarbuda, dialCode: '1', flag: '🇦🇬'}, + {name: 'Argentina', iso2: CountryISO.Argentina, dialCode: '54', flag: '🇦🇷'}, + {name: 'Armenia', iso2: CountryISO.Armenia, dialCode: '374', flag: '🇦🇲'}, + {name: 'Aruba', iso2: CountryISO.Aruba, dialCode: '297', flag: '🇦🇼'}, + {name: 'Australia', iso2: CountryISO.Australia, dialCode: '61', flag: '🇦🇺'}, + {name: 'Austria', iso2: CountryISO.Austria, dialCode: '43', flag: '🇦🇹'}, + {name: 'Azerbaijan', iso2: CountryISO.Azerbaijan, dialCode: '994', flag: '🇦🇿'}, + {name: 'Bahamas', iso2: CountryISO.Bahamas, dialCode: '1', flag: '🇧🇸'}, + {name: 'Bahrain', iso2: CountryISO.Bahrain, dialCode: '973', flag: '🇧🇭'}, + {name: 'Bangladesh', iso2: CountryISO.Bangladesh, dialCode: '880', flag: '🇧🇩'}, + {name: 'Barbados', iso2: CountryISO.Barbados, dialCode: '1', flag: '🇧🇧'}, + {name: 'Belarus', iso2: CountryISO.Belarus, dialCode: '375', flag: '🇧🇾'}, + {name: 'Belgium', iso2: CountryISO.Belgium, dialCode: '32', flag: '🇧🇪'}, + {name: 'Belize', iso2: CountryISO.Belize, dialCode: '501', flag: '🇧🇿'}, + {name: 'Benin', iso2: CountryISO.Benin, dialCode: '229', flag: '🇧🇯'}, + {name: 'Bermuda', iso2: CountryISO.Bermuda, dialCode: '1', flag: '🇧🇲'}, + {name: 'Bhutan', iso2: CountryISO.Bhutan, dialCode: '975', flag: '🇧🇹'}, + {name: 'Bolivia', iso2: CountryISO.Bolivia, dialCode: '591', flag: '🇧🇴'}, + {name: 'Bosnia and Herzegovina', iso2: CountryISO.BosniaAndHerzegovina, dialCode: '387', flag: '🇧🇦'}, + {name: 'Botswana', iso2: CountryISO.Botswana, dialCode: '267', flag: '🇧🇼'}, + {name: 'Brazil', iso2: CountryISO.Brazil, dialCode: '55', flag: '🇧🇷'}, + {name: 'British Indian Ocean Territory', iso2: CountryISO.BritishIndianOceanTerritory, dialCode: '246', flag: '🇮🇴'}, + {name: 'British Virgin Islands', iso2: CountryISO.BritishVirginIslands, dialCode: '1', flag: '🇻🇬'}, + {name: 'Brunei', iso2: CountryISO.Brunei, dialCode: '673', flag: '🇧🇳'}, + {name: 'Bulgaria', iso2: CountryISO.Bulgaria, dialCode: '359', flag: '🇧🇬'}, + {name: 'Burkina Faso', iso2: CountryISO.BurkinaFaso, dialCode: '226', flag: '🇧🇫'}, + {name: 'Burundi', iso2: CountryISO.Burundi, dialCode: '257', flag: '🇧🇮'}, + {name: 'Cambodia', iso2: CountryISO.Cambodia, dialCode: '855', flag: '🇰🇭'}, + {name: 'Cameroon', iso2: CountryISO.Cameroon, dialCode: '237', flag: '🇨🇲'}, + {name: 'Canada', iso2: CountryISO.Canada, dialCode: '1', flag: '🇨🇦'}, + {name: 'Cape Verde', iso2: CountryISO.CapeVerde, dialCode: '238', flag: '🇨🇻'}, + {name: 'Caribbean Netherlands', iso2: CountryISO.CaribbeanNetherlands, dialCode: '599', flag: '🇧🇶'}, + {name: 'Cayman Islands', iso2: CountryISO.CaymanIslands, dialCode: '1', flag: '🇰🇾'}, + {name: 'Central African Republic', iso2: CountryISO.CentralAfricanRepublic, dialCode: '236', flag: '🇨🇫'}, + {name: 'Chad', iso2: CountryISO.Chad, dialCode: '235', flag: '🇹🇩'}, + {name: 'Chile', iso2: CountryISO.Chile, dialCode: '56', flag: '🇨🇱'}, + {name: 'China', iso2: CountryISO.China, dialCode: '86', flag: '🇨🇳'}, + {name: 'Christmas Island', iso2: CountryISO.ChristmasIsland, dialCode: '61', flag: '🇨🇽'}, + {name: 'Cocos Islands', iso2: CountryISO.Cocos, dialCode: '61', flag: '🇨🇨'}, + {name: 'Colombia', iso2: CountryISO.Colombia, dialCode: '57', flag: '🇨🇨'}, + {name: 'Comoros', iso2: CountryISO.Comoros, dialCode: '269', flag: '🇰🇲'}, + {name: 'Congo-Kinshasa', iso2: CountryISO.CongoDRCJamhuriYaKidemokrasiaYaKongo, dialCode: '243', flag: '🇨🇩'}, + {name: 'Congo-Brazzaville', iso2: CountryISO.CongoRepublicCongoBrazzaville, dialCode: '242', flag: '🇨🇬'}, + {name: 'Cook Islands', iso2: CountryISO.CookIslands, dialCode: '682', flag: '🇨🇰'}, + {name: 'Costa Rica', iso2: CountryISO.CostaRica, dialCode: '506', flag: '🇨🇷'}, + {name: 'Côte d’Ivoire', iso2: CountryISO.CôteDIvoire, dialCode: '225', flag: '🇨🇮'}, + {name: 'Croatia', iso2: CountryISO.Croatia, dialCode: '385', flag: '🇭🇷'}, + {name: 'Cuba', iso2: CountryISO.Cuba, dialCode: '53', flag: '🇨🇺'}, + {name: 'Curaçao', iso2: CountryISO.Curaçao, dialCode: '599', flag: '🇨🇼'}, + {name: 'Cyprus', iso2: CountryISO.Cyprus, dialCode: '357', flag: '🇨🇾'}, + {name: 'Czech Republic', iso2: CountryISO.CzechRepublic, dialCode: '420', flag: '🇨🇿'}, + {name: 'Denmark', iso2: CountryISO.Denmark, dialCode: '45', flag: '🇩🇰'}, + {name: 'Djibouti', iso2: CountryISO.Djibouti, dialCode: '253', flag: '🇩🇯'}, + {name: 'Dominica', iso2: CountryISO.Dominica, dialCode: '1767', flag: '🇩🇲'}, + {name: 'Dominican Republic', iso2: CountryISO.DominicanRepublic, dialCode: '1', flag: '🇩🇴'}, + {name: 'Ecuador', iso2: CountryISO.Ecuador, dialCode: '593', flag: '🇪🇨'}, + {name: 'Egypt', iso2: CountryISO.Egypt, dialCode: '20', flag: '🇪🇬'}, + {name: 'El Salvador', iso2: CountryISO.ElSalvador, dialCode: '503', flag: '🇸🇻'}, + {name: 'Equatorial Guinea', iso2: CountryISO.EquatorialGuinea, dialCode: '240', flag: '🇬🇶'}, + {name: 'Eritrea', iso2: CountryISO.Eritrea, dialCode: '291', flag: '🇪🇷'}, + {name: 'Estonia', iso2: CountryISO.Estonia, dialCode: '372', flag: '🇪🇪'}, + {name: 'Ethiopia', iso2: CountryISO.Ethiopia, dialCode: '251', flag: '🇪🇹'}, + {name: 'Falkland Islands', iso2: CountryISO.FalklandIslands, dialCode: '500', flag: '🇫🇰'}, + {name: 'Faroe Islands', iso2: CountryISO.FaroeIslands, dialCode: '298', flag: '🇫🇴'}, + {name: 'Fiji', iso2: CountryISO.Fiji, dialCode: '679', flag: '🇫🇯'}, + {name: 'Finland', iso2: CountryISO.Finland, dialCode: '358', flag: '🇫🇮'}, + {name: 'France', iso2: CountryISO.France, dialCode: '33', flag: '🇫🇷'}, + {name: 'French Guiana', iso2: CountryISO.FrenchGuiana, dialCode: '594', flag: '🇬🇫'}, + {name: 'French Polynesia', iso2: CountryISO.FrenchPolynesia, dialCode: '689', flag: '🇵🇫'}, + {name: 'Gabon', iso2: CountryISO.Gabon, dialCode: '241', flag: '🇬🇦'}, + {name: 'Gambia', iso2: CountryISO.Gambia, dialCode: '220', flag: '🇬🇲'}, + {name: 'Georgia', iso2: CountryISO.Georgia, dialCode: '995', flag: '🇬🇪'}, + {name: 'Germany', iso2: CountryISO.Germany, dialCode: '49', flag: '🇩🇪'}, + {name: 'Ghana', iso2: CountryISO.Ghana, dialCode: '233', flag: '🇬🇭'}, + {name: 'Gibraltar', iso2: CountryISO.Gibraltar, dialCode: '350', flag: '🇬🇮'}, + {name: 'Greece', iso2: CountryISO.Greece, dialCode: '30', flag: '🇬🇷'}, + {name: 'Greenland', iso2: CountryISO.Greenland, dialCode: '299', flag: '🇬🇱'}, + {name: 'Grenada', iso2: CountryISO.Grenada, dialCode: '1473', flag: '🇬🇩'}, + {name: 'Guadeloupe', iso2: CountryISO.Guadeloupe, dialCode: '590', flag: '🇬🇵'}, + {name: 'Guam', iso2: CountryISO.Guam, dialCode: '1', flag: '🇬🇺'}, + {name: 'Guatemala', iso2: CountryISO.Guatemala, dialCode: '502', flag: '🇬🇹'}, + {name: 'Guernsey', iso2: CountryISO.Guernsey, dialCode: '44', flag: '🇬🇬'}, + {name: 'Guinea', iso2: CountryISO.Guinea, dialCode: '224', flag: '🇬🇳'}, + {name: 'Guinea-Bissau', iso2: CountryISO.GuineaBissau, dialCode: '245', flag: '🇬🇼'}, + {name: 'Guyana', iso2: CountryISO.Guyana, dialCode: '592', flag: '🇬🇾'}, + {name: 'Haiti', iso2: CountryISO.Haiti, dialCode: '509', flag: '🇭🇹'}, + {name: 'Honduras', iso2: CountryISO.Honduras, dialCode: '504', flag: '🇭🇳'}, + {name: 'Hong Kong', iso2: CountryISO.HongKong, dialCode: '852', flag: '🇭🇰'}, + {name: 'Hungary', iso2: CountryISO.Hungary, dialCode: '36', flag: '🇭🇺'}, + {name: 'Iceland', iso2: CountryISO.Iceland, dialCode: '354', flag: '🇮🇸'}, + {name: 'India', iso2: CountryISO.India, dialCode: '91', flag: '🇮🇳'}, + {name: 'Indonesia', iso2: CountryISO.Indonesia, dialCode: '62', flag: '🇮🇩'}, + {name: 'Iran', iso2: CountryISO.Iran, dialCode: '98', flag: '🇮🇷'}, + {name: 'Iraq', iso2: CountryISO.Iraq, dialCode: '964', flag: '🇮🇶'}, + {name: 'Ireland', iso2: CountryISO.Ireland, dialCode: '353', flag: '🇮🇪'}, + {name: 'Isle of Man', iso2: CountryISO.IsleOfMan, dialCode: '44', flag: '🇮🇲'}, + {name: 'Israel', iso2: CountryISO.Israel, dialCode: '972', flag: '🇮🇱'}, + {name: 'Italy', iso2: CountryISO.Italy, dialCode: '39', flag: '🇮🇹'}, + {name: 'Jamaica', iso2: CountryISO.Jamaica, dialCode: '1', flag: '🇯🇲'}, + {name: 'Japan', iso2: CountryISO.Japan, dialCode: '81', flag: '🇯🇵'}, + {name: 'Jersey', iso2: CountryISO.Jersey, dialCode: '44', flag: '🇯🇪'}, + {name: 'Jordan', iso2: CountryISO.Jordan, dialCode: '962', flag: '🇯🇴'}, + {name: 'Kazakhstan', iso2: CountryISO.Kazakhstan, dialCode: '7', flag: '🇰🇿'}, + {name: 'Kenya', iso2: CountryISO.Kenya, dialCode: '254', flag: '🇰🇪'}, + {name: 'Kiribati', iso2: CountryISO.Kiribati, dialCode: '686', flag: '🇰🇮'}, + {name: 'Kosovo', iso2: CountryISO.Kosovo, dialCode: '383', flag: '🇽🇰'}, + {name: 'Kuwait', iso2: CountryISO.Kuwait, dialCode: '965', flag: '🇰🇼'}, + {name: 'Kyrgyzstan', iso2: CountryISO.Kyrgyzstan, dialCode: '996', flag: '🇰🇬'}, + {name: 'Laos', iso2: CountryISO.Laos, dialCode: '856', flag: '🇱🇦'}, + {name: 'Latvia', iso2: CountryISO.Latvia, dialCode: '371', flag: '🇱🇻'}, + {name: 'Lebanon', iso2: CountryISO.Lebanon, dialCode: '961', flag: '🇱🇧'}, + {name: 'Lesotho', iso2: CountryISO.Lesotho, dialCode: '266', flag: '🇱🇸'}, + {name: 'Liberia', iso2: CountryISO.Liberia, dialCode: '231', flag: '🇱🇷'}, + {name: 'Libya', iso2: CountryISO.Libya, dialCode: '218', flag: '🇱🇾'}, + {name: 'Liechtenstein', iso2: CountryISO.Liechtenstein, dialCode: '423', flag: '🇱🇮'}, + {name: 'Lithuania', iso2: CountryISO.Lithuania, dialCode: '370', flag: '🇱🇹'}, + {name: 'Luxembourg', iso2: CountryISO.Luxembourg, dialCode: '352', flag: '🇱🇺'}, + {name: 'Macau', iso2: CountryISO.Macau, dialCode: '853', flag: '🇲🇴'}, + {name: 'Macedonia', iso2: CountryISO.Macedonia, dialCode: '389', flag: '🇲🇰'}, + {name: 'Madagascar', iso2: CountryISO.Madagascar, dialCode: '261', flag: '🇲🇬'}, + {name: 'Malawi', iso2: CountryISO.Malawi, dialCode: '265', flag: '🇲🇼'}, + {name: 'Malaysia', iso2: CountryISO.Malaysia, dialCode: '60', flag: '🇲🇾'}, + {name: 'Maldives', iso2: CountryISO.Maldives, dialCode: '960', flag: '🇲🇻'}, + {name: 'Mali', iso2: CountryISO.Mali, dialCode: '223', flag: '🇲🇱'}, + {name: 'Malta', iso2: CountryISO.Malta, dialCode: '356', flag: '🇲🇹'}, + {name: 'Marshall Islands', iso2: CountryISO.MarshallIslands, dialCode: '692', flag: '🇲🇭'}, + {name: 'Martinique', iso2: CountryISO.Martinique, dialCode: '596', flag: '🇲🇶'}, + {name: 'Mauritania', iso2: CountryISO.Mauritania, dialCode: '222', flag: '🇲🇷'}, + {name: 'Mauritius', iso2: CountryISO.Mauritius, dialCode: '230', flag: '🇲🇺'}, + {name: 'Mayotte', iso2: CountryISO.Mayotte, dialCode: '262', flag: '🇾🇹'}, + {name: 'Mexico', iso2: CountryISO.Mexico, dialCode: '52', flag: '🇲🇽'}, + {name: 'Micronesia', iso2: CountryISO.Micronesia, dialCode: '691', flag: '🇫🇲'}, + {name: 'Moldova', iso2: CountryISO.Moldova, dialCode: '373', flag: '🇲🇩'}, + {name: 'Monaco', iso2: CountryISO.Monaco, dialCode: '377', flag: '🇲🇨'}, + {name: 'Mongolia', iso2: CountryISO.Mongolia, dialCode: '976', flag: '🇲🇳'}, + {name: 'Montenegro', iso2: CountryISO.Montenegro, dialCode: '382', flag: '🇲🇪'}, + {name: 'Montserrat', iso2: CountryISO.Montserrat, dialCode: '1', flag: '🇲🇸'}, + {name: 'Morocco', iso2: CountryISO.Morocco, dialCode: '212', flag: '🇲🇦'}, + {name: 'Mozambique', iso2: CountryISO.Mozambique, dialCode: '258', flag: '🇲🇿'}, + {name: 'Myanmar', iso2: CountryISO.Myanmar, dialCode: '95', flag: '🇲🇲'}, + {name: 'Namibia', iso2: CountryISO.Namibia, dialCode: '264', flag: '🇳🇦'}, + {name: 'Nauru', iso2: CountryISO.Nauru, dialCode: '674', flag: '🇳🇷'}, + {name: 'Nepal', iso2: CountryISO.Nepal, dialCode: '977', flag: '🇳🇵'}, + {name: 'Netherlands', iso2: CountryISO.Netherlands, dialCode: '31', flag: '🇳🇱'}, + {name: 'New Caledonia', iso2: CountryISO.NewCaledonia, dialCode: '687', flag: '🇳🇨'}, + {name: 'New Zealand', iso2: CountryISO.NewZealand, dialCode: '64', flag: '🇳🇿'}, + {name: 'Nicaragua', iso2: CountryISO.Nicaragua, dialCode: '505', flag: '🇳🇮'}, + {name: 'Niger', iso2: CountryISO.Niger, dialCode: '227', flag: '🇳🇪'}, + {name: 'Nigeria', iso2: CountryISO.Nigeria, dialCode: '234', flag: '🇳🇬'}, + {name: 'Niue', iso2: CountryISO.Niue, dialCode: '683', flag: '🇳🇺'}, + {name: 'Norfolk Island', iso2: CountryISO.NorfolkIsland, dialCode: '672', flag: '🇳🇫'}, + {name: 'North Korea', iso2: CountryISO.NorthKorea, dialCode: '850', flag: '🇰🇵'}, + {name: 'Northern Mariana Islands', iso2: CountryISO.NorthernMarianaIslands, dialCode: '1670', flag: '🇲🇵'}, + {name: 'Norway', iso2: CountryISO.Norway, dialCode: '47', flag: '🇳🇴'}, + {name: 'Oman', iso2: CountryISO.Oman, dialCode: '968', flag: '🇴🇲'}, + {name: 'Pakistan', iso2: CountryISO.Pakistan, dialCode: '92', flag: '🇵🇰'}, + {name: 'Palau', iso2: CountryISO.Palau, dialCode: '680', flag: '🇵🇼'}, + {name: 'Palestine', iso2: CountryISO.Palestine, dialCode: '970', flag: '🇵🇸'}, + {name: 'Panama', iso2: CountryISO.Panama, dialCode: '507', flag: '🇵🇦'}, + {name: 'Papua New Guinea', iso2: CountryISO.PapuaNewGuinea, dialCode: '675', flag: '🇵🇬'}, + {name: 'Paraguay', iso2: CountryISO.Paraguay, dialCode: '595', flag: '🇵🇾'}, + {name: 'Peru', iso2: CountryISO.Peru, dialCode: '51', flag: '🇵🇪'}, + {name: 'Philippines', iso2: CountryISO.Philippines, dialCode: '63', flag: '🇵🇭'}, + {name: 'Poland', iso2: CountryISO.Poland, dialCode: '48', flag: '🇵🇱'}, + {name: 'Portugal', iso2: CountryISO.Portugal, dialCode: '351', flag: '🇵🇹'}, + {name: 'Puerto Rico', iso2: CountryISO.PuertoRico, dialCode: '1', flag: '🇵🇷'}, + {name: 'Qatar', iso2: CountryISO.Qatar, dialCode: '974', flag: '🇶🇦'}, + {name: 'Réunion', iso2: CountryISO.Réunion, dialCode: '262', flag: '🇷🇪'}, + {name: 'Romania', iso2: CountryISO.Romania, dialCode: '40', flag: '🇷🇴'}, + {name: 'Russia', iso2: CountryISO.Russia, dialCode: '7', flag: '🇷🇺'}, + {name: 'Rwanda', iso2: CountryISO.Rwanda, dialCode: '250', flag: '🇷🇼'}, + {name: 'Saint Barthélemy', iso2: CountryISO.SaintBarthélemy, dialCode: '590', flag: '🇧🇱'}, + {name: 'Saint Helena', iso2: CountryISO.SaintHelena, dialCode: '290', flag: '🇸🇭'}, + {name: 'Saint Kitts and Nevis', iso2: CountryISO.SaintKittsAndNevis, dialCode: '1869', flag: '🇰🇳'}, + {name: 'Saint Lucia', iso2: CountryISO.SaintLucia, dialCode: '1', flag: '🇱🇨'}, + {name: 'Saint Martin', iso2: CountryISO.SaintMartin, dialCode: '590', flag: '🇲🇫'}, + {name: 'Saint Pierre and Miquelon', iso2: CountryISO.SaintPierreAndMiquelon, dialCode: '508', flag: '🇵🇲'}, + {name: 'Saint Vincent and the Grenadines', iso2: CountryISO.SaintVincentAndTheGrenadines, dialCode: '1', flag: '🇻🇨'}, + {name: 'Samoa', iso2: CountryISO.Samoa, dialCode: '685', flag: '🇼🇸'}, + {name: 'San Marino', iso2: CountryISO.SanMarino, dialCode: '378', flag: '🇸🇲'}, + {name: 'São Tomé and Príncipe', iso2: CountryISO.SãoToméAndPríncipe, dialCode: '239', flag: '🇸🇹'}, + {name: 'Saudi Arabia', iso2: CountryISO.SaudiArabia, dialCode: '966', flag: '🇸🇦'}, + {name: 'Senegal', iso2: CountryISO.Senegal, dialCode: '221', flag: '🇸🇳'}, + {name: 'Serbia', iso2: CountryISO.Serbia, dialCode: '381', flag: '🇷🇸'}, + {name: 'Seychelles', iso2: CountryISO.Seychelles, dialCode: '248', flag: '🇸🇨'}, + {name: 'Sierra Leone', iso2: CountryISO.SierraLeone, dialCode: '232', flag: '🇸🇱'}, + {name: 'Singapore', iso2: CountryISO.Singapore, dialCode: '65', flag: '🇸🇬'}, + {name: 'Sint Maarten', iso2: CountryISO.SintMaarten, dialCode: '1', flag: '🇸🇽'}, + {name: 'Slovakia', iso2: CountryISO.Slovakia, dialCode: '421', flag: '🇸🇰'}, + {name: 'Slovenia', iso2: CountryISO.Slovenia, dialCode: '386', flag: '🇸🇮'}, + {name: 'Solomon Islands', iso2: CountryISO.SolomonIslands, dialCode: '677', flag: '🇸🇧'}, + {name: 'Somalia', iso2: CountryISO.Somalia, dialCode: '252', flag: '🇸🇴'}, + {name: 'South Africa', iso2: CountryISO.SouthAfrica, dialCode: '27', flag: '🇿🇦'}, + {name: 'South Korea', iso2: CountryISO.SouthKorea, dialCode: '82', flag: '🇰🇷'}, + {name: 'South Sudan', iso2: CountryISO.SouthSudan, dialCode: '211', flag: '🇸🇸'}, + {name: 'Spain', iso2: CountryISO.Spain, dialCode: '34', flag: '🇪🇸'}, + {name: 'Sri Lanka', iso2: CountryISO.SriLanka, dialCode: '94', flag: '🇱🇰'}, + {name: 'Sudan', iso2: CountryISO.Sudan, dialCode: '249', flag: '🇸🇩'}, + {name: 'Suriname: ', iso2: CountryISO.Suriname, dialCode: '597', flag: '🇸🇷'}, + {name: 'Svalbard and Jan Mayen', iso2: CountryISO.SvalbardAndJanMayen, dialCode: '47', flag: '🇸🇯'}, + {name: 'Swaziland', iso2: CountryISO.Swaziland, dialCode: '268', flag: '🇸🇿'}, + {name: 'Sweden', iso2: CountryISO.Sweden, dialCode: '46', flag: '🇸🇪'}, + {name: 'Switzerland', iso2: CountryISO.Switzerland, dialCode: '41', flag: '🇨🇭'}, + {name: 'Syria', iso2: CountryISO.Syria, dialCode: '963', flag: '🇸🇾'}, + {name: 'Taiwan', iso2: CountryISO.Taiwan, dialCode: '886', flag: '🇹🇼'}, + {name: 'Tajikistan', iso2: CountryISO.Tajikistan, dialCode: '992', flag: '🇹🇯'}, + {name: 'Tanzania', iso2: CountryISO.Tanzania, dialCode: '255', flag: '🇹🇿'}, + {name: 'Thailand', iso2: CountryISO.Thailand, dialCode: '66', flag: '🇹🇭'}, + {name: 'Timor-Leste', iso2: CountryISO.TimorLeste, dialCode: '670', flag: '🇹🇱'}, + {name: 'Togo', iso2: CountryISO.Togo, dialCode: '228', flag: '🇹🇬'}, + {name: 'Tokelau', iso2: CountryISO.Tokelau, dialCode: '690', flag: '🇹🇰'}, + {name: 'Tonga', iso2: CountryISO.Tonga, dialCode: '676', flag: '🇹🇴'}, + {name: 'Trinidad and Tobago', iso2: CountryISO.TrinidadAndTobago, dialCode: '1', flag: '🇹🇹'}, + {name: 'Tunisia', iso2: CountryISO.Tunisia, dialCode: '216', flag: '🇹🇳'}, + {name: 'Turkey', iso2: CountryISO.Turkey, dialCode: '90', flag: '🇹🇷'}, + {name: 'Turkmenistan', iso2: CountryISO.Turkmenistan, dialCode: '993', flag: '🇹🇲'}, + {name: 'Turks and Caicos Islands', iso2: CountryISO.TurksAndCaicosIslands, dialCode: '1649', flag: '🇹🇨'}, + {name: 'Tuvalu', iso2: CountryISO.Tuvalu, dialCode: '688', flag: '🇹🇻'}, + {name: 'U.S. Virgin Islands', iso2: CountryISO.USVirginIslands, dialCode: '1', flag: '🇻🇮'}, + {name: 'Uganda', iso2: CountryISO.Uganda, dialCode: '256', flag: '🇺🇬'}, + {name: 'Ukraine', iso2: CountryISO.Ukraine, dialCode: '380', flag: '🇺🇦'}, + {name: 'United Arab Emirates', iso2: CountryISO.UnitedArabEmirates, dialCode: '971', flag: '🇦🇪'}, + {name: 'United Kingdom', iso2: CountryISO.UnitedKingdom, dialCode: '44', flag: '🇬🇧'}, + {name: 'United States', iso2: CountryISO.UnitedStates, dialCode: '1', flag: '🇺🇸'}, + {name: 'Uruguay', iso2: CountryISO.Uruguay, dialCode: '598', flag: '🇺🇾'}, + {name: 'Uzbekistan', iso2: CountryISO.Uzbekistan, dialCode: '998', flag: '🇺🇿'}, + {name: 'Vanuatu', iso2: CountryISO.Vanuatu, dialCode: '678', flag: '🇻🇺'}, + {name: 'Vatican City', iso2: CountryISO.VaticanCity, dialCode: '39', flag: '🇻🇦'}, + {name: 'Venezuela', iso2: CountryISO.Venezuela, dialCode: '58', flag: '🇻🇪'}, + {name: 'Vietnam', iso2: CountryISO.Vietnam, dialCode: '84', flag: '🇻🇳'}, + {name: 'Wallis and Futuna', iso2: CountryISO.WallisAndFutuna, dialCode: '681', flag: '🇼🇫'}, + {name: 'Western Sahara', iso2: CountryISO.WesternSahara, dialCode: '212', flag: '🇪🇭'}, + {name: 'Yemen', iso2: CountryISO.Yemen, dialCode: '967', flag: '🇾🇪'}, + {name: 'Zambia', iso2: CountryISO.Zambia, dialCode: '260', flag: '🇿🇲'}, + {name: 'Zimbabwe', iso2: CountryISO.Zimbabwe, dialCode: '263', flag: '🇿🇼'}, + {name: 'Åland Islands', iso2: CountryISO.ÅlandIslands, dialCode: '358', flag: '🇦🇽'} ]; } From afc3820b79a1c54330fb6d30e1808e7d49de4b34 Mon Sep 17 00:00:00 2001 From: fe-dev Date: Mon, 13 Jun 2022 16:48:15 +0300 Subject: [PATCH 786/798] UI: Change placeholder --- ui-ngx/src/app/shared/components/phone-input.component.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui-ngx/src/app/shared/components/phone-input.component.html b/ui-ngx/src/app/shared/components/phone-input.component.html index 4b81391b5f..039f52a272 100644 --- a/ui-ngx/src/app/shared/components/phone-input.component.html +++ b/ui-ngx/src/app/shared/components/phone-input.component.html @@ -28,12 +28,12 @@
- phone-input.phone-input-label + security.2fa.dialog.sms-step-label Date: Mon, 13 Jun 2022 18:06:32 +0300 Subject: [PATCH 787/798] UI: Fixed vulnerabilities in tb-web-ui --- msa/web-ui/package.json | 21 +- msa/web-ui/pom.xml | 4 +- msa/web-ui/yarn.lock | 966 ++++++++++++++++++++++------------------ 3 files changed, 537 insertions(+), 454 deletions(-) diff --git a/msa/web-ui/package.json b/msa/web-ui/package.json index 1cd252e0de..81e0663613 100644 --- a/msa/web-ui/package.json +++ b/msa/web-ui/package.json @@ -13,14 +13,14 @@ }, "dependencies": { "compression": "^1.7.4", - "config": "^3.3.1", + "config": "^3.3.7", "connect-history-api-fallback": "^1.6.0", - "express": "^4.17.1", + "express": "^4.18.1", "http": "0.0.0", "http-proxy": "^1.18.1", - "js-yaml": "^3.14.0", - "winston": "^3.3.3", - "winston-daily-rotate-file": "^4.5.0" + "js-yaml": "^4.1.0", + "winston": "^3.7.2", + "winston-daily-rotate-file": "^4.7.1" }, "nyc": { "exclude": [ @@ -31,13 +31,18 @@ ] }, "devDependencies": { - "fs-extra": "^10.0.0", - "nodemon": "^2.0.12", - "pkg": "^5.3.1" + "fs-extra": "^10.1.0", + "nodemon": "^2.0.16", + "pkg": "^5.7.0" }, "pkg": { "assets": [ "node_modules/config/**/*.*" ] + }, + "resolutions": { + "color-string": "^1.5.5", + "follow-redirects": "^1.14.8", + "minimist": "^1.2.6" } } diff --git a/msa/web-ui/pom.xml b/msa/web-ui/pom.xml index f04b339a8e..c949555d46 100644 --- a/msa/web-ui/pom.xml +++ b/msa/web-ui/pom.xml @@ -80,8 +80,8 @@ install-node-and-yarn - v12.16.1 - v1.22.4 + v16.13.1 + v1.22.17 diff --git a/msa/web-ui/yarn.lock b/msa/web-ui/yarn.lock index 2c7012b515..2204573d19 100644 --- a/msa/web-ui/yarn.lock +++ b/msa/web-ui/yarn.lock @@ -2,25 +2,29 @@ # yarn lockfile v1 -"@babel/helper-validator-identifier@^7.12.11": - version "7.14.9" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.9.tgz#6654d171b2024f6d8ee151bf2509699919131d48" - integrity sha512-pQYxPY0UP6IHISRitNe8bsijHex4TWZXi2HwKVsjPiltzlhse2znVcm9Ace510VT1kxIHjGJCZZQBX2gJDbo0g== - -"@babel/parser@7.13.13": - version "7.13.13" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.13.13.tgz#42f03862f4aed50461e543270916b47dd501f0df" - integrity sha512-OhsyMrqygfk5v8HmWwOzlYjJrtLaFhF34MrfG/Z73DgYCI6ojNUTUp2TYbtnjo8PegeJp12eamsNettCQjKjVw== - -"@babel/types@7.13.12": - version "7.13.12" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.13.12.tgz#edbf99208ef48852acdff1c8a681a1e4ade580cd" - integrity sha512-K4nY2xFN4QMvQwkQ+zmBDp6ANMbVNw6BbxWmYA4qNjhR9W+Lj/8ky5MEY2Me5r+B2c6/v6F53oMndG+f9s3IiA== - dependencies: - "@babel/helper-validator-identifier" "^7.12.11" - lodash "^4.17.19" +"@babel/helper-validator-identifier@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz#e8c602438c4a8195751243da9031d1607d247cad" + integrity sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw== + +"@babel/parser@7.17.10": + version "7.17.10" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.17.10.tgz#873b16db82a8909e0fbd7f115772f4b739f6ce78" + integrity sha512-n2Q6i+fnJqzOaq2VkdXxy2TCPCWQZHiCo0XqmrCvDWcZQKRyZzYi4Z0yxlBuN0w+r2ZHmre+Q087DSrw3pbJDQ== + +"@babel/types@7.17.10": + version "7.17.10" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.17.10.tgz#d35d7b4467e439fcf06d195f8100e0fea7fc82c4" + integrity sha512-9O26jG0mBYfGkUYCYZRnBwbVLd1UZOICEr2Em6InB6jVfsAv1GKgwXHmrSg+WFWDmeKTA6vyTZiN8tCSM5Oo3A== + dependencies: + "@babel/helper-validator-identifier" "^7.16.7" to-fast-properties "^2.0.0" +"@colors/colors@1.5.0": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9" + integrity sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ== + "@dabh/diagnostics@^2.0.2": version "2.0.2" resolved "https://registry.yarnpkg.com/@dabh/diagnostics/-/diagnostics-2.0.2.tgz#290d08f7b381b8f94607dc8f471a12c675f9db31" @@ -73,7 +77,7 @@ abbrev@1: resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== -accepts@~1.3.5, accepts@~1.3.7: +accepts@~1.3.5: version "1.3.7" resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd" integrity sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA== @@ -81,6 +85,14 @@ accepts@~1.3.5, accepts@~1.3.7: mime-types "~2.1.24" negotiator "0.6.2" +accepts@~1.3.8: + version "1.3.8" + resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e" + integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw== + dependencies: + mime-types "~2.1.34" + negotiator "0.6.3" + agent-base@6: version "6.0.2" resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" @@ -115,6 +127,11 @@ ansi-regex@^5.0.0: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75" integrity sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg== +ansi-regex@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" + integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== + ansi-styles@^4.0.0: version "4.3.0" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" @@ -130,10 +147,10 @@ ansi-styles@^4.1.0: "@types/color-name" "^1.1.1" color-convert "^2.0.1" -anymatch@~3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.1.tgz#c55ecf02185e2469259399310c173ce31233b142" - integrity sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg== +anymatch@~3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716" + integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg== dependencies: normalize-path "^3.0.0" picomatch "^2.0.4" @@ -151,12 +168,10 @@ are-we-there-yet@~1.1.2: delegates "^1.0.0" readable-stream "^2.0.6" -argparse@^1.0.7: - version "1.0.10" - resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" - integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== - dependencies: - sprintf-js "~1.0.2" +argparse@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" + integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== array-flatten@1.1.1: version "1.1.1" @@ -168,10 +183,10 @@ array-union@^2.1.0: resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== -async@^3.1.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/async/-/async-3.2.0.tgz#b3a2685c5ebb641d3de02d161002c60fc9f85720" - integrity sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw== +async@^3.2.3: + version "3.2.4" + resolved "https://registry.yarnpkg.com/async/-/async-3.2.4.tgz#2d22e00f8cddeb5fde5dd33522b56d1cf569a81c" + integrity sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ== at-least-node@^1.0.0: version "1.0.0" @@ -202,35 +217,37 @@ bl@^4.0.3: inherits "^2.0.4" readable-stream "^3.4.0" -body-parser@1.19.0: - version "1.19.0" - resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.19.0.tgz#96b2709e57c9c4e09a6fd66a8fd979844f69f08a" - integrity sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw== +body-parser@1.20.0: + version "1.20.0" + resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.0.tgz#3de69bd89011c11573d7bfee6a64f11b6bd27cc5" + integrity sha512-DfJ+q6EPcGKZD1QWUjSpqp+Q7bDQTsQIF4zfUAtZ6qk+H/3/QRhg9CEp39ss+/T2vw0+HaidC0ecJj/DRLIaKg== dependencies: - bytes "3.1.0" + bytes "3.1.2" content-type "~1.0.4" debug "2.6.9" - depd "~1.1.2" - http-errors "1.7.2" + depd "2.0.0" + destroy "1.2.0" + http-errors "2.0.0" iconv-lite "0.4.24" - on-finished "~2.3.0" - qs "6.7.0" - raw-body "2.4.0" - type-is "~1.6.17" + on-finished "2.4.1" + qs "6.10.3" + raw-body "2.5.1" + type-is "~1.6.18" + unpipe "1.0.0" -boxen@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/boxen/-/boxen-4.2.0.tgz#e411b62357d6d6d36587c8ac3d5d974daa070e64" - integrity sha512-eB4uT9RGzg2odpER62bBwSLvUeGC+WbRjjyyFhGsKnc8wp/m0+hQsMUvUe3H2V0D5vw0nBdO1hCJoZo5mKeuIQ== +boxen@^5.0.0: + version "5.1.2" + resolved "https://registry.yarnpkg.com/boxen/-/boxen-5.1.2.tgz#788cb686fc83c1f486dfa8a40c68fc2b831d2b50" + integrity sha512-9gYgQKXx+1nP8mP7CzFyaUARhg7D3n1dF/FnErWmu9l6JvGpNUN278h0aSb+QjoiKSWG+iZ3uHrcqk0qrY9RQQ== dependencies: ansi-align "^3.0.0" - camelcase "^5.3.1" - chalk "^3.0.0" - cli-boxes "^2.2.0" - string-width "^4.1.0" - term-size "^2.1.0" - type-fest "^0.8.1" + camelcase "^6.2.0" + chalk "^4.1.0" + cli-boxes "^2.2.1" + string-width "^4.2.2" + type-fest "^0.20.2" widest-line "^3.1.0" + wrap-ansi "^7.0.0" brace-expansion@^1.1.7: version "1.1.11" @@ -240,7 +257,7 @@ brace-expansion@^1.1.7: balanced-match "^1.0.0" concat-map "0.0.1" -braces@^3.0.1, braces@~3.0.2: +braces@^3.0.2, braces@~3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== @@ -260,10 +277,10 @@ bytes@3.0.0: resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048" integrity sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg= -bytes@3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6" - integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg== +bytes@3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" + integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== cacheable-request@^6.0.0: version "6.1.0" @@ -278,20 +295,20 @@ cacheable-request@^6.0.0: normalize-url "^4.1.0" responselike "^1.0.2" -camelcase@^5.3.1: - version "5.3.1" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" - integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== - -chalk@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-3.0.0.tgz#3f73c2bf526591f574cc492c51e2456349f844e4" - integrity sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg== +call-bind@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" + integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA== dependencies: - ansi-styles "^4.1.0" - supports-color "^7.1.0" + function-bind "^1.1.1" + get-intrinsic "^1.0.2" -chalk@^4.1.0: +camelcase@^6.2.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" + integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== + +chalk@^4.1.0, chalk@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== @@ -299,20 +316,20 @@ chalk@^4.1.0: ansi-styles "^4.1.0" supports-color "^7.1.0" -chokidar@^3.2.2: - version "3.4.2" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.4.2.tgz#38dc8e658dec3809741eb3ef7bb0a47fe424232d" - integrity sha512-IZHaDeBeI+sZJRX7lGcXsdzgvZqKv6sECqsbErJA4mHWfpRrD8B97kSFN4cQz6nGBGiuFia1MKR4d6c1o8Cv7A== +chokidar@^3.5.2: + version "3.5.3" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" + integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== dependencies: - anymatch "~3.1.1" + anymatch "~3.1.2" braces "~3.0.2" - glob-parent "~5.1.0" + glob-parent "~5.1.2" is-binary-path "~2.1.0" is-glob "~4.0.1" normalize-path "~3.0.0" - readdirp "~3.4.0" + readdirp "~3.6.0" optionalDependencies: - fsevents "~2.1.2" + fsevents "~2.3.2" chownr@^1.1.1: version "1.1.4" @@ -324,10 +341,10 @@ ci-info@^2.0.0: resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46" integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ== -cli-boxes@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-2.2.0.tgz#538ecae8f9c6ca508e3c3c95b453fe93cb4c168d" - integrity sha512-gpaBrMAizVEANOpfZp/EEUixTXDyGt7DFzdK5hU+UbWt/J0lB0w20ncZj59Z9a93xHb9u12zF5BS6i9RKbtg4w== +cli-boxes@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-2.2.1.tgz#ddd5035d25094fce220e9cab40a45840a440318f" + integrity sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw== cliui@^7.0.2: version "7.0.4" @@ -374,10 +391,10 @@ color-name@^1.0.0, color-name@~1.1.4: resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== -color-string@^1.5.2: - version "1.5.3" - resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.5.3.tgz#c9bbc5f01b58b5492f3d6857459cb6590ce204cc" - integrity sha512-dC2C5qeWoYkxki5UAXapdjqO672AM4vZuPGRQfO8b5HKuKGBbKWpITyDYN7TOFKvRW7kOgAn3746clDBMDJyQw== +color-string@^1.5.2, color-string@^1.5.5: + version "1.9.1" + resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.9.1.tgz#4467f9146f036f855b764dfb5bf8582bf342c7a4" + integrity sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg== dependencies: color-name "^1.0.0" simple-swizzle "^0.2.2" @@ -390,11 +407,6 @@ color@3.0.x: color-convert "^1.9.1" color-string "^1.5.2" -colors@^1.2.1: - version "1.4.0" - resolved "https://registry.yarnpkg.com/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78" - integrity sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA== - colorspace@1.1.x: version "1.1.2" resolved "https://registry.yarnpkg.com/colorspace/-/colorspace-1.1.2.tgz#e0128950d082b86a2168580796a0aa5d6c68d8c5" @@ -428,10 +440,10 @@ concat-map@0.0.1: resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= -config@^3.3.1: - version "3.3.1" - resolved "https://registry.yarnpkg.com/config/-/config-3.3.1.tgz#b6a70e2908a43b98ed20be7e367edf0cc8ed5a19" - integrity sha512-+2/KaaaAzdwUBE3jgZON11L1ggLLhpf2FsGrfqYFHZW22ySGv/HqYIXrBwKKvn+XZh1UBUjHwAcrfsSkSygT+Q== +config@^3.3.7: + version "3.3.7" + resolved "https://registry.yarnpkg.com/config/-/config-3.3.7.tgz#4310410dc2bf4e0effdca21a12a4035860a24ee4" + integrity sha512-mX/n7GKDYZMqvvkY6e6oBY49W8wxdmQt+ho/5lhwFDXqQW9gI+Ahp8EKp8VAbISPnmf2+Bv5uZK7lKXZ6pf1aA== dependencies: json5 "^2.1.1" @@ -457,12 +469,12 @@ console-control-strings@^1.0.0, console-control-strings@~1.1.0: resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4= -content-disposition@0.5.3: - version "0.5.3" - resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.3.tgz#e130caf7e7279087c5616c2007d0485698984fbd" - integrity sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g== +content-disposition@0.5.4: + version "0.5.4" + resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe" + integrity sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ== dependencies: - safe-buffer "5.1.2" + safe-buffer "5.2.1" content-type@~1.0.4: version "1.0.4" @@ -474,10 +486,10 @@ cookie-signature@1.0.6: resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" integrity sha1-4wOogrNCzD7oylE6eZmXNNqzriw= -cookie@0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.0.tgz#beb437e7022b3b6d49019d088665303ebe9c14ba" - integrity sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg== +cookie@0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.5.0.tgz#d1f5d71adec6558c58f389987c366aa47e994f8b" + integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw== core-util-is@~1.0.0: version "1.0.2" @@ -489,7 +501,7 @@ crypto-random-string@^2.0.0: resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-2.0.0.tgz#ef2a7a966ec11083388369baa02ebead229b30d5" integrity sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA== -debug@2.6.9, debug@^2.2.0: +debug@2.6.9: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== @@ -503,10 +515,10 @@ debug@4: dependencies: ms "2.1.2" -debug@^3.2.6: - version "3.2.6" - resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" - integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== +debug@^3.2.7: + version "3.2.7" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" + integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== dependencies: ms "^2.1.1" @@ -544,15 +556,15 @@ delegates@^1.0.0: resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" integrity sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o= -depd@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" - integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= +depd@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" + integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== -destroy@~1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" - integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA= +destroy@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015" + integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg== detect-libc@^1.0.3: version "1.0.3" @@ -637,7 +649,7 @@ escodegen@^2.0.0: optionalDependencies: source-map "~0.6.1" -esprima@^4.0.0, esprima@^4.0.1: +esprima@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== @@ -667,64 +679,59 @@ expand-template@^2.0.3: resolved "https://registry.yarnpkg.com/expand-template/-/expand-template-2.0.3.tgz#6e14b3fcee0f3a6340ecb57d2e8918692052a47c" integrity sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg== -express@^4.17.1: - version "4.17.1" - resolved "https://registry.yarnpkg.com/express/-/express-4.17.1.tgz#4491fc38605cf51f8629d39c2b5d026f98a4c134" - integrity sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g== +express@^4.18.1: + version "4.18.1" + resolved "https://registry.yarnpkg.com/express/-/express-4.18.1.tgz#7797de8b9c72c857b9cd0e14a5eea80666267caf" + integrity sha512-zZBcOX9TfehHQhtupq57OF8lFZ3UZi08Y97dwFCkD8p9d/d2Y3M+ykKcwaMDEL+4qyUolgBDX6AblpR3fL212Q== dependencies: - accepts "~1.3.7" + accepts "~1.3.8" array-flatten "1.1.1" - body-parser "1.19.0" - content-disposition "0.5.3" + body-parser "1.20.0" + content-disposition "0.5.4" content-type "~1.0.4" - cookie "0.4.0" + cookie "0.5.0" cookie-signature "1.0.6" debug "2.6.9" - depd "~1.1.2" + depd "2.0.0" encodeurl "~1.0.2" escape-html "~1.0.3" etag "~1.8.1" - finalhandler "~1.1.2" + finalhandler "1.2.0" fresh "0.5.2" + http-errors "2.0.0" merge-descriptors "1.0.1" methods "~1.1.2" - on-finished "~2.3.0" + on-finished "2.4.1" parseurl "~1.3.3" path-to-regexp "0.1.7" - proxy-addr "~2.0.5" - qs "6.7.0" + proxy-addr "~2.0.7" + qs "6.10.3" range-parser "~1.2.1" - safe-buffer "5.1.2" - send "0.17.1" - serve-static "1.14.1" - setprototypeof "1.1.1" - statuses "~1.5.0" + safe-buffer "5.2.1" + send "0.18.0" + serve-static "1.15.0" + setprototypeof "1.2.0" + statuses "2.0.1" type-is "~1.6.18" utils-merge "1.0.1" vary "~1.1.2" -fast-glob@^3.1.1: - version "3.2.4" - resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.4.tgz#d20aefbf99579383e7f3cc66529158c9b98554d3" - integrity sha512-kr/Oo6PX51265qeuCYsyGypiO5uJFgBS0jksyG7FUeCyQzNwYnzrNIMR1NXfkZXsMYXYLRAHgISHBz8gQcxKHQ== +fast-glob@^3.2.9: + version "3.2.11" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.11.tgz#a1172ad95ceb8a16e20caa5c5e56480e5129c1d9" + integrity sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew== dependencies: "@nodelib/fs.stat" "^2.0.2" "@nodelib/fs.walk" "^1.2.3" - glob-parent "^5.1.0" + glob-parent "^5.1.2" merge2 "^1.3.0" - micromatch "^4.0.2" - picomatch "^2.2.1" + micromatch "^4.0.4" fast-levenshtein@~2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= -fast-safe-stringify@^2.0.4: - version "2.0.7" - resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz#124aa885899261f68aedb42a7c080de9da608743" - integrity sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA== - fastq@^1.6.0: version "1.8.0" resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.8.0.tgz#550e1f9f59bbc65fe185cb6a9b4d95357107f481" @@ -737,12 +744,12 @@ fecha@^4.2.0: resolved "https://registry.yarnpkg.com/fecha/-/fecha-4.2.0.tgz#3ffb6395453e3f3efff850404f0a59b6747f5f41" integrity sha512-aN3pcx/DSmtyoovUudctc8+6Hl4T+hI9GBBHLjA76jdZl7+b1sgh5g4k+u/GL3dTy1/pnYzKp69FpJ0OicE3Wg== -file-stream-rotator@^0.5.7: - version "0.5.7" - resolved "https://registry.yarnpkg.com/file-stream-rotator/-/file-stream-rotator-0.5.7.tgz#868a2e5966f7640a17dd86eda0e4467c089f6286" - integrity sha512-VYb3HZ/GiAGUCrfeakO8Mp54YGswNUHvL7P09WQcXAJNSj3iQ5QraYSp3cIn1MUyw6uzfgN/EFOarCNa4JvUHQ== +file-stream-rotator@^0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/file-stream-rotator/-/file-stream-rotator-0.6.1.tgz#007019e735b262bb6c6f0197e58e5c87cb96cec3" + integrity sha512-u+dBid4PvZw17PmDeRcNOtCP9CCK/9lRN2w+r1xIS7yOL9JFrIBKTvrYsxT4P0pGtThYTn++QS5ChHaUov3+zQ== dependencies: - moment "^2.11.2" + moment "^2.29.1" fill-range@^7.0.1: version "7.0.1" @@ -751,17 +758,17 @@ fill-range@^7.0.1: dependencies: to-regex-range "^5.0.1" -finalhandler@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d" - integrity sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA== +finalhandler@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.2.0.tgz#7d23fe5731b207b4640e4fcd00aec1f9207a7b32" + integrity sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg== dependencies: debug "2.6.9" encodeurl "~1.0.2" escape-html "~1.0.3" - on-finished "~2.3.0" + on-finished "2.4.1" parseurl "~1.3.3" - statuses "~1.5.0" + statuses "2.0.1" unpipe "~1.0.0" fn.name@1.x.x: @@ -769,15 +776,15 @@ fn.name@1.x.x: resolved "https://registry.yarnpkg.com/fn.name/-/fn.name-1.1.0.tgz#26cad8017967aea8731bc42961d04a3d5988accc" integrity sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw== -follow-redirects@^1.0.0: - version "1.13.0" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.13.0.tgz#b42e8d93a2a7eea5ed88633676d6597bc8e384db" - integrity sha512-aq6gF1BEKje4a9i9+5jimNFIpq4Q1WiwBToeRK5NvZBd/TRsmW8BsJfOEGkr76TbOyPVD3OVDN910EcUNtRYEA== +follow-redirects@^1.0.0, follow-redirects@^1.14.8: + version "1.15.1" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.1.tgz#0ca6a452306c9b276e4d3127483e29575e207ad5" + integrity sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA== -forwarded@~0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84" - integrity sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ= +forwarded@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" + integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow== fresh@0.5.2: version "0.5.2" @@ -797,10 +804,10 @@ fs-constants@^1.0.0: resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== -fs-extra@^10.0.0: - version "10.0.0" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-10.0.0.tgz#9ff61b655dde53fb34a82df84bb214ce802e17c1" - integrity sha512-C5owb14u9eJwizKGdchcDUQeFtlSHHthBk8pbX9Vc1PFZrLombudjDnNns88aYslCyF6IY5SUw3Roz6xShcEIQ== +fs-extra@^10.1.0: + version "10.1.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-10.1.0.tgz#02873cfbc4084dde127eaa5f9905eef2325d1abf" + integrity sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ== dependencies: graceful-fs "^4.2.0" jsonfile "^6.0.1" @@ -816,10 +823,10 @@ fs-extra@^9.1.0: jsonfile "^6.0.1" universalify "^2.0.0" -fsevents@~2.1.2: - version "2.1.3" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.1.3.tgz#fb738703ae8d2f9fe900c33836ddebee8b97f23e" - integrity sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ== +fsevents@~2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" + integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== function-bind@^1.1.1: version "1.1.1" @@ -845,6 +852,15 @@ get-caller-file@^2.0.5: resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== +get-intrinsic@^1.0.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.2.tgz#336975123e05ad0b7ba41f152ee4aadbea6cf598" + integrity sha512-Jfm3OyCxHh9DJyc28qGk+JmfkpO41A4XkneDSujN9MDXrm4oDKdHvndhZ2dN94+ERNfkYJWDclW6k2L/ZGHjXA== + dependencies: + function-bind "^1.1.1" + has "^1.0.3" + has-symbols "^1.0.3" + get-stream@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" @@ -864,30 +880,30 @@ github-from-package@0.0.0: resolved "https://registry.yarnpkg.com/github-from-package/-/github-from-package-0.0.0.tgz#97fb5d96bfde8973313f20e8288ef9a167fa64ce" integrity sha1-l/tdlr/eiXMxPyDoKI75oWf6ZM4= -glob-parent@^5.1.0, glob-parent@~5.1.0: - version "5.1.1" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.1.tgz#b6c1ef417c4e5663ea498f1c45afac6916bbc229" - integrity sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ== +glob-parent@^5.1.2, glob-parent@~5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== dependencies: is-glob "^4.0.1" -global-dirs@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/global-dirs/-/global-dirs-2.0.1.tgz#acdf3bb6685bcd55cb35e8a052266569e9469201" - integrity sha512-5HqUqdhkEovj2Of/ms3IeS/EekcO54ytHRLV4PEY2rhRwrHXLQjeVEES0Lhka0xwNDtGYn58wyC4s5+MHsOO6A== +global-dirs@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/global-dirs/-/global-dirs-3.0.0.tgz#70a76fe84ea315ab37b1f5576cbde7d48ef72686" + integrity sha512-v8ho2DS5RiCjftj1nD9NmnfaOzTdud7RRnVd9kFNOjqZbISlx5DQ+OrTkywgd0dIt7oFCvKetZSHoHcP3sDdiA== dependencies: - ini "^1.3.5" + ini "2.0.0" -globby@^11.0.3: - version "11.0.4" - resolved "https://registry.yarnpkg.com/globby/-/globby-11.0.4.tgz#2cbaff77c2f2a62e71e9b2813a67b97a3a3001a5" - integrity sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg== +globby@^11.1.0: + version "11.1.0" + resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" + integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== dependencies: array-union "^2.1.0" dir-glob "^3.0.1" - fast-glob "^3.1.1" - ignore "^5.1.4" - merge2 "^1.3.0" + fast-glob "^3.2.9" + ignore "^5.2.0" + merge2 "^1.4.1" slash "^3.0.0" got@^9.6.0: @@ -922,6 +938,11 @@ has-flag@^4.0.0: resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== +has-symbols@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" + integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== + has-unicode@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" @@ -944,27 +965,16 @@ http-cache-semantics@^4.0.0: resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390" integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ== -http-errors@1.7.2: - version "1.7.2" - resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.2.tgz#4f5029cf13239f31036e5b2e55292bcfbcc85c8f" - integrity sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg== - dependencies: - depd "~1.1.2" - inherits "2.0.3" - setprototypeof "1.1.1" - statuses ">= 1.5.0 < 2" - toidentifier "1.0.0" - -http-errors@~1.7.2: - version "1.7.3" - resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.3.tgz#6c619e4f9c60308c38519498c14fbb10aacebb06" - integrity sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw== +http-errors@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3" + integrity sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ== dependencies: - depd "~1.1.2" + depd "2.0.0" inherits "2.0.4" - setprototypeof "1.1.1" - statuses ">= 1.5.0 < 2" - toidentifier "1.0.0" + setprototypeof "1.2.0" + statuses "2.0.1" + toidentifier "1.0.1" http-proxy@^1.18.1: version "1.18.1" @@ -1005,10 +1015,10 @@ ignore-by-default@^1.0.1: resolved "https://registry.yarnpkg.com/ignore-by-default/-/ignore-by-default-1.0.1.tgz#48ca6d72f6c6a3af00a9ad4ae6876be3889e2b09" integrity sha1-SMptcvbGo68Aqa1K5odr44ieKwk= -ignore@^5.1.4: - version "5.1.8" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.8.tgz#f150a8b50a34289b33e22f5889abd4d8016f0e57" - integrity sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw== +ignore@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.0.tgz#6d3bac8fa7fe0d45d9f9be7bac2fc279577e345a" + integrity sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ== import-lazy@^2.1.0: version "2.1.0" @@ -1020,17 +1030,17 @@ imurmurhash@^0.1.4: resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= -inherits@2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" - integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= - inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== -ini@^1.3.5, ini@~1.3.0: +ini@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ini/-/ini-2.0.0.tgz#e5fd556ecdd5726be978fa1001862eacb0a94bc5" + integrity sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA== + +ini@~1.3.0: version "1.3.5" resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927" integrity sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw== @@ -1067,10 +1077,10 @@ is-ci@^2.0.0: dependencies: ci-info "^2.0.0" -is-core-module@^2.2.0: - version "2.6.0" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.6.0.tgz#d7553b2526fe59b92ba3e40c8df757ec8a709e19" - integrity sha512-wShG8vs60jKfPWpF2KZRaAtvt3a20OAn7+IJ6hLPECpSABLcKtFKTTI4ZtH5QcBruBHlq+WsdHWyz0BCZW7svQ== +is-core-module@2.9.0, is-core-module@^2.8.1: + version "2.9.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.9.0.tgz#e1c34429cd51c6dd9e09e0799e396e27b19a9c69" + integrity sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A== dependencies: has "^1.0.3" @@ -1103,18 +1113,18 @@ is-glob@^4.0.1, is-glob@~4.0.1: dependencies: is-extglob "^2.1.1" -is-installed-globally@^0.3.1: - version "0.3.2" - resolved "https://registry.yarnpkg.com/is-installed-globally/-/is-installed-globally-0.3.2.tgz#fd3efa79ee670d1187233182d5b0a1dd00313141" - integrity sha512-wZ8x1js7Ia0kecP/CHM/3ABkAmujX7WPvQk6uu3Fly/Mk44pySulQpnHG46OMjHGXApINnV4QhY3SWnECO2z5g== +is-installed-globally@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/is-installed-globally/-/is-installed-globally-0.4.0.tgz#9a0fd407949c30f86eb6959ef1b7994ed0b7b520" + integrity sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ== dependencies: - global-dirs "^2.0.1" - is-path-inside "^3.0.1" + global-dirs "^3.0.0" + is-path-inside "^3.0.2" -is-npm@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/is-npm/-/is-npm-4.0.0.tgz#c90dd8380696df87a7a6d823c20d0b12bbe3c84d" - integrity sha512-96ECIfh9xtDDlPylNPXhzjsykHsMJZ18ASpaWzQyBr4YRTcVjUvzaHayDAES2oU/3KpljhHUjtSRNiDwi0F0ig== +is-npm@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/is-npm/-/is-npm-5.0.0.tgz#43e8d65cc56e1b67f8d47262cf667099193f45a8" + integrity sha512-WW/rQLOazUq+ST/bCAVBp/2oMERWLsR7OrKyt052dNDk4DHcDE0/7QSXITlmi+VBcV13DfIbysG3tZJm5RfdBA== is-number@^7.0.0: version "7.0.0" @@ -1126,10 +1136,10 @@ is-obj@^2.0.0: resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-2.0.0.tgz#473fb05d973705e3fd9620545018ca8e22ef4982" integrity sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w== -is-path-inside@^3.0.1: - version "3.0.2" - resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.2.tgz#f5220fc82a3e233757291dddc9c5877f2a1f3017" - integrity sha512-/2UGPSgmtqwo1ktx8NDHjuPwZWmHhO+gj0f93EkhLB5RgW9RZevWYYlIkS6zePc6U2WpOdQYIwHe9YC4DWEBVg== +is-path-inside@^3.0.2: + version "3.0.3" + resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" + integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== is-stream@^2.0.0: version "2.0.0" @@ -1151,13 +1161,12 @@ isarray@~1.0.0: resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= -js-yaml@^3.14.0: - version "3.14.0" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.0.tgz#a7a34170f26a21bb162424d8adacb4113a69e482" - integrity sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A== +js-yaml@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" + integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== dependencies: - argparse "^1.0.7" - esprima "^4.0.0" + argparse "^2.0.1" json-buffer@3.0.0: version "3.0.0" @@ -1192,7 +1201,7 @@ kuler@^2.0.0: resolved "https://registry.yarnpkg.com/kuler/-/kuler-2.0.0.tgz#e2c570a3800388fb44407e851531c1d670b061b3" integrity sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A== -latest-version@^5.0.0: +latest-version@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/latest-version/-/latest-version-5.1.0.tgz#119dfe908fe38d15dfa43ecd13fa12ec8832face" integrity sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA== @@ -1207,20 +1216,15 @@ levn@~0.3.0: prelude-ls "~1.1.2" type-check "~0.3.2" -lodash@^4.17.19: - version "4.17.21" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" - integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== - -logform@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/logform/-/logform-2.2.0.tgz#40f036d19161fc76b68ab50fdc7fe495544492f2" - integrity sha512-N0qPlqfypFx7UHNn4B3lzS/b0uLqt2hmuoa+PpuXNYgozdJYAyauF5Ky0BWVjrxDlMWiT3qN4zPq3vVAfZy7Yg== +logform@^2.3.2, logform@^2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/logform/-/logform-2.4.0.tgz#131651715a17d50f09c2a2c1a524ff1a4164bcfe" + integrity sha512-CPSJw4ftjf517EhXZGGvTHHkYobo7ZCc0kvwUoOYcjfR2UVrI66RHj8MCrfAdEitdmFqbu2BYdYs8FHHZSb6iw== dependencies: - colors "^1.2.1" - fast-safe-stringify "^2.0.4" + "@colors/colors" "1.5.0" fecha "^4.2.0" ms "^2.1.1" + safe-stable-stringify "^2.3.1" triple-beam "^1.3.0" lowercase-keys@^1.0.0, lowercase-keys@^1.0.1: @@ -1257,7 +1261,7 @@ merge-descriptors@1.0.1: resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" integrity sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E= -merge2@^1.3.0: +merge2@^1.3.0, merge2@^1.4.1: version "1.4.1" resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== @@ -1267,19 +1271,24 @@ methods@~1.1.2: resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4= -micromatch@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.2.tgz#4fcb0999bf9fbc2fcbdd212f6d629b9a56c39259" - integrity sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q== +micromatch@^4.0.4: + version "4.0.5" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6" + integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA== dependencies: - braces "^3.0.1" - picomatch "^2.0.5" + braces "^3.0.2" + picomatch "^2.3.1" mime-db@1.44.0, "mime-db@>= 1.43.0 < 2": version "1.44.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.44.0.tgz#fa11c5eb0aca1334b4233cb4d52f10c5a6272f92" integrity sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg== +mime-db@1.52.0: + version "1.52.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" + integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== + mime-types@~2.1.24: version "2.1.27" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.27.tgz#47949f98e279ea53119f5722e0f34e529bec009f" @@ -1287,6 +1296,13 @@ mime-types@~2.1.24: dependencies: mime-db "1.44.0" +mime-types@~2.1.34: + version "2.1.35" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" + integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== + dependencies: + mime-db "1.52.0" + mime@1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" @@ -1309,36 +1325,36 @@ minimatch@^3.0.4: dependencies: brace-expansion "^1.1.7" -minimist@^1.2.0, minimist@^1.2.3, minimist@^1.2.5: - version "1.2.5" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" - integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== +minimist@^1.2.0, minimist@^1.2.3, minimist@^1.2.5, minimist@^1.2.6: + version "1.2.6" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44" + integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q== mkdirp-classic@^0.5.2, mkdirp-classic@^0.5.3: version "0.5.3" resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113" integrity sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A== -moment@^2.11.2: - version "2.27.0" - resolved "https://registry.yarnpkg.com/moment/-/moment-2.27.0.tgz#8bff4e3e26a236220dfe3e36de756b6ebaa0105d" - integrity sha512-al0MUK7cpIcglMv3YF13qSgdAIqxHTO7brRtaz3DlSULbqfazqkc5kEjNrLDOM7fsjshoFIihnU8snrP7zUvhQ== +moment@^2.29.1: + version "2.29.3" + resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.3.tgz#edd47411c322413999f7a5940d526de183c031f3" + integrity sha512-c6YRvhEo//6T2Jz/vVtYzqBzwvPT95JBQ+smCytzf7c50oMZRsR/a4w88aD34I+/QVSfnoAnSBFPJHItlOMJVw== ms@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= -ms@2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" - integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== - ms@2.1.2, ms@^2.1.1: version "2.1.2" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== +ms@2.1.3: + version "2.1.3" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + multistream@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/multistream/-/multistream-4.1.0.tgz#7bf00dfd119556fbc153cff3de4c6d477909f5a8" @@ -1357,38 +1373,40 @@ negotiator@0.6.2: resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw== -node-abi@^2.7.0: +negotiator@0.6.3: + version "0.6.3" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" + integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== + +node-abi@^2.21.0: version "2.30.1" resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-2.30.1.tgz#c437d4b1fe0e285aaf290d45b45d4d7afedac4cf" integrity sha512-/2D0wOQPgaUWzVSVgRMx+trKJRC2UG4SUc4oCJoXx9Uxjtp0Vy3/kt7zcbxHF8+Z/pK3UloLWzBISg72brfy1w== dependencies: semver "^5.4.1" -node-fetch@^2.6.1: - version "2.6.2" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.2.tgz#986996818b73785e47b1965cc34eb093a1d464d0" - integrity sha512-aLoxToI6RfZ+0NOjmWAgn9+LEd30YCkJKFSyWacNZdEKTit/ZMcKjGkTRo8uWEsnIb/hfKecNPEbln02PdWbcA== +node-fetch@^2.6.6: + version "2.6.7" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad" + integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ== + dependencies: + whatwg-url "^5.0.0" -nodemon@^2.0.12: - version "2.0.12" - resolved "https://registry.yarnpkg.com/nodemon/-/nodemon-2.0.12.tgz#5dae4e162b617b91f1873b3bfea215dd71e144d5" - integrity sha512-egCTmNZdObdBxUBw6ZNwvZ/xzk24CKRs5K6d+5zbmrMr7rOpPmfPeF6OxM3DDpaRx331CQRFEktn+wrFFfBSOA== +nodemon@^2.0.16: + version "2.0.16" + resolved "https://registry.yarnpkg.com/nodemon/-/nodemon-2.0.16.tgz#d71b31bfdb226c25de34afea53486c8ef225fdef" + integrity sha512-zsrcaOfTWRuUzBn3P44RDliLlp263Z/76FPoHFr3cFFkOz0lTPAcIw8dCzfdVIx/t3AtDYCZRCDkoCojJqaG3w== dependencies: - chokidar "^3.2.2" - debug "^3.2.6" + chokidar "^3.5.2" + debug "^3.2.7" ignore-by-default "^1.0.1" minimatch "^3.0.4" - pstree.remy "^1.1.7" + pstree.remy "^1.1.8" semver "^5.7.1" supports-color "^5.5.0" touch "^3.1.0" - undefsafe "^2.0.3" - update-notifier "^4.1.0" - -noop-logger@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/noop-logger/-/noop-logger-0.1.1.tgz#94a2b1633c4f1317553007d8966fd0e841b6a4c2" - integrity sha1-lKKxYzxPExdVMAfYlm/Q6EG2pMI= + undefsafe "^2.0.5" + update-notifier "^5.1.0" nopt@~1.0.10: version "1.0.10" @@ -1432,10 +1450,15 @@ object-hash@^2.0.1: resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-2.0.3.tgz#d12db044e03cd2ca3d77c0570d87225b02e1e6ea" integrity sha512-JPKn0GMu+Fa3zt3Bmr66JhokJU5BaNBIh4ZeTlaCBzrBsOeXzwcKKAK1tbLiPKgvwmPXsDvvLHoWh5Bm7ofIYg== -on-finished@~2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" - integrity sha1-IPEzZIGwg811M3mSoWlxqi2QaUc= +object-inspect@^1.9.0: + version "1.12.2" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.2.tgz#c0641f26394532f28ab8d796ab954e43c009a8ea" + integrity sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ== + +on-finished@2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f" + integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg== dependencies: ee-first "1.1.1" @@ -1495,10 +1518,10 @@ parseurl@~1.3.3: resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== -path-parse@^1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" - integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw== +path-parse@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" + integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== path-to-regexp@0.1.7: version "0.1.7" @@ -1510,49 +1533,54 @@ path-type@^4.0.0: resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== -picomatch@^2.0.4, picomatch@^2.0.5, picomatch@^2.2.1: +picomatch@^2.0.4, picomatch@^2.2.1: version "2.2.2" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.2.tgz#21f333e9b6b8eaff02468f5146ea406d345f4dad" integrity sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg== -pkg-fetch@3.2.2: - version "3.2.2" - resolved "https://registry.yarnpkg.com/pkg-fetch/-/pkg-fetch-3.2.2.tgz#33f391eb176c1844e93189a32f2279b36a1ec949" - integrity sha512-bLhFNT4cNnONxzbHo1H2mCCKuQkCR4dgQtv0gUZnWtp8TDP0v0UAXKHG7DXhAoTC5IYP3slLsFJtIda9ksny8g== +picomatch@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== + +pkg-fetch@3.4.1: + version "3.4.1" + resolved "https://registry.yarnpkg.com/pkg-fetch/-/pkg-fetch-3.4.1.tgz#be68bb9f7fdb0f6ed995abc518ab2e35aa64d2fd" + integrity sha512-fS4cdayCa1r4jHkOKGPJKnS9PEs6OWZst+s+m0+CmhmPZObMnxoRnf9T9yUWl+lzM2b5aJF7cnQIySCT7Hq8Dg== dependencies: - chalk "^4.1.0" + chalk "^4.1.2" fs-extra "^9.1.0" https-proxy-agent "^5.0.0" - node-fetch "^2.6.1" + node-fetch "^2.6.6" progress "^2.0.3" semver "^7.3.5" + tar-fs "^2.1.1" yargs "^16.2.0" -pkg@^5.3.1: - version "5.3.1" - resolved "https://registry.yarnpkg.com/pkg/-/pkg-5.3.1.tgz#8f81671613b9e5bb1d83c39b2eed4799e1e679fe" - integrity sha512-jT/sptM1ZG++FNk+jnJYNoWLDQXYd7hqpnBhd5j18SNW1jJzNYo55RahuCiD0KN0PX9mb53GWCqKM0ia/mJytA== +pkg@^5.7.0: + version "5.7.0" + resolved "https://registry.yarnpkg.com/pkg/-/pkg-5.7.0.tgz#6422df05e8aa147764be6ef912921d0fa719ea95" + integrity sha512-PTiAjNq/CGAtK5qUBR6pjheqnipTFjeecgSgIKEcAOJA4GpmZeOZC8pMOoT0rfes5vHsmcFo7wbSRTAmXQurrg== dependencies: - "@babel/parser" "7.13.13" - "@babel/types" "7.13.12" - chalk "^4.1.0" + "@babel/parser" "7.17.10" + "@babel/types" "7.17.10" + chalk "^4.1.2" escodegen "^2.0.0" fs-extra "^9.1.0" - globby "^11.0.3" + globby "^11.1.0" into-stream "^6.0.0" - minimist "^1.2.5" + is-core-module "2.9.0" + minimist "^1.2.6" multistream "^4.1.0" - pkg-fetch "3.2.2" - prebuild-install "6.0.1" - progress "^2.0.3" - resolve "^1.20.0" + pkg-fetch "3.4.1" + prebuild-install "6.1.4" + resolve "^1.22.0" stream-meter "^1.0.4" - tslib "2.1.0" -prebuild-install@6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-6.0.1.tgz#5902172f7a40eb67305b96c2a695db32636ee26d" - integrity sha512-7GOJrLuow8yeiyv75rmvZyeMGzl8mdEX5gY69d6a6bHWmiPevwqFw+tQavhK0EYMaSg3/KD24cWqeQv1EWsqDQ== +prebuild-install@6.1.4: + version "6.1.4" + resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-6.1.4.tgz#ae3c0142ad611d58570b89af4986088a4937e00f" + integrity sha512-Z4vpywnK1lBg+zdPCVCsKq0xO66eEV9rWo2zrROGGiRS4JtueBOdlB1FnY8lcy7JsUud/Q3ijUxyWN26Ika0vQ== dependencies: detect-libc "^1.0.3" expand-template "^2.0.3" @@ -1560,15 +1588,13 @@ prebuild-install@6.0.1: minimist "^1.2.3" mkdirp-classic "^0.5.3" napi-build-utils "^1.0.1" - node-abi "^2.7.0" - noop-logger "^0.1.1" + node-abi "^2.21.0" npmlog "^4.0.1" pump "^3.0.0" rc "^1.2.7" simple-get "^3.0.3" tar-fs "^2.0.0" tunnel-agent "^0.6.0" - which-pm-runs "^1.0.0" prelude-ls@~1.1.2: version "1.1.2" @@ -1590,15 +1616,15 @@ progress@^2.0.3: resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== -proxy-addr@~2.0.5: - version "2.0.6" - resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.6.tgz#fdc2336505447d3f2f2c638ed272caf614bbb2bf" - integrity sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw== +proxy-addr@~2.0.7: + version "2.0.7" + resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025" + integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg== dependencies: - forwarded "~0.1.2" + forwarded "0.2.0" ipaddr.js "1.9.1" -pstree.remy@^1.1.7: +pstree.remy@^1.1.8: version "1.1.8" resolved "https://registry.yarnpkg.com/pstree.remy/-/pstree.remy-1.1.8.tgz#c242224f4a67c21f686839bbdb4ac282b8373d3a" integrity sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w== @@ -1611,30 +1637,32 @@ pump@^3.0.0: end-of-stream "^1.1.0" once "^1.3.1" -pupa@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/pupa/-/pupa-2.0.1.tgz#dbdc9ff48ffbea4a26a069b6f9f7abb051008726" - integrity sha512-hEJH0s8PXLY/cdXh66tNEQGndDrIKNqNC5xmrysZy3i5C3oEoLna7YAOad+7u125+zH1HNXUmGEkrhb3c2VriA== +pupa@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/pupa/-/pupa-2.1.1.tgz#f5e8fd4afc2c5d97828faa523549ed8744a20d62" + integrity sha512-l1jNAspIBSFqbT+y+5FosojNpVpF94nlI+wDUpqP9enwOTfHx9f0gh5nB96vl+6yTpsJsypeNrwfzPrKuHB41A== dependencies: escape-goat "^2.0.0" -qs@6.7.0: - version "6.7.0" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc" - integrity sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ== +qs@6.10.3: + version "6.10.3" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.10.3.tgz#d6cde1b2ffca87b5aa57889816c5f81535e22e8e" + integrity sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ== + dependencies: + side-channel "^1.0.4" range-parser@~1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== -raw-body@2.4.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.0.tgz#a1ce6fb9c9bc356ca52e89256ab59059e13d0332" - integrity sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q== +raw-body@2.5.1: + version "2.5.1" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.1.tgz#fe1b1628b181b700215e5fd42389f98b71392857" + integrity sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig== dependencies: - bytes "3.1.0" - http-errors "1.7.2" + bytes "3.1.2" + http-errors "2.0.0" iconv-lite "0.4.24" unpipe "1.0.0" @@ -1670,10 +1698,10 @@ readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.6.0: string_decoder "^1.1.1" util-deprecate "^1.0.1" -readdirp@~3.4.0: - version "3.4.0" - resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.4.0.tgz#9fdccdf9e9155805449221ac645e8303ab5b9ada" - integrity sha512-0xe001vZBnJEK+uKcj8qOhyAKPzIT+gStxWr3LCB0DwcXR5NZJ3IaC+yGnHCYzB/S7ov3m3EEbZI2zeNvX+hGQ== +readdirp@~3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" + integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== dependencies: picomatch "^2.2.1" @@ -1701,13 +1729,14 @@ requires-port@^1.0.0: resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" integrity sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8= -resolve@^1.20.0: - version "1.20.0" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975" - integrity sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A== +resolve@^1.22.0: + version "1.22.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.0.tgz#5e0b8c67c15df57a89bdbabe603a002f21731198" + integrity sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw== dependencies: - is-core-module "^2.2.0" - path-parse "^1.0.6" + is-core-module "^2.8.1" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" responselike@^1.0.2: version "1.0.2" @@ -1731,11 +1760,16 @@ safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== -safe-buffer@^5.0.1, safe-buffer@~5.2.0: +safe-buffer@5.2.1, safe-buffer@^5.0.1, safe-buffer@~5.2.0: version "5.2.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== +safe-stable-stringify@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/safe-stable-stringify/-/safe-stable-stringify-2.3.1.tgz#ab67cbe1fe7d40603ca641c5e765cb942d04fc73" + integrity sha512-kYBSfT+troD9cDA85VDnHZ1rpHC50O0g1e6WlGHVCz/g+JS+9WKLj+XwFYyR8UbrZN8ll9HUpDAAddY58MGisg== + "safer-buffer@>= 2.1.2 < 3": version "2.1.2" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" @@ -1758,6 +1792,13 @@ semver@^6.0.0, semver@^6.2.0, semver@^6.3.0: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== +semver@^7.3.4: + version "7.3.7" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.7.tgz#12c5b649afdbf9049707796e22a4028814ce523f" + integrity sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g== + dependencies: + lru-cache "^6.0.0" + semver@^7.3.5: version "7.3.5" resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7" @@ -1765,44 +1806,53 @@ semver@^7.3.5: dependencies: lru-cache "^6.0.0" -send@0.17.1: - version "0.17.1" - resolved "https://registry.yarnpkg.com/send/-/send-0.17.1.tgz#c1d8b059f7900f7466dd4938bdc44e11ddb376c8" - integrity sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg== +send@0.18.0: + version "0.18.0" + resolved "https://registry.yarnpkg.com/send/-/send-0.18.0.tgz#670167cc654b05f5aa4a767f9113bb371bc706be" + integrity sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg== dependencies: debug "2.6.9" - depd "~1.1.2" - destroy "~1.0.4" + depd "2.0.0" + destroy "1.2.0" encodeurl "~1.0.2" escape-html "~1.0.3" etag "~1.8.1" fresh "0.5.2" - http-errors "~1.7.2" + http-errors "2.0.0" mime "1.6.0" - ms "2.1.1" - on-finished "~2.3.0" + ms "2.1.3" + on-finished "2.4.1" range-parser "~1.2.1" - statuses "~1.5.0" + statuses "2.0.1" -serve-static@1.14.1: - version "1.14.1" - resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.14.1.tgz#666e636dc4f010f7ef29970a88a674320898b2f9" - integrity sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg== +serve-static@1.15.0: + version "1.15.0" + resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.15.0.tgz#faaef08cffe0a1a62f60cad0c4e513cff0ac9540" + integrity sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g== dependencies: encodeurl "~1.0.2" escape-html "~1.0.3" parseurl "~1.3.3" - send "0.17.1" + send "0.18.0" set-blocking@~2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= -setprototypeof@1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.1.tgz#7e95acb24aa92f5885e0abef5ba131330d4ae683" - integrity sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw== +setprototypeof@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" + integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== + +side-channel@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" + integrity sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw== + dependencies: + call-bind "^1.0.0" + get-intrinsic "^1.0.2" + object-inspect "^1.9.0" signal-exit@^3.0.0, signal-exit@^3.0.2: version "3.0.3" @@ -1840,20 +1890,15 @@ source-map@~0.6.1: resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== -sprintf-js@~1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" - integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= - stack-trace@0.0.x: version "0.0.10" resolved "https://registry.yarnpkg.com/stack-trace/-/stack-trace-0.0.10.tgz#547c70b347e8d32b4e108ea1a2a159e5fdde19c0" integrity sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA= -"statuses@>= 1.5.0 < 2", statuses@~1.5.0: - version "1.5.0" - resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" - integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= +statuses@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63" + integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== stream-meter@^1.0.4: version "1.0.4" @@ -1906,6 +1951,15 @@ string-width@^4.2.0: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.0" +string-width@^4.2.2: + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + string_decoder@^1.1.1: version "1.3.0" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" @@ -1948,6 +2002,13 @@ strip-ansi@^6.0.0: dependencies: ansi-regex "^5.0.0" +strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + strip-json-comments@~2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" @@ -1967,7 +2028,12 @@ supports-color@^7.1.0: dependencies: has-flag "^4.0.0" -tar-fs@^2.0.0: +supports-preserve-symlinks-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" + integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== + +tar-fs@^2.0.0, tar-fs@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-2.1.1.tgz#489a15ab85f1f0befabb370b7de4f9eb5cbe8784" integrity sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng== @@ -1988,11 +2054,6 @@ tar-stream@^2.1.4: inherits "^2.0.3" readable-stream "^3.1.1" -term-size@^2.1.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/term-size/-/term-size-2.2.0.tgz#1f16adedfe9bdc18800e1776821734086fcc6753" - integrity sha512-a6sumDlzyHVJWb8+YofY4TW112G6p2FCPEAFk+59gIYHv3XHRhm9ltVQ9kli4hNWeQBwSpe8cRN25x0ROunMOw== - text-hex@1.0.x: version "1.0.0" resolved "https://registry.yarnpkg.com/text-hex/-/text-hex-1.0.0.tgz#69dc9c1b17446ee79a92bf5b884bb4b9127506f5" @@ -2015,10 +2076,10 @@ to-regex-range@^5.0.1: dependencies: is-number "^7.0.0" -toidentifier@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553" - integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw== +toidentifier@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" + integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== touch@^3.1.0: version "3.1.0" @@ -2027,16 +2088,16 @@ touch@^3.1.0: dependencies: nopt "~1.0.10" +tr46@~0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" + integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== + triple-beam@^1.2.0, triple-beam@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/triple-beam/-/triple-beam-1.3.0.tgz#a595214c7298db8339eeeee083e4d10bd8cb8dd9" integrity sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw== -tslib@2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.1.0.tgz#da60860f1c2ecaa5703ab7d39bc05b6bf988b97a" - integrity sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A== - tunnel-agent@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" @@ -2051,12 +2112,12 @@ type-check@~0.3.2: dependencies: prelude-ls "~1.1.2" -type-fest@^0.8.1: - version "0.8.1" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" - integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== +type-fest@^0.20.2: + version "0.20.2" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" + integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== -type-is@~1.6.17, type-is@~1.6.18: +type-is@~1.6.18: version "1.6.18" resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== @@ -2071,12 +2132,10 @@ typedarray-to-buffer@^3.1.5: dependencies: is-typedarray "^1.0.0" -undefsafe@^2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/undefsafe/-/undefsafe-2.0.3.tgz#6b166e7094ad46313b2202da7ecc2cd7cc6e7aae" - integrity sha512-nrXZwwXrD/T/JXeygJqdCO6NZZ1L66HrxM/Z7mIq2oPanoN0F1nLx3lwJMu6AwJY69hdixaFQOuoYsMjE5/C2A== - dependencies: - debug "^2.2.0" +undefsafe@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/undefsafe/-/undefsafe-2.0.5.tgz#38733b9327bdcd226db889fb723a6efd162e6e2c" + integrity sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA== unique-string@^2.0.0: version "2.0.0" @@ -2100,22 +2159,23 @@ unpipe@1.0.0, unpipe@~1.0.0: resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw= -update-notifier@^4.1.0: - version "4.1.3" - resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-4.1.3.tgz#be86ee13e8ce48fb50043ff72057b5bd598e1ea3" - integrity sha512-Yld6Z0RyCYGB6ckIjffGOSOmHXj1gMeE7aROz4MG+XMkmixBX4jUngrGXNYz7wPKBmtoD4MnBa2Anu7RSKht/A== +update-notifier@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-5.1.0.tgz#4ab0d7c7f36a231dd7316cf7729313f0214d9ad9" + integrity sha512-ItnICHbeMh9GqUy31hFPrD1kcuZ3rpxDZbf4KUDavXwS0bW5m7SLbDQpGX3UYr072cbrF5hFUs3r5tUsPwjfHw== dependencies: - boxen "^4.2.0" - chalk "^3.0.0" + boxen "^5.0.0" + chalk "^4.1.0" configstore "^5.0.1" has-yarn "^2.1.0" import-lazy "^2.1.0" is-ci "^2.0.0" - is-installed-globally "^0.3.1" - is-npm "^4.0.0" + is-installed-globally "^0.4.0" + is-npm "^5.0.0" is-yarn-global "^0.3.0" - latest-version "^5.0.0" - pupa "^2.0.1" + latest-version "^5.1.0" + pupa "^2.1.1" + semver "^7.3.4" semver-diff "^3.1.1" xdg-basedir "^4.0.0" @@ -2141,10 +2201,18 @@ vary@~1.1.2: resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw= -which-pm-runs@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/which-pm-runs/-/which-pm-runs-1.0.0.tgz#670b3afbc552e0b55df6b7780ca74615f23ad1cb" - integrity sha1-Zws6+8VS4LVd9rd4DKdGFfI60cs= +webidl-conversions@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" + integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ== + +whatwg-url@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" + integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw== + dependencies: + tr46 "~0.0.3" + webidl-conversions "^3.0.0" wide-align@^1.1.0: version "1.1.3" @@ -2160,17 +2228,17 @@ widest-line@^3.1.0: dependencies: string-width "^4.0.0" -winston-daily-rotate-file@^4.5.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/winston-daily-rotate-file/-/winston-daily-rotate-file-4.5.0.tgz#3914ac57c4bdae1138170bec85af0c2217b253b1" - integrity sha512-/HqeWiU48dzGqcrABRlxYWVMdL6l3uKCtFSJyrqK+E2rLnSFNsgYpvwx15EgTitBLNzH69lQd/+z2ASryV2aqw== +winston-daily-rotate-file@^4.7.1: + version "4.7.1" + resolved "https://registry.yarnpkg.com/winston-daily-rotate-file/-/winston-daily-rotate-file-4.7.1.tgz#f60a643af87f8867f23170d8cd87dbe3603a625f" + integrity sha512-7LGPiYGBPNyGHLn9z33i96zx/bd71pjBn9tqQzO3I4Tayv94WPmBNwKC7CO1wPHdP9uvu+Md/1nr6VSH9h0iaA== dependencies: - file-stream-rotator "^0.5.7" + file-stream-rotator "^0.6.1" object-hash "^2.0.1" triple-beam "^1.3.0" - winston-transport "^4.2.0" + winston-transport "^4.4.0" -winston-transport@^4.2.0, winston-transport@^4.4.0: +winston-transport@^4.4.0: version "4.4.0" resolved "https://registry.yarnpkg.com/winston-transport/-/winston-transport-4.4.0.tgz#17af518daa690d5b2ecccaa7acf7b20ca7925e59" integrity sha512-Lc7/p3GtqtqPBYYtS6KCN3c77/2QCev51DvcJKbkFPQNoj1sinkGwLGFDxkXY9J6p9+EPnYs+D90uwbnaiURTw== @@ -2178,20 +2246,30 @@ winston-transport@^4.2.0, winston-transport@^4.4.0: readable-stream "^2.3.7" triple-beam "^1.2.0" -winston@^3.3.3: - version "3.3.3" - resolved "https://registry.yarnpkg.com/winston/-/winston-3.3.3.tgz#ae6172042cafb29786afa3d09c8ff833ab7c9170" - integrity sha512-oEXTISQnC8VlSAKf1KYSSd7J6IWuRPQqDdo8eoRNaYKLvwSb5+79Z3Yi1lrl6KDpU6/VWaxpakDAtb1oQ4n9aw== +winston-transport@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/winston-transport/-/winston-transport-4.5.0.tgz#6e7b0dd04d393171ed5e4e4905db265f7ab384fa" + integrity sha512-YpZzcUzBedhlTAfJg6vJDlyEai/IFMIVcaEZZyl3UXIl4gmqRpU7AE89AHLkbzLUsv0NVmw7ts+iztqKxxPW1Q== + dependencies: + logform "^2.3.2" + readable-stream "^3.6.0" + triple-beam "^1.3.0" + +winston@^3.7.2: + version "3.7.2" + resolved "https://registry.yarnpkg.com/winston/-/winston-3.7.2.tgz#95b4eeddbec902b3db1424932ac634f887c400b1" + integrity sha512-QziIqtojHBoyzUOdQvQiar1DH0Xp9nF1A1y7NVy2DGEsz82SBDtOalS0ulTRGVT14xPX3WRWkCsdcJKqNflKng== dependencies: "@dabh/diagnostics" "^2.0.2" - async "^3.1.0" + async "^3.2.3" is-stream "^2.0.0" - logform "^2.2.0" + logform "^2.4.0" one-time "^1.0.0" readable-stream "^3.4.0" + safe-stable-stringify "^2.3.1" stack-trace "0.0.x" triple-beam "^1.3.0" - winston-transport "^4.4.0" + winston-transport "^4.5.0" word-wrap@~1.2.3: version "1.2.3" From 258417346f2d672d2d8b795f5a05b57fd6c96937 Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Tue, 14 Jun 2022 09:16:32 +0300 Subject: [PATCH 788/798] refactoring: customer - test start --- .../controller/AbstractNotifyEntityTest.java | 113 +++++++++++----- .../controller/BaseAlarmControllerTest.java | 22 +-- .../BaseCustomerControllerTest.java | 125 ++++++++++++++---- 3 files changed, 192 insertions(+), 68 deletions(-) diff --git a/application/src/test/java/org/thingsboard/server/controller/AbstractNotifyEntityTest.java b/application/src/test/java/org/thingsboard/server/controller/AbstractNotifyEntityTest.java index a5827d6f6d..6fb7b93bae 100644 --- a/application/src/test/java/org/thingsboard/server/controller/AbstractNotifyEntityTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/AbstractNotifyEntityTest.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.controller; +import lombok.extern.slf4j.Slf4j; import org.mockito.Mockito; import org.springframework.boot.test.mock.mockito.SpyBean; import org.thingsboard.server.cluster.TbClusterService; @@ -26,6 +27,8 @@ import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityIdFactory; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.UserId; +import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; +import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.dao.audit.AuditLogService; import org.thingsboard.server.dao.model.ModelConstants; @@ -35,6 +38,7 @@ import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.thingsboard.server.service.entitiy.DefaultTbNotificationEntityService.edgeTypeByActionType; +@Slf4j public abstract class AbstractNotifyEntityTest extends AbstractWebTest { @SpyBean @@ -43,31 +47,53 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest { @SpyBean protected AuditLogService auditLogService; - protected void testNotifyEntityOk(HasName entity, EntityId entityId, EntityId originatorId, - TenantId tenantId, CustomerId customerId, UserId userId, String userName, - ActionType actionType) { - testSendNotificationMsgToEdgeServiceOk(entityId, tenantId, actionType); - testLogEntityActionOk(entity, originatorId, tenantId, customerId, userId, userName, actionType); - testPushMsgToRuleEngineOk(entity, originatorId, tenantId, customerId, userId, userName, actionType); + protected void testNotifyEntityOne(HasName entity, EntityId entityId, EntityId originatorId, + TenantId tenantId, CustomerId customerId, UserId userId, String userName, + ActionType actionType, Object... additionalInfo) { + testSendNotificationMsgToEdgeServiceOne(entityId, tenantId, actionType); + testLogEntityActionOne(entity, originatorId, tenantId, customerId, userId, userName, actionType, additionalInfo); + testPushMsgToRuleEngineOne(originatorId, tenantId); Mockito.reset(tbClusterService, auditLogService); } - protected void testNotifyEntityDeleteOk(HasName entity, EntityId entityId, EntityId originatorId, - TenantId tenantId, CustomerId customerId, UserId userId, String userName, - ActionType actionType) { - Mockito.verify(tbClusterService, never()).sendNotificationMsgToEdgeService(Mockito.any(), - Mockito.any(), Mockito.any(entityId.getClass()), Mockito.any(), Mockito.any(), Mockito.any()); - testLogEntityActionOk(entity, originatorId, tenantId, customerId, userId, userName, actionType); - testPushMsgToRuleEngineOk(entity, originatorId, tenantId, customerId, userId, userName, actionType); + + protected void testNotifyEntityDeleteOneMsgToEdgeServiceNever(HasName entity, EntityId entityId, EntityId originatorId, + TenantId tenantId, CustomerId customerId, UserId userId, String userName, + ActionType actionType, Object... additionalInfo) { + testNotificationMsgToEdgeServiceNever(entityId); + testLogEntityActionOne(entity, originatorId, tenantId, customerId, userId, userName, actionType, additionalInfo); + testPushMsgToRuleEngineOne(entityId, tenantId); + testBroadcastEntityStateChangeEventOne(entityId, tenantId); + } - private void testNotifyEntityError(EntityId entityId, HasName entity, TenantId tenantId, - UserId userId, String userName, ActionType actionType, Exception exp, - Object... additionalInfo) { + protected void testNotifyEntityOneMsgToEdgeServiceNever(HasName entity, EntityId entityId, EntityId originatorId, + TenantId tenantId, CustomerId customerId, UserId userId, String userName, + ActionType actionType, Object... additionalInfo) { + testNotificationMsgToEdgeServiceNever(entityId); + testLogEntityActionOne(entity, originatorId, tenantId, customerId, userId, userName, actionType, additionalInfo); + testPushMsgToRuleEngineOne(originatorId, tenantId); + Mockito.reset(tbClusterService, auditLogService); + } + + protected void testNotifyEntityBroadcastEntityStateChangeEventOneMsgToEdgeServiceNever(HasName entity, EntityId entityId, EntityId originatorId, + TenantId tenantId, CustomerId customerId, UserId userId, String userName, + ActionType actionType, Object... additionalInfo) { + testNotificationMsgToEdgeServiceNever(entityId); + testLogEntityActionOne(entity, originatorId, tenantId, customerId, userId, userName, actionType, additionalInfo); + testPushMsgToRuleEngineOne(originatorId, tenantId); + testBroadcastEntityStateChangeEventOne(entityId, tenantId); + Mockito.reset(tbClusterService, auditLogService); + } + + protected void testNotifyEntityError(HasName entity, TenantId tenantId, + UserId userId, String userName, ActionType actionType, Exception exp, + Object... additionalInfo) { CustomerId customer_NULL_UUID = (CustomerId) EntityIdFactory.getByTypeAndUuid(EntityType.CUSTOMER, ModelConstants.NULL_UUID); - EntityId entity_NULL_UUID = EntityIdFactory.getByTypeAndUuid(EntityType.valueOf(entity.getClass().toString().substring(entity.getClass().toString().lastIndexOf(".") + 1).toUpperCase(Locale.ENGLISH)), + EntityId entity_NULL_UUID = EntityIdFactory.getByTypeAndUuid(EntityType.valueOf(entity.getClass().toString() + .substring(entity.getClass().toString().lastIndexOf(".") + 1).toUpperCase(Locale.ENGLISH)), ModelConstants.NULL_UUID); - testNotificationMsgToEdgeServiceNever(entityId); + testNotificationMsgToEdgeServiceNever(entity_NULL_UUID); if (additionalInfo.length > 0) { Mockito.verify(auditLogService, times(1)).logEntityAction(Mockito.eq(tenantId), Mockito.eq(customer_NULL_UUID), Mockito.eq(userId), Mockito.eq(userName), @@ -77,9 +103,9 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest { Mockito.verify(auditLogService, times(1)).logEntityAction(Mockito.eq(tenantId), Mockito.eq(customer_NULL_UUID), Mockito.eq(userId), Mockito.eq(userName), Mockito.eq(entity_NULL_UUID), Mockito.any(entity.getClass()), Mockito.eq(actionType), - Mockito.any(exp.getClass()), Mockito.isNull()); + Mockito.any(exp.getClass())); } - Mockito.verify(tbClusterService, never()).pushMsgToRuleEngine(Mockito.any(), Mockito.any(entityId.getClass()), + Mockito.verify(tbClusterService, never()).pushMsgToRuleEngine(Mockito.any(), Mockito.any(entity_NULL_UUID.getClass()), Mockito.any(), Mockito.any()); Mockito.reset(tbClusterService, auditLogService); } @@ -106,27 +132,46 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest { Mockito.any(entityId.getClass()), Mockito.any(), Mockito.any()); } - private void testLogEntityActionOk(HasName entity, EntityId originatorId, - TenantId tenantId, CustomerId customerId, UserId userId, String userName, - ActionType actionType) { - Mockito.verify(auditLogService, times(1)).logEntityAction(Mockito.eq(tenantId), Mockito.eq(customerId), - Mockito.eq(userId), Mockito.eq(userName), Mockito.eq(originatorId), - Mockito.eq(entity), Mockito.eq(actionType), Mockito.isNull()); + private void testLogEntityActionOne(HasName entity, EntityId originatorId, TenantId tenantId, CustomerId customerId, + UserId userId, String userName, ActionType actionType, Object... additionalInfo) { + if (additionalInfo.length == 0) { + Mockito.verify(auditLogService, times(1)).logEntityAction(Mockito.eq(tenantId), Mockito.eq(customerId), + Mockito.eq(userId), Mockito.eq(userName), Mockito.eq(originatorId), + Mockito.eq(entity), Mockito.eq(actionType), Mockito.isNull()); + } else { + String additionalInfoStr = extractParameter(String.class, 0, additionalInfo); + Mockito.verify(auditLogService, times(1)).logEntityAction(Mockito.eq(tenantId), Mockito.eq(customerId), + Mockito.eq(userId), Mockito.eq(userName), Mockito.eq(originatorId), + Mockito.eq(entity), Mockito.eq(actionType), Mockito.isNull(), Mockito.eq(additionalInfoStr)); + } } - private void testPushMsgToRuleEngineOk(HasName entity, EntityId originatorId, - TenantId tenantId, CustomerId customerId, UserId userId, String userName, - ActionType actionType) { - - Mockito.verify(auditLogService, times(1)).logEntityAction(Mockito.eq(tenantId), Mockito.eq(customerId), - Mockito.eq(userId), Mockito.eq(userName), Mockito.eq(originatorId), - Mockito.eq(entity), Mockito.eq(actionType), Mockito.isNull()); + private void testPushMsgToRuleEngineOne(EntityId originatorId, TenantId tenantId) { + Mockito.verify(tbClusterService, times(1)).pushMsgToRuleEngine(Mockito.eq(tenantId), + Mockito.eq(originatorId), Mockito.any(TbMsg.class), Mockito.isNull()); } - private void testSendNotificationMsgToEdgeServiceOk(EntityId entityId, TenantId tenantId, ActionType actionType) { + private void testSendNotificationMsgToEdgeServiceOne(EntityId entityId, TenantId tenantId, ActionType actionType) { Mockito.verify(tbClusterService, times(1)).sendNotificationMsgToEdgeService(Mockito.eq(tenantId), Mockito.isNull(), Mockito.eq(entityId), Mockito.isNull(), Mockito.isNull(), Mockito.eq(edgeTypeByActionType(actionType))); } + private void testBroadcastEntityStateChangeEventOne(EntityId entityId, TenantId tenantId) { + Mockito.verify(tbClusterService, times(1)).broadcastEntityStateChangeEvent(Mockito.eq(tenantId), + Mockito.any(entityId.getClass()), Mockito.any(ComponentLifecycleEvent.class)); + } + + private T extractParameter(Class clazz, int index, Object... additionalInfo) { + T result = null; + if (additionalInfo != null && additionalInfo.length > index) { + Object paramObject = additionalInfo[index]; + if (clazz.isInstance(paramObject)) { + result = clazz.cast(paramObject); + } + } + return result; + } + + } diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseAlarmControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseAlarmControllerTest.java index 16a9f993c3..d82f4175b5 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseAlarmControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseAlarmControllerTest.java @@ -65,7 +65,7 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest { Alarm alarm = createAlarm(TEST_ALARM_TYPE); - testNotifyEntityOk(alarm, alarm.getId(), alarm.getOriginator(), + testNotifyEntityOne(alarm, alarm.getId(), alarm.getOriginator(), tenantId, customerId, customerUserId, CUSTOMER_USER_EMAIL, ActionType.ADDED); logout(); } @@ -78,7 +78,7 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest { Alarm alarm = createAlarm(TEST_ALARM_TYPE); - testNotifyEntityOk(alarm, alarm.getId(), alarm.getOriginator(), + testNotifyEntityOne(alarm, alarm.getId(), alarm.getOriginator(), tenantId, customerId, tenantAdminUserId, TENANT_ADMIN_EMAIL, ActionType.ADDED); logout(); } @@ -91,7 +91,7 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest { Alarm alarm = createAlarm(TEST_ALARM_TYPE); - testNotifyEntityOk(alarm, alarm.getId(), alarm.getOriginator(), + testNotifyEntityOne(alarm, alarm.getId(), alarm.getOriginator(), tenantId, customerId, customerUserId, CUSTOMER_USER_EMAIL, ActionType.ADDED); alarm.setSeverity(AlarmSeverity.MAJOR); @@ -99,7 +99,7 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest { Assert.assertNotNull(updatedAlarm); Assert.assertEquals(AlarmSeverity.MAJOR, updatedAlarm.getSeverity()); - testNotifyEntityOk(alarm, alarm.getId(), alarm.getOriginator(), + testNotifyEntityOne(alarm, alarm.getId(), alarm.getOriginator(), tenantId, customerId, customerUserId, CUSTOMER_USER_EMAIL, ActionType.UPDATED); logout(); } @@ -112,7 +112,7 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest { Alarm alarm = createAlarm(TEST_ALARM_TYPE); - testNotifyEntityOk(alarm, alarm.getId(), alarm.getOriginator(), + testNotifyEntityOne(alarm, alarm.getId(), alarm.getOriginator(), tenantId, customerId, tenantAdminUserId, TENANT_ADMIN_EMAIL, ActionType.ADDED); alarm.setSeverity(AlarmSeverity.MAJOR); @@ -120,7 +120,7 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest { Assert.assertNotNull(updatedAlarm); Assert.assertEquals(AlarmSeverity.MAJOR, updatedAlarm.getSeverity()); - testNotifyEntityOk(updatedAlarm, updatedAlarm.getId(), updatedAlarm.getOriginator(), + testNotifyEntityOne(updatedAlarm, updatedAlarm.getId(), updatedAlarm.getOriginator(), tenantId, customerId, tenantAdminUserId, TENANT_ADMIN_EMAIL, ActionType.UPDATED); logout(); } @@ -166,7 +166,7 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest { doDelete("/api/alarm/" + alarm.getId()).andExpect(status().isOk()); - testNotifyEntityDeleteOk(alarm, alarm.getId(), alarm.getOriginator(), + testNotifyEntityOneMsgToEdgeServiceNever(alarm, alarm.getId(), alarm.getOriginator(), tenantId, customerId, customerUserId, CUSTOMER_USER_EMAIL, ActionType.DELETED); logout(); } @@ -180,7 +180,7 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest { doDelete("/api/alarm/" + alarm.getId()).andExpect(status().isOk()); - testNotifyEntityDeleteOk(alarm, alarm.getId(), alarm.getOriginator(), + testNotifyEntityOneMsgToEdgeServiceNever(alarm, alarm.getId(), alarm.getOriginator(), tenantId, tenantAdminCustomerId, tenantAdminUserId, TENANT_ADMIN_EMAIL, ActionType.DELETED); logout(); } @@ -229,7 +229,7 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest { Assert.assertNotNull(foundAlarm); Assert.assertEquals(AlarmStatus.CLEARED_UNACK, foundAlarm.getStatus()); - testNotifyEntityOk(foundAlarm, foundAlarm.getId(), foundAlarm.getOriginator(), + testNotifyEntityOne(foundAlarm, foundAlarm.getId(), foundAlarm.getOriginator(), tenantId, customerId, customerUserId, CUSTOMER_USER_EMAIL, ActionType.ALARM_CLEAR); logout(); } @@ -246,7 +246,7 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest { Assert.assertNotNull(foundAlarm); Assert.assertEquals(AlarmStatus.CLEARED_UNACK, foundAlarm.getStatus()); - testNotifyEntityOk(foundAlarm, foundAlarm.getId(), foundAlarm.getOriginator(), + testNotifyEntityOne(foundAlarm, foundAlarm.getId(), foundAlarm.getOriginator(), tenantId, customerId, tenantAdminUserId, TENANT_ADMIN_EMAIL, ActionType.ALARM_CLEAR); logout(); } @@ -264,7 +264,7 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest { Assert.assertNotNull(foundAlarm); Assert.assertEquals(AlarmStatus.ACTIVE_ACK, foundAlarm.getStatus()); - testNotifyEntityOk(foundAlarm, foundAlarm.getId(), foundAlarm.getOriginator(), + testNotifyEntityOne(foundAlarm, foundAlarm.getId(), foundAlarm.getOriginator(), tenantId, customerId, customerUserId, CUSTOMER_USER_EMAIL, ActionType.ALARM_ACK); logout(); } diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseCustomerControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseCustomerControllerTest.java index a7f623d2e4..0aa81c2d18 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseCustomerControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseCustomerControllerTest.java @@ -25,14 +25,17 @@ import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; +import org.mockito.Mockito; import org.thingsboard.common.util.ThingsBoardExecutors; import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.User; +import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.security.Authority; +import org.thingsboard.server.dao.exception.DataValidationException; import java.util.ArrayList; import java.util.List; @@ -86,71 +89,164 @@ public abstract class BaseCustomerControllerTest extends AbstractControllerTest public void testSaveCustomer() throws Exception { Customer customer = new Customer(); customer.setTitle("My customer"); + + Mockito.reset(tbClusterService, auditLogService); + Customer savedCustomer = doPost("/api/customer", customer, Customer.class); Assert.assertNotNull(savedCustomer); Assert.assertNotNull(savedCustomer.getId()); Assert.assertTrue(savedCustomer.getCreatedTime() > 0); Assert.assertEquals(customer.getTitle(), savedCustomer.getTitle()); + + testNotifyEntityOneMsgToEdgeServiceNever(savedCustomer, savedCustomer.getId(), savedCustomer.getId(), + savedCustomer.getTenantId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), + tenantAdmin.getEmail(), ActionType.ADDED); + savedCustomer.setTitle("My new customer"); + + Mockito.reset(tbClusterService, auditLogService); + doPost("/api/customer", savedCustomer, Customer.class); + testNotifyEntityOne(savedCustomer, savedCustomer.getId(), savedCustomer.getId(), + savedCustomer.getTenantId(), savedCustomer.getId(), tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.UPDATED); + Customer foundCustomer = doGet("/api/customer/" + savedCustomer.getId().getId().toString(), Customer.class); Assert.assertEquals(foundCustomer.getTitle(), savedCustomer.getTitle()); doDelete("/api/customer/" + savedCustomer.getId().getId().toString()) .andExpect(status().isOk()); + + testNotifyEntityBroadcastEntityStateChangeEventOneMsgToEdgeServiceNever(savedCustomer, savedCustomer.getId(), savedCustomer.getId(), + savedCustomer.getTenantId(), savedCustomer.getId(), tenantAdmin.getId(), tenantAdmin.getEmail(), + ActionType.DELETED, savedCustomer.getId().getId().toString()); } @Test public void testSaveCustomerWithViolationOfValidation() throws Exception { Customer customer = new Customer(); + + Mockito.reset(tbClusterService, auditLogService); + + doPost("/api/customer", customer) + .andExpect(status().isBadRequest()) + .andExpect(statusReason(containsString("Customer title should be specified"))); + + testNotifyEntityError(customer, savedTenant.getId(), + tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, new DataValidationException("Test: empty title")); + customer.setTitle(RandomStringUtils.randomAlphabetic(300)); + doPost("/api/customer", customer).andExpect(statusReason(containsString("length of title must be equal or less than 255"))); + + testNotifyEntityError(customer, savedTenant.getId(), + tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, new DataValidationException("Test: bad title")); + customer.setTitle("Normal title"); + customer.setEmail("invalid@mail"); + doPost("/api/customer", customer) + .andExpect(status().isBadRequest()) + .andExpect(statusReason(containsString("Invalid email address format 'invalid@mail'"))); + + testNotifyEntityError(customer, savedTenant.getId(), + tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, new DataValidationException("Test: invalid email")); + + customer.setEmail("normal@mail.com.ua"); + customer.setCity(RandomStringUtils.randomAlphabetic(300)); doPost("/api/customer", customer).andExpect(statusReason(containsString("length of city must be equal or less than 255"))); + + testNotifyEntityError(customer, savedTenant.getId(), + tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, new DataValidationException("Test: bad City")); + customer.setCity("Normal city"); customer.setCountry(RandomStringUtils.randomAlphabetic(300)); doPost("/api/customer", customer).andExpect(statusReason(containsString("length of country must be equal or less than 255"))); + + testNotifyEntityError(customer, savedTenant.getId(), + tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, new DataValidationException("Test: bad Country")); + customer.setCountry("Ukraine"); customer.setPhone(RandomStringUtils.randomAlphabetic(300)); doPost("/api/customer", customer).andExpect(statusReason(containsString("length of phone must be equal or less than 255"))); + + testNotifyEntityError(customer, savedTenant.getId(), + tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, new DataValidationException("Test: bad Phone")); + customer.setPhone("+3892555554512"); customer.setState(RandomStringUtils.randomAlphabetic(300)); doPost("/api/customer", customer).andExpect(statusReason(containsString("length of state must be equal or less than 255"))); + + testNotifyEntityError(customer, savedTenant.getId(), + tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, new DataValidationException("Test: bad state")); + customer.setState("Normal state"); customer.setZip(RandomStringUtils.randomAlphabetic(300)); doPost("/api/customer", customer).andExpect(statusReason(containsString("length of zip or postal code must be equal or less than 255"))); + + testNotifyEntityError(customer, savedTenant.getId(), + tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, new DataValidationException("Test: bad Zip")); } @Test public void testUpdateCustomerFromDifferentTenant() throws Exception { Customer customer = new Customer(); customer.setTitle("My customer"); + + Mockito.reset(tbClusterService, auditLogService); + Customer savedCustomer = doPost("/api/customer", customer, Customer.class); + + testNotifyEntityOneMsgToEdgeServiceNever(savedCustomer, savedCustomer.getId(), savedCustomer.getId(), + savedCustomer.getTenantId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED); + doPost("/api/customer", savedCustomer, Customer.class); loginDifferentTenant(); + + Mockito.reset(tbClusterService, auditLogService); + doPost("/api/customer", savedCustomer, Customer.class, status().isForbidden()); + + testNotifyEntityNever(savedCustomer.getId(), savedCustomer); + deleteDifferentTenant(); login(tenantAdmin.getName(), "testPassword1"); + + Mockito.reset(tbClusterService, auditLogService); + doDelete("/api/customer/" + savedCustomer.getId().getId().toString()) .andExpect(status().isOk()); + + testNotifyEntityDeleteOneMsgToEdgeServiceNever(savedCustomer, savedCustomer.getId(), savedCustomer.getId(), + savedCustomer.getTenantId(), savedCustomer.getId(), tenantAdmin.getId(), tenantAdmin.getEmail(), + ActionType.DELETED, savedCustomer.getId().getId().toString()); } @Test public void testFindCustomerById() throws Exception { Customer customer = new Customer(); customer.setTitle("My customer"); + + Mockito.reset(tbClusterService, auditLogService); + Customer savedCustomer = doPost("/api/customer", customer, Customer.class); + testNotifyEntityOneMsgToEdgeServiceNever(savedCustomer, savedCustomer.getId(), savedCustomer.getId(), + savedCustomer.getTenantId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), + tenantAdmin.getEmail(), ActionType.ADDED); + Customer foundCustomer = doGet("/api/customer/" + savedCustomer.getId().getId().toString(), Customer.class); Assert.assertNotNull(foundCustomer); Assert.assertEquals(savedCustomer, foundCustomer); doDelete("/api/customer/" + savedCustomer.getId().getId().toString()) .andExpect(status().isOk()); + + testNotifyEntityBroadcastEntityStateChangeEventOneMsgToEdgeServiceNever(savedCustomer, savedCustomer.getId(), savedCustomer.getId(), + savedCustomer.getTenantId(), savedCustomer.getId(), tenantAdmin.getId(), tenantAdmin.getEmail(), + ActionType.DELETED, savedCustomer.getId().getId().toString()); } @Test @@ -159,36 +255,19 @@ public abstract class BaseCustomerControllerTest extends AbstractControllerTest customer.setTitle("My customer"); Customer savedCustomer = doPost("/api/customer", customer, Customer.class); + Mockito.reset(tbClusterService, auditLogService); + doDelete("/api/customer/" + savedCustomer.getId().getId().toString()) .andExpect(status().isOk()); + testNotifyEntityBroadcastEntityStateChangeEventOneMsgToEdgeServiceNever(savedCustomer, savedCustomer.getId(), savedCustomer.getId(), + savedCustomer.getTenantId(), savedCustomer.getId(), tenantAdmin.getId(), tenantAdmin.getEmail(), + ActionType.DELETED, savedCustomer.getId().getId().toString()); + doGet("/api/customer/" + savedCustomer.getId().getId().toString()) .andExpect(status().isNotFound()); } - @Test - public void testSaveCustomerWithEmptyTitle() throws Exception { - Customer customer = new Customer(); - doPost("/api/customer", customer) - .andExpect(status().isBadRequest()) - .andExpect(statusReason(containsString("Customer title should be specified"))); - } - - @Test - public void testSaveCustomerWithInvalidEmail() throws Exception { - Customer customer = new Customer(); - customer.setTitle("My customer"); - customer.setEmail("invalid@mail"); - doPost("/api/customer", customer) - .andExpect(status().isBadRequest()) - .andExpect(statusReason(containsString("Invalid email address format 'invalid@mail'"))); - -// loginSysAdmin(); -// -// doDelete("/api/tenant/"+savedTenant.getId().getId().toString()) -// .andExpect(status().isOk()); - } - @Test public void testFindCustomers() throws Exception { TenantId tenantId = savedTenant.getId(); From 6ebd4120c13311ded31671540df8da63247db959 Mon Sep 17 00:00:00 2001 From: fe-dev Date: Tue, 14 Jun 2022 10:21:01 +0300 Subject: [PATCH 789/798] UI: Remove padding --- .../components/dashboard-page/add-widget-dialog.component.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui-ngx/src/app/modules/home/components/dashboard-page/add-widget-dialog.component.html b/ui-ngx/src/app/modules/home/components/dashboard-page/add-widget-dialog.component.html index 99f5e6ebfd..9fa86d8f61 100644 --- a/ui-ngx/src/app/modules/home/components/dashboard-page/add-widget-dialog.component.html +++ b/ui-ngx/src/app/modules/home/components/dashboard-page/add-widget-dialog.component.html @@ -28,7 +28,7 @@ -
+
Date: Tue, 14 Jun 2022 11:18:24 +0300 Subject: [PATCH 790/798] UI: Fixed vulnerabilities in tb-js-executor --- msa/js-executor/package.json | 35 +- msa/js-executor/pom.xml | 4 +- msa/js-executor/queue/serviceBusTemplate.js | 53 +- msa/js-executor/yarn.lock | 2317 +++++++++---------- 4 files changed, 1101 insertions(+), 1308 deletions(-) diff --git a/msa/js-executor/package.json b/msa/js-executor/package.json index 6cbfc1261d..d1df447072 100644 --- a/msa/js-executor/package.json +++ b/msa/js-executor/package.json @@ -12,20 +12,19 @@ "start-prod": "NODE_ENV=production nodemon server.js" }, "dependencies": { - "@azure/service-bus": "^1.1.9", - "@google-cloud/pubsub": "^2.5.0", - "amqplib": "^0.6.0", - "aws-sdk": "^2.741.0", - "azure-sb": "^0.11.1", - "config": "^3.3.1", - "express": "^4.17.1", - "js-yaml": "^3.14.0", - "kafkajs": "^1.15.0", - "long": "^4.0.0", + "@azure/service-bus": "^7.5.1", + "@google-cloud/pubsub": "^3.0.1", + "amqplib": "^0.10.0", + "aws-sdk": "^2.1152.0", + "config": "^3.3.7", + "express": "^4.18.1", + "js-yaml": "^4.1.0", + "kafkajs": "^2.0.2", + "long": "^5.2.0", "uuid-parse": "^1.1.0", "uuid-random": "^1.3.2", - "winston": "^3.3.3", - "winston-daily-rotate-file": "^4.5.0" + "winston": "^3.7.2", + "winston-daily-rotate-file": "^4.7.1" }, "nyc": { "exclude": [ @@ -36,13 +35,19 @@ ] }, "devDependencies": { - "fs-extra": "^10.0.0", - "nodemon": "^2.0.12", - "pkg": "^5.3.1" + "fs-extra": "^10.1.0", + "nodemon": "^2.0.16", + "pkg": "^5.7.0" }, "pkg": { "assets": [ "node_modules/config/**/*.*" ] + }, + "resolutions": { + "ansi-regex": "^5.0.1", + "color-string": "^1.5.5", + "minimist": "^1.2.6", + "node-fetch": "^2.6.7" } } diff --git a/msa/js-executor/pom.xml b/msa/js-executor/pom.xml index 5532a70b78..a910e4759f 100644 --- a/msa/js-executor/pom.xml +++ b/msa/js-executor/pom.xml @@ -71,8 +71,8 @@ install-node-and-yarn - v12.16.1 - v1.22.4 + v16.13.1 + v1.22.17 diff --git a/msa/js-executor/queue/serviceBusTemplate.js b/msa/js-executor/queue/serviceBusTemplate.js index 9016640c67..e8efae1635 100644 --- a/msa/js-executor/queue/serviceBusTemplate.js +++ b/msa/js-executor/queue/serviceBusTemplate.js @@ -18,8 +18,7 @@ const config = require('config'), JsInvokeMessageProcessor = require('../api/jsInvokeMessageProcessor'), logger = require('../config/logger')._logger('serviceBusTemplate'); -const {ServiceBusClient, ReceiveMode} = require("@azure/service-bus"); -const azure = require('azure-sb'); +const {ServiceBusClient, ServiceBusAdministrationClient} = require("@azure/service-bus"); const requestTopic = config.get('request_topic'); const namespaceName = config.get('service_bus.namespace_name'); @@ -28,7 +27,6 @@ const sasKey = config.get('service_bus.sas_key'); const queueProperties = config.get('service_bus.queue_properties'); let sbClient; -let receiverClient; let receiver; let serviceBusService; @@ -61,11 +59,10 @@ function ServiceBusProducer() { } function CustomSender(topic) { - this.queueClient = sbClient.createQueueClient(topic); - this.sender = this.queueClient.createSender(); + this.sender = sbClient.createSender(topic); this.send = async (message) => { - return this.sender.send(message); + return this.sender.sendMessages(message); } } @@ -74,8 +71,8 @@ function CustomSender(topic) { logger.info('Starting ThingsBoard JavaScript Executor Microservice...'); const connectionString = `Endpoint=sb://${namespaceName}.servicebus.windows.net/;SharedAccessKeyName=${sasKeyName};SharedAccessKey=${sasKey}`; - sbClient = ServiceBusClient.createFromConnectionString(connectionString); - serviceBusService = azure.createServiceBusService(connectionString); + sbClient = new ServiceBusClient(connectionString) + serviceBusService = new ServiceBusAdministrationClient(connectionString); parseQueueProperties(); @@ -84,9 +81,9 @@ function CustomSender(topic) { if (err) { reject(err); } else { - data.forEach(queue => { - queues.push(queue.QueueName); - }); + for (const queue of data) { + queues.push(queue.name); + } resolve(); } }); @@ -97,8 +94,7 @@ function CustomSender(topic) { queues.push(requestTopic); } - receiverClient = sbClient.createQueueClient(requestTopic); - receiver = receiverClient.createReceiver(ReceiveMode.peekLock); + receiver = sbClient.createReceiver(requestTopic, {receiveMode: 'peekLock'}); const messageProcessor = new JsInvokeMessageProcessor(new ServiceBusProducer()); @@ -111,18 +107,18 @@ function CustomSender(topic) { const errorHandler = (error) => { logger.error('Failed to receive message from queue.', error); }; - receiver.registerMessageHandler(messageHandler, errorHandler); + receiver.subscribe({processMessage: messageHandler, processError: errorHandler}) } catch (e) { logger.error('Failed to start ThingsBoard JavaScript Executor Microservice: %s', e.message); logger.error(e.stack); - exit(-1); + await exit(-1); } })(); async function createQueueIfNotExist(topic) { return new Promise((resolve, reject) => { - serviceBusService.createQueueIfNotExists(topic, queueOptions, (err) => { - if (err) { + serviceBusService.createQueue(topic, queueOptions, (err) => { + if (err && err.code !== "MessageEntityAlreadyExistsError") { reject(err); } else { resolve(); @@ -139,10 +135,10 @@ function parseQueueProperties() { properties[p.substring(0, delimiterPosition)] = p.substring(delimiterPosition + 1); }); queueOptions = { - DuplicateDetection: 'false', - MaxSizeInMegabytes: properties['maxSizeInMb'], - DefaultMessageTimeToLive: `PT${properties['messageTimeToLiveInSec']}S`, - LockDuration: `PT${properties['lockDurationInSec']}S` + requiresDuplicateDetection: false, + maxSizeInMegabytes: properties['maxSizeInMb'], + defaultMessageTimeToLive: `PT${properties['messageTimeToLiveInSec']}S`, + lockDuration: `PT${properties['lockDurationInSec']}S` }; } @@ -161,24 +157,11 @@ async function exit(status) { } } - if (receiverClient) { - try { - await receiverClient.close(); - } catch (e) { - - } - } - senderMap.forEach((k, v) => { try { v.sender.close(); } catch (e) { - } - try { - v.queueClient.close(); - } catch (e) { - } }); @@ -191,4 +174,4 @@ async function exit(status) { } logger.info('Azure Service Bus resources stopped.') process.exit(status); -} \ No newline at end of file +} diff --git a/msa/js-executor/yarn.lock b/msa/js-executor/yarn.lock index 0c46da466a..f93ad99803 100644 --- a/msa/js-executor/yarn.lock +++ b/msa/js-executor/yarn.lock @@ -9,75 +9,71 @@ dependencies: tslib "^1.9.3" -"@azure/amqp-common@1.0.0-preview.16": - version "1.0.0-preview.16" - resolved "https://registry.yarnpkg.com/@azure/amqp-common/-/amqp-common-1.0.0-preview.16.tgz#3b7a9d39f7503347530fe9afddf92fa7bfd1ec5e" - integrity sha512-rMTBh54lV6/SBujXfPX0OqN+UNpG1zR4EYY+JP66QM0D2jzNdxLiv6gart5Ersy4Tmd89MRlgikaj5+t5NsDgA== +"@azure/core-amqp@^3.1.0": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@azure/core-amqp/-/core-amqp-3.1.0.tgz#f8e210cfbabbb5d163649eedeb80c0b69045d358" + integrity sha512-TyI0WFNrVb0EkRg36UwdcqR/7n9YpcEw64O4xVrgzMAlXIciVZpabl05C/Q0iUvLkItmez2XSVDduncjr02oGw== dependencies: - "@types/async-lock" "^1.1.0" - "@types/is-buffer" "^2.0.0" - async-lock "^1.1.3" - buffer "^5.2.1" - debug "^3.1.0" + "@azure/abort-controller" "^1.0.0" + "@azure/core-auth" "^1.3.0" + "@azure/logger" "^1.0.0" + buffer "^6.0.0" events "^3.0.0" - is-buffer "^2.0.3" - jssha "^2.3.1" + jssha "^3.1.0" process "^0.11.10" - rhea "^1.0.18" - rhea-promise "^0.1.15" - stream-browserify "^2.0.2" - tslib "^1.9.3" + rhea "^2.0.3" + rhea-promise "^2.1.0" + tslib "^2.2.0" url "^0.11.0" - util "^0.11.1" + util "^0.12.1" -"@azure/core-auth@^1.1.3": - version "1.1.3" - resolved "https://registry.yarnpkg.com/@azure/core-auth/-/core-auth-1.1.3.tgz#94e7bbc207010e7a2fdba61565443e4e1cf1e131" - integrity sha512-A4xigW0YZZpkj1zK7dKuzbBpGwnhEcRk6WWuIshdHC32raR3EQ1j6VA9XZqE+RFsUgH6OAmIK5BWIz+mZjnd6Q== +"@azure/core-asynciterator-polyfill@^1.0.0": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@azure/core-asynciterator-polyfill/-/core-asynciterator-polyfill-1.0.2.tgz#0dd3849fb8d97f062a39db0e5cadc9ffaf861fec" + integrity sha512-3rkP4LnnlWawl0LZptJOdXNrT/fHp2eQMadoasa6afspXdpGrtPZuAQc2PD0cpgyuoXtUWyC3tv7xfntjGS5Dw== + +"@azure/core-auth@^1.3.0": + version "1.3.2" + resolved "https://registry.yarnpkg.com/@azure/core-auth/-/core-auth-1.3.2.tgz#6a2c248576c26df365f6c7881ca04b7f6d08e3d0" + integrity sha512-7CU6DmCHIZp5ZPiZ9r3J17lTKMmYsm/zGvNkjArQwPkrLlZ1TZ+EUYfGgh2X31OLMVAQCTJZW4cXHJi02EbJnA== dependencies: "@azure/abort-controller" "^1.0.0" - "@azure/core-tracing" "1.0.0-preview.8" - "@opentelemetry/api" "^0.6.1" - tslib "^2.0.0" + tslib "^2.2.0" -"@azure/core-http@^1.0.0": - version "1.1.6" - resolved "https://registry.yarnpkg.com/@azure/core-http/-/core-http-1.1.6.tgz#9d76bc569c9907e3224bd09c09b4ac08bde9faf8" - integrity sha512-/C+qNzhwlLKt0F6SjaBEyY2pwZvwL2LviyS5PHlCh77qWuTF1sETmYAINM88BCN+kke+UlECK4YOQaAjJwyHvQ== +"@azure/core-http@^2.0.0": + version "2.2.5" + resolved "https://registry.yarnpkg.com/@azure/core-http/-/core-http-2.2.5.tgz#e8cf8345d4a7f0ee7c8304a300c43d2df992ddbe" + integrity sha512-kctMqSQ6zfnlFpuYzfUKadeTyOQYbIQ+3Rj7dzVC3Dk1dOnHroTwR9hLYKX8/n85iJpkyaksaXpuh5L7GJRYuQ== dependencies: "@azure/abort-controller" "^1.0.0" - "@azure/core-auth" "^1.1.3" - "@azure/core-tracing" "1.0.0-preview.9" + "@azure/core-auth" "^1.3.0" + "@azure/core-tracing" "1.0.0-preview.13" "@azure/logger" "^1.0.0" - "@opentelemetry/api" "^0.10.2" "@types/node-fetch" "^2.5.0" - "@types/tunnel" "^0.0.1" - form-data "^3.0.0" - node-fetch "^2.6.0" + "@types/tunnel" "^0.0.3" + form-data "^4.0.0" + node-fetch "^2.6.7" process "^0.11.10" tough-cookie "^4.0.0" - tslib "^2.0.0" + tslib "^2.2.0" tunnel "^0.0.6" - uuid "^8.1.0" + uuid "^8.3.0" xml2js "^0.4.19" -"@azure/core-tracing@1.0.0-preview.8": - version "1.0.0-preview.8" - resolved "https://registry.yarnpkg.com/@azure/core-tracing/-/core-tracing-1.0.0-preview.8.tgz#1e0ff857e855edb774ffd33476003c27b5bb2705" - integrity sha512-ZKUpCd7Dlyfn7bdc+/zC/sf0aRIaNQMDuSj2RhYRFe3p70hVAnYGp3TX4cnG2yoEALp/LTj/XnZGQ8Xzf6Ja/Q== +"@azure/core-paging@^1.1.1": + version "1.3.0" + resolved "https://registry.yarnpkg.com/@azure/core-paging/-/core-paging-1.3.0.tgz#ebf6520a931be7d5ff1cf58bc4fe6c14d6a3080d" + integrity sha512-H6Tg9eBm0brHqLy0OSAGzxIh1t4UL8eZVrSUMJ60Ra9cwq2pOskFqVpz2pYoHDsBY1jZ4V/P8LRGb5D5pmC6rg== dependencies: - "@opencensus/web-types" "0.0.7" - "@opentelemetry/api" "^0.6.1" - tslib "^1.10.0" + tslib "^2.2.0" -"@azure/core-tracing@1.0.0-preview.9": - version "1.0.0-preview.9" - resolved "https://registry.yarnpkg.com/@azure/core-tracing/-/core-tracing-1.0.0-preview.9.tgz#84f3b85572013f9d9b85e1e5d89787aa180787eb" - integrity sha512-zczolCLJ5QG42AEPQ+Qg9SRYNUyB+yZ5dzof4YEc+dyWczO9G2sBqbAjLB7IqrsdHN2apkiB2oXeDKCsq48jug== +"@azure/core-tracing@1.0.0-preview.13": + version "1.0.0-preview.13" + resolved "https://registry.yarnpkg.com/@azure/core-tracing/-/core-tracing-1.0.0-preview.13.tgz#55883d40ae2042f6f1e12b17dd0c0d34c536d644" + integrity sha512-KxDlhXyMlh2Jhj2ykX6vNEU0Vou4nHr025KoSEiz7cS3BNiHNaZcdECk/DmLkEB0as5T7b/TpRcehJ5yV6NeXQ== dependencies: - "@opencensus/web-types" "0.0.7" - "@opentelemetry/api" "^0.10.2" - tslib "^2.0.0" + "@opentelemetry/api" "^1.0.1" + tslib "^2.2.0" "@azure/logger@^1.0.0": version "1.0.0" @@ -86,44 +82,52 @@ dependencies: tslib "^1.9.3" -"@azure/service-bus@^1.1.9": - version "1.1.9" - resolved "https://registry.yarnpkg.com/@azure/service-bus/-/service-bus-1.1.9.tgz#856cec37d0fbd8069fd9b717afa3aa9fdb48c95b" - integrity sha512-7i+7h1gh8vUeeaHddEVg+x7lXSVqhk/f2z4x4ErZByrE0QsyibFVPpBqS3q1RIjpDgmKPxcj7Wzu7O8vJLSaRw== +"@azure/service-bus@^7.5.1": + version "7.5.1" + resolved "https://registry.yarnpkg.com/@azure/service-bus/-/service-bus-7.5.1.tgz#5c006f29cf42779c20b87431e4482150b83be736" + integrity sha512-fIbI5aJDzN2HctcS+3i7rXY3L5jWG7/SuXOq0cMpwy+aH5aHOZVoxX8eeSnUnr2jUpUflU1wJ9yiYZ/sXqZvqw== dependencies: - "@azure/amqp-common" "1.0.0-preview.16" - "@azure/core-http" "^1.0.0" - "@opentelemetry/types" "^0.2.0" + "@azure/abort-controller" "^1.0.0" + "@azure/core-amqp" "^3.1.0" + "@azure/core-asynciterator-polyfill" "^1.0.0" + "@azure/core-auth" "^1.3.0" + "@azure/core-http" "^2.0.0" + "@azure/core-paging" "^1.1.1" + "@azure/core-tracing" "1.0.0-preview.13" + "@azure/logger" "^1.0.0" "@types/is-buffer" "^2.0.0" - "@types/long" "^4.0.0" - buffer "^5.2.1" - debug "^4.1.1" + "@types/long" "^4.0.1" + buffer "^6.0.0" is-buffer "^2.0.3" + jssha "^3.1.0" long "^4.0.0" process "^0.11.10" - rhea "^1.0.23" - rhea-promise "^0.1.15" - tslib "^1.10.0" - -"@babel/helper-validator-identifier@^7.12.11": - version "7.14.9" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.9.tgz#6654d171b2024f6d8ee151bf2509699919131d48" - integrity sha512-pQYxPY0UP6IHISRitNe8bsijHex4TWZXi2HwKVsjPiltzlhse2znVcm9Ace510VT1kxIHjGJCZZQBX2gJDbo0g== - -"@babel/parser@7.13.13": - version "7.13.13" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.13.13.tgz#42f03862f4aed50461e543270916b47dd501f0df" - integrity sha512-OhsyMrqygfk5v8HmWwOzlYjJrtLaFhF34MrfG/Z73DgYCI6ojNUTUp2TYbtnjo8PegeJp12eamsNettCQjKjVw== - -"@babel/types@7.13.12": - version "7.13.12" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.13.12.tgz#edbf99208ef48852acdff1c8a681a1e4ade580cd" - integrity sha512-K4nY2xFN4QMvQwkQ+zmBDp6ANMbVNw6BbxWmYA4qNjhR9W+Lj/8ky5MEY2Me5r+B2c6/v6F53oMndG+f9s3IiA== - dependencies: - "@babel/helper-validator-identifier" "^7.12.11" - lodash "^4.17.19" + rhea-promise "^2.1.0" + tslib "^2.2.0" + +"@babel/helper-validator-identifier@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz#e8c602438c4a8195751243da9031d1607d247cad" + integrity sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw== + +"@babel/parser@7.17.10": + version "7.17.10" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.17.10.tgz#873b16db82a8909e0fbd7f115772f4b739f6ce78" + integrity sha512-n2Q6i+fnJqzOaq2VkdXxy2TCPCWQZHiCo0XqmrCvDWcZQKRyZzYi4Z0yxlBuN0w+r2ZHmre+Q087DSrw3pbJDQ== + +"@babel/types@7.17.10": + version "7.17.10" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.17.10.tgz#d35d7b4467e439fcf06d195f8100e0fea7fc82c4" + integrity sha512-9O26jG0mBYfGkUYCYZRnBwbVLd1UZOICEr2Em6InB6jVfsAv1GKgwXHmrSg+WFWDmeKTA6vyTZiN8tCSM5Oo3A== + dependencies: + "@babel/helper-validator-identifier" "^7.16.7" to-fast-properties "^2.0.0" +"@colors/colors@1.5.0": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9" + integrity sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ== + "@dabh/diagnostics@^2.0.2": version "2.0.2" resolved "https://registry.yarnpkg.com/@dabh/diagnostics/-/diagnostics-2.0.2.tgz#290d08f7b381b8f94607dc8f471a12c675f9db31" @@ -133,10 +137,10 @@ enabled "2.0.x" kuler "^2.0.0" -"@google-cloud/paginator@^3.0.0": - version "3.0.4" - resolved "https://registry.yarnpkg.com/@google-cloud/paginator/-/paginator-3.0.4.tgz#4106cadd8153d157c8afd16d66dfb89a38ba8748" - integrity sha512-fKI+jYQdV1F9jtG6tSRro3ilNSeBWVmTzxc8Z0kiPRXcj8eshh9fiF8TtxfDefyUKgTdWgHpzGBwLbZ/OGikJg== +"@google-cloud/paginator@^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@google-cloud/paginator/-/paginator-4.0.0.tgz#9c3e01544717aecb9a922b4269ff298f30a0f1bb" + integrity sha512-wNmCZl+2G2DmgT/VlF+AROf80SoaC/CwS8trwmjNaq26VRNK8yPbU5F/Vy+R9oDAGKWQU2k8+Op5H4kFJVXFaQ== dependencies: arrify "^2.0.0" extend "^3.0.2" @@ -156,55 +160,45 @@ resolved "https://registry.yarnpkg.com/@google-cloud/promisify/-/promisify-2.0.2.tgz#81d654b4cb227c65c7ad2f9a7715262febd409ed" integrity sha512-EvuabjzzZ9E2+OaYf+7P9OAiiwbTxKYL0oGLnREQd+Su2NTQBpomkdlkBowFvyWsaV0d1sSGxrKpSNcrhPqbxg== -"@google-cloud/pubsub@^2.5.0": - version "2.5.0" - resolved "https://registry.yarnpkg.com/@google-cloud/pubsub/-/pubsub-2.5.0.tgz#6c696d9b448f2e1689be9a37ef0362ed173731fd" - integrity sha512-7bbbQqa+LSTopVjt20EZ8maO6rEpbO7v8EvDImHMsbRS30HJ5+kClbaQTRvhNzhc1qy221A1GbHPHMCQ/U5E3Q== +"@google-cloud/pubsub@^3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@google-cloud/pubsub/-/pubsub-3.0.1.tgz#3a6bb9649a1b4309d82d8670a9c01375e622692f" + integrity sha512-dznNbRd/Y8J0C0xvdvCPi3B1msK/dj/Nya+NQZ2doUOLT6eoa261tBwk9umOQs5L5GKcdlqQKbBjrNjDYVbzQA== dependencies: - "@google-cloud/paginator" "^3.0.0" + "@google-cloud/paginator" "^4.0.0" "@google-cloud/precise-date" "^2.0.0" "@google-cloud/projectify" "^2.0.0" "@google-cloud/promisify" "^2.0.0" - "@opentelemetry/api" "^0.10.0" - "@opentelemetry/tracing" "^0.10.0" + "@opentelemetry/api" "^1.0.0" + "@opentelemetry/semantic-conventions" "^1.0.0" "@types/duplexify" "^3.6.0" "@types/long" "^4.0.0" arrify "^2.0.0" extend "^3.0.2" - google-auth-library "^6.0.0" - google-gax "^2.7.0" + google-auth-library "^8.0.2" + google-gax "^3.0.1" is-stream-ended "^0.1.4" lodash.snakecase "^4.1.1" p-defer "^3.0.0" -"@grpc/grpc-js@~1.1.1": - version "1.1.5" - resolved "https://registry.yarnpkg.com/@grpc/grpc-js/-/grpc-js-1.1.5.tgz#2d0b261cd54a529f6b78ac0de9d6fd91a9a3129c" - integrity sha512-2huf5z85TdZI4nLmJQ9Zdfd+6vmIyBDs7B4L71bTaHKA9pRsGKAH24XaktMk/xneKJIqAgeIZtg1cyivVZtvrg== +"@grpc/grpc-js@~1.6.0": + version "1.6.7" + resolved "https://registry.yarnpkg.com/@grpc/grpc-js/-/grpc-js-1.6.7.tgz#4c4fa998ff719fe859ac19fe977fdef097bb99aa" + integrity sha512-eBM03pu9hd3VqDQG+kHahiG1x80RGkkqqRb1Pchcwqej/KkAH95gAvKs6laqaHCycYaPK+TKuNQnOz9UXYA8qw== dependencies: - "@grpc/proto-loader" "^0.6.0-pre14" - "@types/node" "^12.12.47" - google-auth-library "^6.0.0" - semver "^6.2.0" - -"@grpc/proto-loader@^0.5.1": - version "0.5.5" - resolved "https://registry.yarnpkg.com/@grpc/proto-loader/-/proto-loader-0.5.5.tgz#6725e7a1827bdf8e92e29fbf4e9ef0203c0906a9" - integrity sha512-WwN9jVNdHRQoOBo9FDH7qU+mgfjPc8GygPYms3M+y3fbQLfnCe/Kv/E01t7JRgnrsOHH8euvSbed3mIalXhwqQ== - dependencies: - lodash.camelcase "^4.3.0" - protobufjs "^6.8.6" + "@grpc/proto-loader" "^0.6.4" + "@types/node" ">=12.12.47" -"@grpc/proto-loader@^0.6.0-pre14": - version "0.6.0-pre9" - resolved "https://registry.yarnpkg.com/@grpc/proto-loader/-/proto-loader-0.6.0-pre9.tgz#0c6fe42f6c5ef9ce1b3cef7be64d5b09d6fe4d6d" - integrity sha512-oM+LjpEjNzW5pNJjt4/hq1HYayNeQT+eGrOPABJnYHv7TyNPDNzkQ76rDYZF86X5swJOa4EujEMzQ9iiTdPgww== +"@grpc/proto-loader@^0.6.12", "@grpc/proto-loader@^0.6.4": + version "0.6.13" + resolved "https://registry.yarnpkg.com/@grpc/proto-loader/-/proto-loader-0.6.13.tgz#008f989b72a40c60c96cd4088522f09b05ac66bc" + integrity sha512-FjxPYDRTn6Ec3V0arm1FtSpmP6V50wuph2yILpyvTKzjc76oDdoihXqM1DzOW5ubvCC8GivfCnNtfaRE8myJ7g== dependencies: "@types/long" "^4.0.1" lodash.camelcase "^4.3.0" long "^4.0.0" - protobufjs "^6.9.0" - yargs "^15.3.1" + protobufjs "^6.11.3" + yargs "^16.2.0" "@nodelib/fs.scandir@2.1.3": version "2.1.3" @@ -227,67 +221,15 @@ "@nodelib/fs.scandir" "2.1.3" fastq "^1.6.0" -"@opencensus/web-types@0.0.7": - version "0.0.7" - resolved "https://registry.yarnpkg.com/@opencensus/web-types/-/web-types-0.0.7.tgz#4426de1fe5aa8f624db395d2152b902874f0570a" - integrity sha512-xB+w7ZDAu3YBzqH44rCmG9/RlrOmFuDPt/bpf17eJr8eZSrLt7nc7LnWdxM9Mmoj/YKMHpxRg28txu3TcpiL+g== - -"@opentelemetry/api@^0.10.0", "@opentelemetry/api@^0.10.2": - version "0.10.2" - resolved "https://registry.yarnpkg.com/@opentelemetry/api/-/api-0.10.2.tgz#9647b881f3e1654089ff7ea59d587b2d35060654" - integrity sha512-GtpMGd6vkzDMYcpu2t9LlhEgMy/SzBwRnz48EejlRArYqZzqSzAsKmegUK7zHgl+EOIaK9mKHhnRaQu3qw20cA== - dependencies: - "@opentelemetry/context-base" "^0.10.2" - -"@opentelemetry/api@^0.6.1": - version "0.6.1" - resolved "https://registry.yarnpkg.com/@opentelemetry/api/-/api-0.6.1.tgz#a00b504801f408230b9ad719716fe91ad888c642" - integrity sha512-wpufGZa7tTxw7eAsjXJtiyIQ42IWQdX9iUQp7ACJcKo1hCtuhLU+K2Nv1U6oRwT1oAlZTE6m4CgWKZBhOiau3Q== - dependencies: - "@opentelemetry/context-base" "^0.6.1" - -"@opentelemetry/context-base@^0.10.2": - version "0.10.2" - resolved "https://registry.yarnpkg.com/@opentelemetry/context-base/-/context-base-0.10.2.tgz#55bea904b2b91aa8a8675df9eaba5961bddb1def" - integrity sha512-hZNKjKOYsckoOEgBziGMnBcX0M7EtstnCmwz5jZUOUYwlZ+/xxX6z3jPu1XVO2Jivk0eLfuP9GP+vFD49CMetw== +"@opentelemetry/api@^1.0.0", "@opentelemetry/api@^1.0.1": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/api/-/api-1.1.0.tgz#563539048255bbe1a5f4f586a4a10a1bb737f44a" + integrity sha512-hf+3bwuBwtXsugA2ULBc95qxrOqP2pOekLz34BJhcAKawt94vfeNyUKpYc0lZQ/3sCP6LqRa7UAdHA7i5UODzQ== -"@opentelemetry/context-base@^0.6.1": - version "0.6.1" - resolved "https://registry.yarnpkg.com/@opentelemetry/context-base/-/context-base-0.6.1.tgz#b260e454ee4f9635ea024fc83be225e397f15363" - integrity sha512-5bHhlTBBq82ti3qPT15TRxkYTFPPQWbnkkQkmHPtqiS1XcTB69cEKd3Jm7Cfi/vkPoyxapmePE9tyA7EzLt8SQ== - -"@opentelemetry/core@^0.10.2": - version "0.10.2" - resolved "https://registry.yarnpkg.com/@opentelemetry/core/-/core-0.10.2.tgz#86b9e94bbcaf8e07bb86e8205aa1d53af854e7de" - integrity sha512-DhkiTp5eje2zTGd+HAIKWpGE6IR6lq7tUpYt4nnkhOi6Hq9WQAANVDCWEZEbYOw57LkdXbE50FZ/kMvHDm450Q== - dependencies: - "@opentelemetry/api" "^0.10.2" - "@opentelemetry/context-base" "^0.10.2" - semver "^7.1.3" - -"@opentelemetry/resources@^0.10.2": - version "0.10.2" - resolved "https://registry.yarnpkg.com/@opentelemetry/resources/-/resources-0.10.2.tgz#6e291d525450359c615aac013fd977047f2c26d7" - integrity sha512-5JGC2TPSAIHth615IURt+sSsTljY43zTfJD0JE9PHC6ipZPiQ0dpQDZOrLn8NAMfOHY1jeWwpIuLASjqbXUfuw== - dependencies: - "@opentelemetry/api" "^0.10.2" - "@opentelemetry/core" "^0.10.2" - gcp-metadata "^3.5.0" - -"@opentelemetry/tracing@^0.10.0": - version "0.10.2" - resolved "https://registry.yarnpkg.com/@opentelemetry/tracing/-/tracing-0.10.2.tgz#384779a6e1be988200cc316a97030d95bd8f2129" - integrity sha512-mNAhARn4dEdOjTa9OdysjI4fRHMbvr4YSbPuH7jhkyPzgoa+DnvnbY3GGpEay6kpuYJsrW8Ef9OIKAV/GndhbQ== - dependencies: - "@opentelemetry/api" "^0.10.2" - "@opentelemetry/context-base" "^0.10.2" - "@opentelemetry/core" "^0.10.2" - "@opentelemetry/resources" "^0.10.2" - -"@opentelemetry/types@^0.2.0": - version "0.2.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/types/-/types-0.2.0.tgz#2a0afd40fa7026e39ea56a454642bda72b172f80" - integrity sha512-GtwNB6BNDdsIPAYEdpp3JnOGO/3AJxjPvny53s3HERBdXSJTGQw8IRhiaTEX0b3w9P8+FwFZde4k+qkjn67aVw== +"@opentelemetry/semantic-conventions@^1.0.0": + version "1.3.1" + resolved "https://registry.yarnpkg.com/@opentelemetry/semantic-conventions/-/semantic-conventions-1.3.1.tgz#ba07b864a3c955f061aa30ea3ef7f4ae4449794a" + integrity sha512-wU5J8rUoo32oSef/rFpOT1HIjLjAv3qIDHkw1QIhODV3OpAVHi5oVzlouozg9obUmZKtbZ0qUe/m7FP0y0yBzA== "@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2": version "1.1.2" @@ -354,11 +296,6 @@ dependencies: defer-to-connect "^1.0.1" -"@types/async-lock@^1.1.0": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@types/async-lock/-/async-lock-1.1.2.tgz#cbc26a34b11b83b28f7783a843c393b443ef8bef" - integrity sha512-j9n4bb6RhgFIydBe0+kpjnBPYumDaDyU8zvbWykyVMkku+c2CSu31MZkLeaBfqIwU+XCxlDpYDfyMQRkM0AkeQ== - "@types/color-name@^1.1.1": version "1.1.1" resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0" @@ -396,20 +333,15 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-14.6.1.tgz#fdf6f6c6c73d3d8eee9c98a9a0485bc524b048d7" integrity sha512-HnYlg/BRF8uC1FyKRFZwRaCPTPYKa+6I8QiUZFLredaGOou481cgFS4wKRFyKvQtX8xudqkSdBczJHIYSQYKrQ== -"@types/node@^12.12.47": - version "12.12.54" - resolved "https://registry.yarnpkg.com/@types/node/-/node-12.12.54.tgz#a4b58d8df3a4677b6c08bfbc94b7ad7a7a5f82d1" - integrity sha512-ge4xZ3vSBornVYlDnk7yZ0gK6ChHf/CHB7Gl1I0Jhah8DDnEQqBzgohYG4FX4p81TNirSETOiSyn+y1r9/IR6w== - -"@types/node@^13.7.0": - version "13.13.15" - resolved "https://registry.yarnpkg.com/@types/node/-/node-13.13.15.tgz#fe1cc3aa465a3ea6858b793fd380b66c39919766" - integrity sha512-kwbcs0jySLxzLsa2nWUAGOd/s21WU1jebrEdtzhsj1D4Yps1EOuyI1Qcu+FD56dL7NRNIJtDDjcqIG22NwkgLw== +"@types/node@>=12.12.47", "@types/node@>=13.7.0": + version "17.0.42" + resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.42.tgz#d7e8f22700efc94d125103075c074396b5f41f9b" + integrity sha512-Q5BPGyGKcvQgAMbsr7qEGN/kIPN6zZecYYABeTDBizOsau+2NMdSVTar9UQw21A2+JyA2KRNDYaYrPB0Rpk2oQ== -"@types/tunnel@^0.0.1": - version "0.0.1" - resolved "https://registry.yarnpkg.com/@types/tunnel/-/tunnel-0.0.1.tgz#0d72774768b73df26f25df9184273a42da72b19c" - integrity sha512-AOqu6bQu5MSWwYvehMXLukFHnupHrpZ8nvgae5Ggie9UwzDR1CCwoXgSSWNZJuyOlCdfdsWMA5F2LlmvyoTv8A== +"@types/tunnel@^0.0.3": + version "0.0.3" + resolved "https://registry.yarnpkg.com/@types/tunnel/-/tunnel-0.0.3.tgz#f109e730b072b3136347561fc558c9358bb8c6e9" + integrity sha512-sOUTGn6h1SfQ+gbgqC364jLFBw2lnFqkgF3q0WovEHRLMrVD1sd5aufqi/aJObLekJO+Aq5z646U4Oxy6shXMA== dependencies: "@types/node" "*" @@ -425,13 +357,13 @@ abort-controller@^3.0.0: dependencies: event-target-shim "^5.0.0" -accepts@~1.3.7: - version "1.3.7" - resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd" - integrity sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA== +accepts@~1.3.8: + version "1.3.8" + resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e" + integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw== dependencies: - mime-types "~2.1.24" - negotiator "0.6.2" + mime-types "~2.1.34" + negotiator "0.6.3" agent-base@6: version "6.0.1" @@ -440,27 +372,15 @@ agent-base@6: dependencies: debug "4" -ajv@^6.12.3: - version "6.12.4" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.4.tgz#0614facc4522127fa713445c6bfd3ebd376e2234" - integrity sha512-eienB2c9qVQs2KWexhkrdMLVDoIQCz5KSeLxwg9Lzk4DOfBtIK9PQwwufcsn1jjGuf9WZmqPMbGxOzfcuphJCQ== - dependencies: - fast-deep-equal "^3.1.1" - fast-json-stable-stringify "^2.0.0" - json-schema-traverse "^0.4.1" - uri-js "^4.2.2" - -amqplib@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/amqplib/-/amqplib-0.6.0.tgz#87857c7c95d56d22438ced4cf1f7e5f0dc43b309" - integrity sha512-zXCh4jQ77TBZe1YtvZ1n7sUxnTjnNagpy8MVi2yc1ive239pS3iLwm4e4d5o4XZGx1BdTKQ/U0ZmaDU3c8MxYQ== +amqplib@^0.10.0: + version "0.10.0" + resolved "https://registry.yarnpkg.com/amqplib/-/amqplib-0.10.0.tgz#766d696f8ceae097ee9eb73e6796999e5d40a1db" + integrity sha512-UueEnRGY6upiSvGsSYM22Woa1SeSukqYtqgYW4Gj8gHvbf5BRhhYRqf3kQ8aSUYYffTOZi6SeOVW2eOXt0hpPA== dependencies: bitsyntax "~0.1.0" - bluebird "^3.5.2" buffer-more-ints "~1.0.0" readable-stream "1.x >=1.1.9" - safe-buffer "~5.1.2" - url-parse "~1.4.3" + url-parse "~1.5.10" ansi-align@^3.0.0: version "3.0.0" @@ -469,25 +389,10 @@ ansi-align@^3.0.0: dependencies: string-width "^3.0.0" -ansi-regex@^2.0.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" - integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8= - -ansi-regex@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" - integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg= - -ansi-regex@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997" - integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg== - -ansi-regex@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75" - integrity sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg== +ansi-regex@^2.0.0, ansi-regex@^3.0.0, ansi-regex@^4.1.0, ansi-regex@^5.0.0, ansi-regex@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" + integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== ansi-styles@^4.0.0, ansi-styles@^4.1.0: version "4.2.1" @@ -497,10 +402,10 @@ ansi-styles@^4.0.0, ansi-styles@^4.1.0: "@types/color-name" "^1.1.1" color-convert "^2.0.1" -anymatch@~3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.1.tgz#c55ecf02185e2469259399310c173ce31233b142" - integrity sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg== +anymatch@~3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716" + integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg== dependencies: normalize-path "^3.0.0" picomatch "^2.0.4" @@ -518,12 +423,10 @@ are-we-there-yet@~1.1.2: delegates "^1.0.0" readable-stream "^2.0.6" -argparse@^1.0.7: - version "1.0.10" - resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" - integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== - dependencies: - sprintf-js "~1.0.2" +argparse@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" + integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== array-flatten@1.1.1: version "1.1.1" @@ -540,27 +443,10 @@ arrify@^2.0.0: resolved "https://registry.yarnpkg.com/arrify/-/arrify-2.0.1.tgz#c9655e9331e0abcd588d2a7cad7e9956f66701fa" integrity sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug== -asn1@~0.2.3: - version "0.2.4" - resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136" - integrity sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg== - dependencies: - safer-buffer "~2.1.0" - -assert-plus@1.0.0, assert-plus@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" - integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU= - -async-lock@^1.1.3: - version "1.2.4" - resolved "https://registry.yarnpkg.com/async-lock/-/async-lock-1.2.4.tgz#80d0d612383045dd0c30eb5aad08510c1397cb91" - integrity sha512-UBQJC2pbeyGutIfYmErGc9RaJYnpZ1FHaxuKwb0ahvGiiCkPUf3p67Io+YLPmmv3RHY+mF6JEtNW8FlHsraAaA== - -async@^3.1.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/async/-/async-3.2.0.tgz#b3a2685c5ebb641d3de02d161002c60fc9f85720" - integrity sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw== +async@^3.2.3: + version "3.2.4" + resolved "https://registry.yarnpkg.com/async/-/async-3.2.4.tgz#2d22e00f8cddeb5fde5dd33522b56d1cf569a81c" + integrity sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ== asynckit@^0.4.0: version "0.4.0" @@ -572,57 +458,26 @@ at-least-node@^1.0.0: resolved "https://registry.yarnpkg.com/at-least-node/-/at-least-node-1.0.0.tgz#602cd4b46e844ad4effc92a8011a3c46e0238dc2" integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg== -aws-sdk@^2.741.0: - version "2.741.0" - resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.741.0.tgz#b832d838e5f1ef9cdb2b4901ade4555b1ca4c085" - integrity sha512-bpk5VvlBSvKu3Lg30AxX+PHk+0TD69K3tYe6D6VeEFl/3XzuZ5RKTGCIl96isSMyYc5bBMhLS9pC9glrxT7OTw== +available-typed-arrays@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz#92f95616501069d07d10edb2fc37d3e1c65123b7" + integrity sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw== + +aws-sdk@^2.1152.0: + version "2.1152.0" + resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.1152.0.tgz#73e4fb81b3a9c289234b5d6848bcdb854f169bdf" + integrity sha512-Lqwk0bDhm3vzpYb3AAM9VgGHeDpbB8+o7UJnP9R+CO23kJfi/XRpKihAcbyKDD/AUQ+O1LJaUVpvaJYLS9Am7w== dependencies: buffer "4.9.2" events "1.1.1" ieee754 "1.1.13" - jmespath "0.15.0" + jmespath "0.16.0" querystring "0.2.0" sax "1.2.1" url "0.10.3" - uuid "3.3.2" + uuid "8.0.0" xml2js "0.4.19" -aws-sign2@~0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" - integrity sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg= - -aws4@^1.8.0: - version "1.10.1" - resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.10.1.tgz#e1e82e4f3e999e2cfd61b161280d16a111f86428" - integrity sha512-zg7Hz2k5lI8kb7U32998pRRFin7zJlkfezGJjUc2heaD4Pw2wObakCDVzkKztTm/Ln7eiVvYsjqak0Ed4LkMDA== - -azure-common@^0.9.22: - version "0.9.25" - resolved "https://registry.yarnpkg.com/azure-common/-/azure-common-0.9.25.tgz#fc7cfb08c65398b3c90f9b9bc14a99e204319b9a" - integrity sha512-L7YO3DUQ0iwiaUyD9Wy6B66Y6HmCzMb9vxUqKklgzU+gFRRBKIMSVR4oZS6IkQfFCSm9eKwHuH2p3UdDPszd7g== - dependencies: - dateformat "1.0.2-1.2.3" - duplexer "~0.1.1" - envconf "~0.0.4" - request "^2.81.0" - through "~2.3.4" - tunnel "~0.0.2" - underscore "1.4.x" - validator "^9.4.1" - xml2js "^0.4.19" - xmlbuilder "15.1.1" - -azure-sb@^0.11.1: - version "0.11.1" - resolved "https://registry.yarnpkg.com/azure-sb/-/azure-sb-0.11.1.tgz#15cba11c1a3b4aca68c344460b99bceec14be11a" - integrity sha512-ZYgPeSDMD99i/Em+6wT78zvBkJ/dbh2ypb4DbqQ1Flaif5vWJFzC/iKxxcq/vq+THWoO3+UbqWa0JNXnW3zAvw== - dependencies: - azure-common "^0.9.22" - mpns "2.1.3" - underscore "^1.8.3" - wns "~0.5.3" - balanced-match@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" @@ -638,13 +493,6 @@ base64-js@^1.3.1: resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== -bcrypt-pbkdf@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" - integrity sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4= - dependencies: - tweetnacl "^0.14.3" - bignumber.js@^9.0.0: version "9.0.0" resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.0.0.tgz#805880f84a329b5eac6e7cb6f8274b6d82bdf075" @@ -673,40 +521,37 @@ bl@^4.0.3: inherits "^2.0.4" readable-stream "^3.4.0" -bluebird@^3.5.2: - version "3.7.2" - resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" - integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== - -body-parser@1.19.0: - version "1.19.0" - resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.19.0.tgz#96b2709e57c9c4e09a6fd66a8fd979844f69f08a" - integrity sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw== +body-parser@1.20.0: + version "1.20.0" + resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.0.tgz#3de69bd89011c11573d7bfee6a64f11b6bd27cc5" + integrity sha512-DfJ+q6EPcGKZD1QWUjSpqp+Q7bDQTsQIF4zfUAtZ6qk+H/3/QRhg9CEp39ss+/T2vw0+HaidC0ecJj/DRLIaKg== dependencies: - bytes "3.1.0" + bytes "3.1.2" content-type "~1.0.4" debug "2.6.9" - depd "~1.1.2" - http-errors "1.7.2" + depd "2.0.0" + destroy "1.2.0" + http-errors "2.0.0" iconv-lite "0.4.24" - on-finished "~2.3.0" - qs "6.7.0" - raw-body "2.4.0" - type-is "~1.6.17" + on-finished "2.4.1" + qs "6.10.3" + raw-body "2.5.1" + type-is "~1.6.18" + unpipe "1.0.0" -boxen@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/boxen/-/boxen-4.2.0.tgz#e411b62357d6d6d36587c8ac3d5d974daa070e64" - integrity sha512-eB4uT9RGzg2odpER62bBwSLvUeGC+WbRjjyyFhGsKnc8wp/m0+hQsMUvUe3H2V0D5vw0nBdO1hCJoZo5mKeuIQ== +boxen@^5.0.0: + version "5.1.2" + resolved "https://registry.yarnpkg.com/boxen/-/boxen-5.1.2.tgz#788cb686fc83c1f486dfa8a40c68fc2b831d2b50" + integrity sha512-9gYgQKXx+1nP8mP7CzFyaUARhg7D3n1dF/FnErWmu9l6JvGpNUN278h0aSb+QjoiKSWG+iZ3uHrcqk0qrY9RQQ== dependencies: ansi-align "^3.0.0" - camelcase "^5.3.1" - chalk "^3.0.0" - cli-boxes "^2.2.0" - string-width "^4.1.0" - term-size "^2.1.0" - type-fest "^0.8.1" + camelcase "^6.2.0" + chalk "^4.1.0" + cli-boxes "^2.2.1" + string-width "^4.2.2" + type-fest "^0.20.2" widest-line "^3.1.0" + wrap-ansi "^7.0.0" brace-expansion@^1.1.7: version "1.1.11" @@ -716,7 +561,7 @@ brace-expansion@^1.1.7: balanced-match "^1.0.0" concat-map "0.0.1" -braces@^3.0.1, braces@~3.0.2: +braces@^3.0.2, braces@~3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== @@ -742,14 +587,6 @@ buffer@4.9.2: ieee754 "^1.1.4" isarray "^1.0.0" -buffer@^5.2.1: - version "5.6.0" - resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.6.0.tgz#a31749dc7d81d84db08abf937b6b8c4033f62786" - integrity sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw== - dependencies: - base64-js "^1.0.2" - ieee754 "^1.1.4" - buffer@^5.5.0: version "5.7.1" resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" @@ -758,10 +595,18 @@ buffer@^5.5.0: base64-js "^1.3.1" ieee754 "^1.1.13" -bytes@3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6" - integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg== +buffer@^6.0.0: + version "6.0.3" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" + integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.2.1" + +bytes@3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" + integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== cacheable-request@^6.0.0: version "6.1.0" @@ -776,25 +621,20 @@ cacheable-request@^6.0.0: normalize-url "^4.1.0" responselike "^1.0.2" -camelcase@^5.0.0, camelcase@^5.3.1: - version "5.3.1" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" - integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== - -caseless@~0.12.0: - version "0.12.0" - resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" - integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= - -chalk@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-3.0.0.tgz#3f73c2bf526591f574cc492c51e2456349f844e4" - integrity sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg== +call-bind@^1.0.0, call-bind@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" + integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA== dependencies: - ansi-styles "^4.1.0" - supports-color "^7.1.0" + function-bind "^1.1.1" + get-intrinsic "^1.0.2" + +camelcase@^6.2.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" + integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== -chalk@^4.1.0: +chalk@^4.1.0, chalk@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== @@ -802,20 +642,20 @@ chalk@^4.1.0: ansi-styles "^4.1.0" supports-color "^7.1.0" -chokidar@^3.2.2: - version "3.4.2" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.4.2.tgz#38dc8e658dec3809741eb3ef7bb0a47fe424232d" - integrity sha512-IZHaDeBeI+sZJRX7lGcXsdzgvZqKv6sECqsbErJA4mHWfpRrD8B97kSFN4cQz6nGBGiuFia1MKR4d6c1o8Cv7A== +chokidar@^3.5.2: + version "3.5.3" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" + integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== dependencies: - anymatch "~3.1.1" + anymatch "~3.1.2" braces "~3.0.2" - glob-parent "~5.1.0" + glob-parent "~5.1.2" is-binary-path "~2.1.0" is-glob "~4.0.1" normalize-path "~3.0.0" - readdirp "~3.4.0" + readdirp "~3.6.0" optionalDependencies: - fsevents "~2.1.2" + fsevents "~2.3.2" chownr@^1.1.1: version "1.1.4" @@ -827,19 +667,10 @@ ci-info@^2.0.0: resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46" integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ== -cli-boxes@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-2.2.0.tgz#538ecae8f9c6ca508e3c3c95b453fe93cb4c168d" - integrity sha512-gpaBrMAizVEANOpfZp/EEUixTXDyGt7DFzdK5hU+UbWt/J0lB0w20ncZj59Z9a93xHb9u12zF5BS6i9RKbtg4w== - -cliui@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/cliui/-/cliui-6.0.0.tgz#511d702c0c4e41ca156d7d0e96021f23e13225b1" - integrity sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ== - dependencies: - string-width "^4.2.0" - strip-ansi "^6.0.0" - wrap-ansi "^6.2.0" +cli-boxes@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-2.2.1.tgz#ddd5035d25094fce220e9cab40a45840a440318f" + integrity sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw== cliui@^7.0.2: version "7.0.4" @@ -886,10 +717,10 @@ color-name@^1.0.0, color-name@~1.1.4: resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== -color-string@^1.5.2: - version "1.5.3" - resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.5.3.tgz#c9bbc5f01b58b5492f3d6857459cb6590ce204cc" - integrity sha512-dC2C5qeWoYkxki5UAXapdjqO672AM4vZuPGRQfO8b5HKuKGBbKWpITyDYN7TOFKvRW7kOgAn3746clDBMDJyQw== +color-string@^1.5.2, color-string@^1.5.5: + version "1.9.1" + resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.9.1.tgz#4467f9146f036f855b764dfb5bf8582bf342c7a4" + integrity sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg== dependencies: color-name "^1.0.0" simple-swizzle "^0.2.2" @@ -902,11 +733,6 @@ color@3.0.x: color-convert "^1.9.1" color-string "^1.5.2" -colors@^1.2.1: - version "1.4.0" - resolved "https://registry.yarnpkg.com/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78" - integrity sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA== - colorspace@1.1.x: version "1.1.2" resolved "https://registry.yarnpkg.com/colorspace/-/colorspace-1.1.2.tgz#e0128950d082b86a2168580796a0aa5d6c68d8c5" @@ -915,7 +741,7 @@ colorspace@1.1.x: color "3.0.x" text-hex "1.0.x" -combined-stream@^1.0.6, combined-stream@^1.0.8, combined-stream@~1.0.6: +combined-stream@^1.0.8: version "1.0.8" resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== @@ -927,10 +753,10 @@ concat-map@0.0.1: resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= -config@^3.3.1: - version "3.3.1" - resolved "https://registry.yarnpkg.com/config/-/config-3.3.1.tgz#b6a70e2908a43b98ed20be7e367edf0cc8ed5a19" - integrity sha512-+2/KaaaAzdwUBE3jgZON11L1ggLLhpf2FsGrfqYFHZW22ySGv/HqYIXrBwKKvn+XZh1UBUjHwAcrfsSkSygT+Q== +config@^3.3.7: + version "3.3.7" + resolved "https://registry.yarnpkg.com/config/-/config-3.3.7.tgz#4310410dc2bf4e0effdca21a12a4035860a24ee4" + integrity sha512-mX/n7GKDYZMqvvkY6e6oBY49W8wxdmQt+ho/5lhwFDXqQW9gI+Ahp8EKp8VAbISPnmf2+Bv5uZK7lKXZ6pf1aA== dependencies: json5 "^2.1.1" @@ -951,12 +777,12 @@ console-control-strings@^1.0.0, console-control-strings@~1.1.0: resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4= -content-disposition@0.5.3: - version "0.5.3" - resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.3.tgz#e130caf7e7279087c5616c2007d0485698984fbd" - integrity sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g== +content-disposition@0.5.4: + version "0.5.4" + resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe" + integrity sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ== dependencies: - safe-buffer "5.1.2" + safe-buffer "5.2.1" content-type@~1.0.4: version "1.0.4" @@ -968,12 +794,12 @@ cookie-signature@1.0.6: resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" integrity sha1-4wOogrNCzD7oylE6eZmXNNqzriw= -cookie@0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.0.tgz#beb437e7022b3b6d49019d088665303ebe9c14ba" - integrity sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg== +cookie@0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.5.0.tgz#d1f5d71adec6558c58f389987c366aa47e994f8b" + integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw== -core-util-is@1.0.2, core-util-is@~1.0.0: +core-util-is@~1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= @@ -983,26 +809,14 @@ crypto-random-string@^2.0.0: resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-2.0.0.tgz#ef2a7a966ec11083388369baa02ebead229b30d5" integrity sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA== -dashdash@^1.12.0: - version "1.14.1" - resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" - integrity sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA= - dependencies: - assert-plus "^1.0.0" - -dateformat@1.0.2-1.2.3: - version "1.0.2-1.2.3" - resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-1.0.2-1.2.3.tgz#b0220c02de98617433b72851cf47de3df2cdbee9" - integrity sha1-sCIMAt6YYXQztyhRz0fePfLNvuk= - -"debug@0.8.0 - 3.5.0", debug@^3.1.0, debug@^3.2.6: +"debug@0.8.0 - 3.5.0", debug@^3.1.0: version "3.2.6" resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== dependencies: ms "^2.1.1" -debug@2.6.9, debug@^2.2.0, debug@~2.6.9: +debug@2.6.9, debug@~2.6.9: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== @@ -1016,10 +830,12 @@ debug@4, debug@^4.1.1: dependencies: ms "^2.1.1" -decamelize@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" - integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= +debug@^3.2.7: + version "3.2.7" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" + integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== + dependencies: + ms "^2.1.1" decompress-response@^3.3.0: version "3.3.0" @@ -1050,6 +866,14 @@ defer-to-connect@^1.0.1: resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-1.1.3.tgz#331ae050c08dcf789f8c83a7b81f0ed94f4ac591" integrity sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ== +define-properties@^1.1.3, define-properties@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.4.tgz#0b14d7bd7fbeb2f3572c3a7eda80ea5d57fb05b1" + integrity sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA== + dependencies: + has-property-descriptors "^1.0.0" + object-keys "^1.1.1" + delayed-stream@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" @@ -1060,15 +884,15 @@ delegates@^1.0.0: resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" integrity sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o= -depd@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" - integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= +depd@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" + integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== -destroy@~1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" - integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA= +destroy@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015" + integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg== detect-libc@^1.0.3: version "1.0.3" @@ -1094,29 +918,16 @@ duplexer3@^0.1.4: resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2" integrity sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI= -duplexer@~0.1.1: - version "0.1.2" - resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.2.tgz#3abe43aef3835f8ae077d136ddce0f276b0400e6" - integrity sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg== - -duplexify@^3.6.0: - version "3.7.1" - resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-3.7.1.tgz#2a4df5317f6ccfd91f86d6fd25d8d8a103b88309" - integrity sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g== +duplexify@^4.0.0: + version "4.1.2" + resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-4.1.2.tgz#18b4f8d28289132fa0b9573c898d9f903f81c7b0" + integrity sha512-fz3OjcNCHmRP12MJoZMPglx8m4rrFP8rovnk4vT8Fs+aonZoCwGg10dSsQsfP/E62eZcPTMSMP6686fu9Qlqtw== dependencies: - end-of-stream "^1.0.0" - inherits "^2.0.1" - readable-stream "^2.0.0" + end-of-stream "^1.4.1" + inherits "^2.0.3" + readable-stream "^3.1.1" stream-shift "^1.0.0" -ecc-jsbn@~0.1.1: - version "0.1.2" - resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" - integrity sha1-OoOpBOVDUyh4dMVkt1SThoSamMk= - dependencies: - jsbn "~0.1.0" - safer-buffer "^2.1.0" - ecdsa-sig-formatter@1.0.11, ecdsa-sig-formatter@^1.0.11: version "1.0.11" resolved "https://registry.yarnpkg.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz#ae0f0fa2d85045ef14a817daa3ce9acd0489e5bf" @@ -1149,17 +960,50 @@ encodeurl@~1.0.2: resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k= -end-of-stream@^1.0.0, end-of-stream@^1.1.0, end-of-stream@^1.4.1: +end-of-stream@^1.1.0, end-of-stream@^1.4.1: version "1.4.4" resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== dependencies: once "^1.4.0" -envconf@~0.0.4: - version "0.0.4" - resolved "https://registry.yarnpkg.com/envconf/-/envconf-0.0.4.tgz#85675afba237c43f98de2d46adc0e532a4dcf48b" - integrity sha1-hWda+6I3xD+Y3i1GrcDlMqTc9Is= +es-abstract@^1.19.0, es-abstract@^1.19.5, es-abstract@^1.20.0: + version "1.20.1" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.20.1.tgz#027292cd6ef44bd12b1913b828116f54787d1814" + integrity sha512-WEm2oBhfoI2sImeM4OF2zE2V3BYdSF+KnSi9Sidz51fQHd7+JuF8Xgcj9/0o+OWeIeIS/MiuNnlruQrJf16GQA== + dependencies: + call-bind "^1.0.2" + es-to-primitive "^1.2.1" + function-bind "^1.1.1" + function.prototype.name "^1.1.5" + get-intrinsic "^1.1.1" + get-symbol-description "^1.0.0" + has "^1.0.3" + has-property-descriptors "^1.0.0" + has-symbols "^1.0.3" + internal-slot "^1.0.3" + is-callable "^1.2.4" + is-negative-zero "^2.0.2" + is-regex "^1.1.4" + is-shared-array-buffer "^1.0.2" + is-string "^1.0.7" + is-weakref "^1.0.2" + object-inspect "^1.12.0" + object-keys "^1.1.1" + object.assign "^4.1.2" + regexp.prototype.flags "^1.4.3" + string.prototype.trimend "^1.0.5" + string.prototype.trimstart "^1.0.5" + unbox-primitive "^1.0.2" + +es-to-primitive@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" + integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA== + dependencies: + is-callable "^1.1.4" + is-date-object "^1.0.1" + is-symbol "^1.0.2" escalade@^3.1.1: version "3.1.1" @@ -1188,7 +1032,7 @@ escodegen@^2.0.0: optionalDependencies: source-map "~0.6.1" -esprima@^4.0.0, esprima@^4.0.1: +esprima@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== @@ -1228,90 +1072,65 @@ expand-template@^2.0.3: resolved "https://registry.yarnpkg.com/expand-template/-/expand-template-2.0.3.tgz#6e14b3fcee0f3a6340ecb57d2e8918692052a47c" integrity sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg== -express@^4.17.1: - version "4.17.1" - resolved "https://registry.yarnpkg.com/express/-/express-4.17.1.tgz#4491fc38605cf51f8629d39c2b5d026f98a4c134" - integrity sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g== +express@^4.18.1: + version "4.18.1" + resolved "https://registry.yarnpkg.com/express/-/express-4.18.1.tgz#7797de8b9c72c857b9cd0e14a5eea80666267caf" + integrity sha512-zZBcOX9TfehHQhtupq57OF8lFZ3UZi08Y97dwFCkD8p9d/d2Y3M+ykKcwaMDEL+4qyUolgBDX6AblpR3fL212Q== dependencies: - accepts "~1.3.7" + accepts "~1.3.8" array-flatten "1.1.1" - body-parser "1.19.0" - content-disposition "0.5.3" + body-parser "1.20.0" + content-disposition "0.5.4" content-type "~1.0.4" - cookie "0.4.0" + cookie "0.5.0" cookie-signature "1.0.6" debug "2.6.9" - depd "~1.1.2" + depd "2.0.0" encodeurl "~1.0.2" escape-html "~1.0.3" etag "~1.8.1" - finalhandler "~1.1.2" + finalhandler "1.2.0" fresh "0.5.2" + http-errors "2.0.0" merge-descriptors "1.0.1" methods "~1.1.2" - on-finished "~2.3.0" + on-finished "2.4.1" parseurl "~1.3.3" path-to-regexp "0.1.7" - proxy-addr "~2.0.5" - qs "6.7.0" + proxy-addr "~2.0.7" + qs "6.10.3" range-parser "~1.2.1" - safe-buffer "5.1.2" - send "0.17.1" - serve-static "1.14.1" - setprototypeof "1.1.1" - statuses "~1.5.0" + safe-buffer "5.2.1" + send "0.18.0" + serve-static "1.15.0" + setprototypeof "1.2.0" + statuses "2.0.1" type-is "~1.6.18" utils-merge "1.0.1" vary "~1.1.2" -extend@^3.0.2, extend@~3.0.2: +extend@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== -extsprintf@1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" - integrity sha1-lpGEQOMEGnpBT4xS48V06zw+HgU= - -extsprintf@^1.2.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f" - integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8= - -fast-deep-equal@^3.1.1: - version "3.1.3" - resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" - integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== - -fast-glob@^3.1.1: - version "3.2.4" - resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.4.tgz#d20aefbf99579383e7f3cc66529158c9b98554d3" - integrity sha512-kr/Oo6PX51265qeuCYsyGypiO5uJFgBS0jksyG7FUeCyQzNwYnzrNIMR1NXfkZXsMYXYLRAHgISHBz8gQcxKHQ== +fast-glob@^3.2.9: + version "3.2.11" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.11.tgz#a1172ad95ceb8a16e20caa5c5e56480e5129c1d9" + integrity sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew== dependencies: "@nodelib/fs.stat" "^2.0.2" "@nodelib/fs.walk" "^1.2.3" - glob-parent "^5.1.0" + glob-parent "^5.1.2" merge2 "^1.3.0" - micromatch "^4.0.2" - picomatch "^2.2.1" - -fast-json-stable-stringify@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" - integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== + micromatch "^4.0.4" fast-levenshtein@~2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= -fast-safe-stringify@^2.0.4: - version "2.0.7" - resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz#124aa885899261f68aedb42a7c080de9da608743" - integrity sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA== - -fast-text-encoding@^1.0.0: +fast-text-encoding@^1.0.0, fast-text-encoding@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/fast-text-encoding/-/fast-text-encoding-1.0.3.tgz#ec02ac8e01ab8a319af182dae2681213cfe9ce53" integrity sha512-dtm4QZH9nZtcDt8qJiOH9fcQd1NAgi+K1O2DbE6GG1PPCK/BWfOH3idCTRQ4ImXRUOyopDEgDEnVEE7Y/2Wrig== @@ -1328,12 +1147,12 @@ fecha@^4.2.0: resolved "https://registry.yarnpkg.com/fecha/-/fecha-4.2.0.tgz#3ffb6395453e3f3efff850404f0a59b6747f5f41" integrity sha512-aN3pcx/DSmtyoovUudctc8+6Hl4T+hI9GBBHLjA76jdZl7+b1sgh5g4k+u/GL3dTy1/pnYzKp69FpJ0OicE3Wg== -file-stream-rotator@^0.5.7: - version "0.5.7" - resolved "https://registry.yarnpkg.com/file-stream-rotator/-/file-stream-rotator-0.5.7.tgz#868a2e5966f7640a17dd86eda0e4467c089f6286" - integrity sha512-VYb3HZ/GiAGUCrfeakO8Mp54YGswNUHvL7P09WQcXAJNSj3iQ5QraYSp3cIn1MUyw6uzfgN/EFOarCNa4JvUHQ== +file-stream-rotator@^0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/file-stream-rotator/-/file-stream-rotator-0.6.1.tgz#007019e735b262bb6c6f0197e58e5c87cb96cec3" + integrity sha512-u+dBid4PvZw17PmDeRcNOtCP9CCK/9lRN2w+r1xIS7yOL9JFrIBKTvrYsxT4P0pGtThYTn++QS5ChHaUov3+zQ== dependencies: - moment "^2.11.2" + moment "^2.29.1" fill-range@^7.0.1: version "7.0.1" @@ -1342,36 +1161,30 @@ fill-range@^7.0.1: dependencies: to-regex-range "^5.0.1" -finalhandler@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d" - integrity sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA== +finalhandler@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.2.0.tgz#7d23fe5731b207b4640e4fcd00aec1f9207a7b32" + integrity sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg== dependencies: debug "2.6.9" encodeurl "~1.0.2" escape-html "~1.0.3" - on-finished "~2.3.0" + on-finished "2.4.1" parseurl "~1.3.3" - statuses "~1.5.0" + statuses "2.0.1" unpipe "~1.0.0" -find-up@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" - integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== - dependencies: - locate-path "^5.0.0" - path-exists "^4.0.0" - fn.name@1.x.x: version "1.1.0" resolved "https://registry.yarnpkg.com/fn.name/-/fn.name-1.1.0.tgz#26cad8017967aea8731bc42961d04a3d5988accc" integrity sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw== -forever-agent@~0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" - integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= +for-each@^0.3.3: + version "0.3.3" + resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e" + integrity sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw== + dependencies: + is-callable "^1.1.3" form-data@^3.0.0: version "3.0.0" @@ -1382,13 +1195,13 @@ form-data@^3.0.0: combined-stream "^1.0.8" mime-types "^2.1.12" -form-data@~2.3.2: - version "2.3.3" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" - integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ== +form-data@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" + integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== dependencies: asynckit "^0.4.0" - combined-stream "^1.0.6" + combined-stream "^1.0.8" mime-types "^2.1.12" forwarded@0.2.0: @@ -1414,10 +1227,10 @@ fs-constants@^1.0.0: resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== -fs-extra@^10.0.0: - version "10.0.0" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-10.0.0.tgz#9ff61b655dde53fb34a82df84bb214ce802e17c1" - integrity sha512-C5owb14u9eJwizKGdchcDUQeFtlSHHthBk8pbX9Vc1PFZrLombudjDnNns88aYslCyF6IY5SUw3Roz6xShcEIQ== +fs-extra@^10.1.0: + version "10.1.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-10.1.0.tgz#02873cfbc4084dde127eaa5f9905eef2325d1abf" + integrity sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ== dependencies: graceful-fs "^4.2.0" jsonfile "^6.0.1" @@ -1433,16 +1246,31 @@ fs-extra@^9.1.0: jsonfile "^6.0.1" universalify "^2.0.0" -fsevents@~2.1.2: - version "2.1.3" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.1.3.tgz#fb738703ae8d2f9fe900c33836ddebee8b97f23e" - integrity sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ== +fsevents@~2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" + integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== function-bind@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== +function.prototype.name@^1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.5.tgz#cce0505fe1ffb80503e6f9e46cc64e46a12a9621" + integrity sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + es-abstract "^1.19.0" + functions-have-names "^1.2.2" + +functions-have-names@^1.2.2: + version "1.2.3" + resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" + integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== + gauge@~2.7.3: version "2.7.4" resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7" @@ -1457,49 +1285,50 @@ gauge@~2.7.3: strip-ansi "^3.0.1" wide-align "^1.1.0" -gaxios@^2.1.0: - version "2.3.4" - resolved "https://registry.yarnpkg.com/gaxios/-/gaxios-2.3.4.tgz#eea99353f341c270c5f3c29fc46b8ead56f0a173" - integrity sha512-US8UMj8C5pRnao3Zykc4AAVr+cffoNKRTg9Rsf2GiuZCW69vgJj38VK2PzlPuQU73FZ/nTk9/Av6/JGcE1N9vA== +gaxios@^4.0.0: + version "4.3.3" + resolved "https://registry.yarnpkg.com/gaxios/-/gaxios-4.3.3.tgz#d44bdefe52d34b6435cc41214fdb160b64abfc22" + integrity sha512-gSaYYIO1Y3wUtdfHmjDUZ8LWaxJQpiavzbF5Kq53akSzvmVg0RfyOcFDbO1KJ/KCGRFz2qG+lS81F0nkr7cRJA== dependencies: abort-controller "^3.0.0" extend "^3.0.2" https-proxy-agent "^5.0.0" is-stream "^2.0.0" - node-fetch "^2.3.0" + node-fetch "^2.6.7" -gaxios@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/gaxios/-/gaxios-3.1.0.tgz#95f65f5a335f61aff602fe124cfdba8524f765fa" - integrity sha512-DDTn3KXVJJigtz+g0J3vhcfbDbKtAroSTxauWsdnP57sM5KZ3d2c/3D9RKFJ86s43hfw6WULg6TXYw/AYiBlpA== +gaxios@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/gaxios/-/gaxios-5.0.0.tgz#df11e5d0a45831dd39eb5fbbba0d6a6b09815e70" + integrity sha512-VD/yc5ln6XU8Ch1hyYY6kRMBE0Yc2np3fPyeJeYHhrPs1i8rgnsApPMWyrugkl7LLoSqpOJVBWlQIa87OAvt8Q== dependencies: abort-controller "^3.0.0" extend "^3.0.2" https-proxy-agent "^5.0.0" is-stream "^2.0.0" - node-fetch "^2.3.0" + node-fetch "^2.6.7" -gcp-metadata@^3.5.0: - version "3.5.0" - resolved "https://registry.yarnpkg.com/gcp-metadata/-/gcp-metadata-3.5.0.tgz#6d28343f65a6bbf8449886a0c0e4a71c77577055" - integrity sha512-ZQf+DLZ5aKcRpLzYUyBS3yo3N0JSa82lNDO8rj3nMSlovLcz2riKFBsYgDzeXcv75oo5eqB2lx+B14UvPoCRnA== - dependencies: - gaxios "^2.1.0" - json-bigint "^0.3.0" - -gcp-metadata@^4.1.0: - version "4.1.4" - resolved "https://registry.yarnpkg.com/gcp-metadata/-/gcp-metadata-4.1.4.tgz#3adadb9158c716c325849ee893741721a3c09e7e" - integrity sha512-5J/GIH0yWt/56R3dNaNWPGQ/zXsZOddYECfJaqxFWgrZ9HC2Kvc5vl9upOgUUHKzURjAVf2N+f6tEJiojqXUuA== +gcp-metadata@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/gcp-metadata/-/gcp-metadata-5.0.0.tgz#a00f999f60a4461401e7c515f8a3267cfb401ee7" + integrity sha512-gfwuX3yA3nNsHSWUL4KG90UulNiq922Ukj3wLTrcnX33BB7PwB1o0ubR8KVvXu9nJH+P5w1j2SQSNNqto+H0DA== dependencies: - gaxios "^3.0.0" + gaxios "^5.0.0" json-bigint "^1.0.0" -get-caller-file@^2.0.1, get-caller-file@^2.0.5: +get-caller-file@^2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== +get-intrinsic@^1.0.2, get-intrinsic@^1.1.0, get-intrinsic@^1.1.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.2.tgz#336975123e05ad0b7ba41f152ee4aadbea6cf598" + integrity sha512-Jfm3OyCxHh9DJyc28qGk+JmfkpO41A4XkneDSujN9MDXrm4oDKdHvndhZ2dN94+ERNfkYJWDclW6k2L/ZGHjXA== + dependencies: + function-bind "^1.1.1" + has "^1.0.3" + has-symbols "^1.0.3" + get-stream@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" @@ -1514,85 +1343,85 @@ get-stream@^5.1.0: dependencies: pump "^3.0.0" -getpass@^0.1.1: - version "0.1.7" - resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" - integrity sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo= +get-symbol-description@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/get-symbol-description/-/get-symbol-description-1.0.0.tgz#7fdb81c900101fbd564dd5f1a30af5aadc1e58d6" + integrity sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw== dependencies: - assert-plus "^1.0.0" + call-bind "^1.0.2" + get-intrinsic "^1.1.1" github-from-package@0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/github-from-package/-/github-from-package-0.0.0.tgz#97fb5d96bfde8973313f20e8288ef9a167fa64ce" integrity sha1-l/tdlr/eiXMxPyDoKI75oWf6ZM4= -glob-parent@^5.1.0, glob-parent@~5.1.0: - version "5.1.1" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.1.tgz#b6c1ef417c4e5663ea498f1c45afac6916bbc229" - integrity sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ== +glob-parent@^5.1.2, glob-parent@~5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== dependencies: is-glob "^4.0.1" -global-dirs@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/global-dirs/-/global-dirs-2.0.1.tgz#acdf3bb6685bcd55cb35e8a052266569e9469201" - integrity sha512-5HqUqdhkEovj2Of/ms3IeS/EekcO54ytHRLV4PEY2rhRwrHXLQjeVEES0Lhka0xwNDtGYn58wyC4s5+MHsOO6A== +global-dirs@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/global-dirs/-/global-dirs-3.0.0.tgz#70a76fe84ea315ab37b1f5576cbde7d48ef72686" + integrity sha512-v8ho2DS5RiCjftj1nD9NmnfaOzTdud7RRnVd9kFNOjqZbISlx5DQ+OrTkywgd0dIt7oFCvKetZSHoHcP3sDdiA== dependencies: - ini "^1.3.5" + ini "2.0.0" -globby@^11.0.3: - version "11.0.4" - resolved "https://registry.yarnpkg.com/globby/-/globby-11.0.4.tgz#2cbaff77c2f2a62e71e9b2813a67b97a3a3001a5" - integrity sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg== +globby@^11.1.0: + version "11.1.0" + resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" + integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== dependencies: array-union "^2.1.0" dir-glob "^3.0.1" - fast-glob "^3.1.1" - ignore "^5.1.4" - merge2 "^1.3.0" + fast-glob "^3.2.9" + ignore "^5.2.0" + merge2 "^1.4.1" slash "^3.0.0" -google-auth-library@^6.0.0: - version "6.0.6" - resolved "https://registry.yarnpkg.com/google-auth-library/-/google-auth-library-6.0.6.tgz#5102e5c643baab45b4c16e9752cd56b8861f3a82" - integrity sha512-fWYdRdg55HSJoRq9k568jJA1lrhg9i2xgfhVIMJbskUmbDpJGHsbv9l41DGhCDXM21F9Kn4kUwdysgxSYBYJUw== +google-auth-library@^8.0.2: + version "8.0.2" + resolved "https://registry.yarnpkg.com/google-auth-library/-/google-auth-library-8.0.2.tgz#5fa0f2d3795c3e4019d2bb315ade4454cc9c30b5" + integrity sha512-HoG+nWFAThLovKpvcbYzxgn+nBJPTfAwtq0GxPN821nOO+21+8oP7MoEHfd1sbDulUFFGfcjJr2CnJ4YssHcyg== dependencies: arrify "^2.0.0" base64-js "^1.3.0" ecdsa-sig-formatter "^1.0.11" fast-text-encoding "^1.0.0" - gaxios "^3.0.0" - gcp-metadata "^4.1.0" - gtoken "^5.0.0" + gaxios "^5.0.0" + gcp-metadata "^5.0.0" + gtoken "^5.3.2" jws "^4.0.0" lru-cache "^6.0.0" -google-gax@^2.7.0: - version "2.7.0" - resolved "https://registry.yarnpkg.com/google-gax/-/google-gax-2.7.0.tgz#5aaff7bdbe32730f975f416dd4897c558c89ab84" - integrity sha512-0dBATy8mMVlfOBrT85Q+NzBpZ4OJZUMrPI9wJULpiIDq2w1zlN30Duor+fQUcMEjanYEc72G58M4iUVve0jfXw== +google-gax@^3.0.1: + version "3.1.0" + resolved "https://registry.yarnpkg.com/google-gax/-/google-gax-3.1.0.tgz#a6fea06bfd0157969ddc0a6d04c47980978ead02" + integrity sha512-OHKVHZtaYHwxrjiI4BH+IzRWH2Q75sZg+q0/RatxLicbeeCCIug99+NKQiE39B5rLnGENtcbUjmXiqJHbSrkSg== dependencies: - "@grpc/grpc-js" "~1.1.1" - "@grpc/proto-loader" "^0.5.1" + "@grpc/grpc-js" "~1.6.0" + "@grpc/proto-loader" "^0.6.12" "@types/long" "^4.0.0" abort-controller "^3.0.0" - duplexify "^3.6.0" - google-auth-library "^6.0.0" + duplexify "^4.0.0" + fast-text-encoding "^1.0.3" + google-auth-library "^8.0.2" is-stream-ended "^0.1.4" - lodash.at "^4.6.0" - lodash.has "^4.5.2" - node-fetch "^2.6.0" - protobufjs "^6.9.0" - retry-request "^4.0.0" - semver "^6.0.0" - walkdir "^0.4.0" + node-fetch "^2.6.1" + object-hash "^3.0.0" + proto3-json-serializer "^1.0.0" + protobufjs "6.11.3" + retry-request "^5.0.0" -google-p12-pem@^3.0.0: - version "3.0.2" - resolved "https://registry.yarnpkg.com/google-p12-pem/-/google-p12-pem-3.0.2.tgz#12d443994b6f4cd8c9e4ac479f2f18d4694cbdb8" - integrity sha512-tbjzndQvSIHGBLzHnhDs3cL4RBjLbLXc2pYvGH+imGVu5b4RMAttUTdnmW2UH0t11QeBTXZ7wlXPS7hrypO/tg== +google-p12-pem@^3.1.3: + version "3.1.4" + resolved "https://registry.yarnpkg.com/google-p12-pem/-/google-p12-pem-3.1.4.tgz#123f7b40da204de4ed1fbf2fd5be12c047fc8b3b" + integrity sha512-HHuHmkLgwjdmVRngf5+gSmpkyaRI6QmOg77J8tkNBHhNEI62sGHyw4/+UkgyZEI7h84NbWprXDJ+sa3xOYFvTg== dependencies: - node-forge "^0.9.0" + node-forge "^1.3.1" got@^9.6.0: version "9.6.0" @@ -1616,28 +1445,19 @@ graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0: resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.4.tgz#2256bde14d3632958c465ebc96dc467ca07a29fb" integrity sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw== -gtoken@^5.0.0: - version "5.0.3" - resolved "https://registry.yarnpkg.com/gtoken/-/gtoken-5.0.3.tgz#b76ef8e9a2fed6fef165e47f7d05b60c498e4d05" - integrity sha512-Nyd1wZCMRc2dj/mAD0LlfQLcAO06uKdpKJXvK85SGrF5+5+Bpfil9u/2aw35ltvEHjvl0h5FMKN5knEU+9JrOg== +gtoken@^5.3.2: + version "5.3.2" + resolved "https://registry.yarnpkg.com/gtoken/-/gtoken-5.3.2.tgz#deb7dc876abe002178e0515e383382ea9446d58f" + integrity sha512-gkvEKREW7dXWF8NV8pVrKfW7WqReAmjjkMBh6lNCCGOM4ucS0r0YyXXl0r/9Yj8wcW/32ISkfc8h5mPTDbtifQ== dependencies: - gaxios "^3.0.0" - google-p12-pem "^3.0.0" + gaxios "^4.0.0" + google-p12-pem "^3.1.3" jws "^4.0.0" - mime "^2.2.0" -har-schema@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" - integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI= - -har-validator@~5.1.3: - version "5.1.5" - resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.5.tgz#1f0803b9f8cb20c0fa13822df1ecddb36bde1efd" - integrity sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w== - dependencies: - ajv "^6.12.3" - har-schema "^2.0.0" +has-bigints@^1.0.1, has-bigints@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.2.tgz#0871bd3e3d51626f6ca0966668ba35d5602d6eaa" + integrity sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ== has-flag@^3.0.0: version "3.0.0" @@ -1649,6 +1469,25 @@ has-flag@^4.0.0: resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== +has-property-descriptors@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz#610708600606d36961ed04c196193b6a607fa861" + integrity sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ== + dependencies: + get-intrinsic "^1.1.1" + +has-symbols@^1.0.1, has-symbols@^1.0.2, has-symbols@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" + integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== + +has-tostringtag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.0.tgz#7e133818a7d394734f941e73c3d3f9291e658b25" + integrity sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ== + dependencies: + has-symbols "^1.0.2" + has-unicode@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" @@ -1671,36 +1510,16 @@ http-cache-semantics@^4.0.0: resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390" integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ== -http-errors@1.7.2: - version "1.7.2" - resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.2.tgz#4f5029cf13239f31036e5b2e55292bcfbcc85c8f" - integrity sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg== - dependencies: - depd "~1.1.2" - inherits "2.0.3" - setprototypeof "1.1.1" - statuses ">= 1.5.0 < 2" - toidentifier "1.0.0" - -http-errors@~1.7.2: - version "1.7.3" - resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.3.tgz#6c619e4f9c60308c38519498c14fbb10aacebb06" - integrity sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw== +http-errors@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3" + integrity sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ== dependencies: - depd "~1.1.2" + depd "2.0.0" inherits "2.0.4" - setprototypeof "1.1.1" - statuses ">= 1.5.0 < 2" - toidentifier "1.0.0" - -http-signature@~1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" - integrity sha1-muzZJRFHcvPZW2WmCruPfBj7rOE= - dependencies: - assert-plus "^1.0.0" - jsprim "^1.2.2" - sshpk "^1.7.0" + setprototypeof "1.2.0" + statuses "2.0.1" + toidentifier "1.0.1" https-proxy-agent@^5.0.0: version "5.0.0" @@ -1722,7 +1541,7 @@ ieee754@1.1.13, ieee754@^1.1.4: resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.13.tgz#ec168558e95aa181fd87d37f55c32bbcb6708b84" integrity sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg== -ieee754@^1.1.13: +ieee754@^1.1.13, ieee754@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== @@ -1732,10 +1551,10 @@ ignore-by-default@^1.0.1: resolved "https://registry.yarnpkg.com/ignore-by-default/-/ignore-by-default-1.0.1.tgz#48ca6d72f6c6a3af00a9ad4ae6876be3889e2b09" integrity sha1-SMptcvbGo68Aqa1K5odr44ieKwk= -ignore@^5.1.4: - version "5.1.8" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.8.tgz#f150a8b50a34289b33e22f5889abd4d8016f0e57" - integrity sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw== +ignore@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.0.tgz#6d3bac8fa7fe0d45d9f9be7bac2fc279577e345a" + integrity sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ== import-lazy@^2.1.0: version "2.1.0" @@ -1747,21 +1566,30 @@ imurmurhash@^0.1.4: resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= -inherits@2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" - integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= - inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.1, inherits@~2.0.3: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== -ini@^1.3.5, ini@~1.3.0: +ini@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ini/-/ini-2.0.0.tgz#e5fd556ecdd5726be978fa1001862eacb0a94bc5" + integrity sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA== + +ini@~1.3.0: version "1.3.5" resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927" integrity sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw== +internal-slot@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.3.tgz#7347e307deeea2faac2ac6205d4bc7d34967f59c" + integrity sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA== + dependencies: + get-intrinsic "^1.1.0" + has "^1.0.3" + side-channel "^1.0.4" + into-stream@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/into-stream/-/into-stream-6.0.0.tgz#4bfc1244c0128224e18b8870e85b2de8e66c6702" @@ -1775,11 +1603,26 @@ ipaddr.js@1.9.1: resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== +is-arguments@^1.0.4: + version "1.1.1" + resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.1.1.tgz#15b3f88fda01f2a97fec84ca761a560f123efa9b" + integrity sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA== + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + is-arrayish@^0.3.1: version "0.3.2" resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.3.2.tgz#4574a2ae56f7ab206896fb431eaeed066fdf8f03" integrity sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ== +is-bigint@^1.0.1: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.4.tgz#08147a1875bc2b32005d41ccd8291dffc6691df3" + integrity sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg== + dependencies: + has-bigints "^1.0.1" + is-binary-path@~2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" @@ -1787,11 +1630,24 @@ is-binary-path@~2.1.0: dependencies: binary-extensions "^2.0.0" +is-boolean-object@^1.1.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.1.2.tgz#5c6dc200246dd9321ae4b885a114bb1f75f63719" + integrity sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA== + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + is-buffer@^2.0.3: version "2.0.4" resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.4.tgz#3e572f23c8411a5cfd9557c849e3665e0b290623" integrity sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A== +is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.4.tgz#47301d58dd0259407865547853df6d61fe471945" + integrity sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w== + is-ci@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-2.0.0.tgz#6bc6334181810e04b5c22b3d589fdca55026404c" @@ -1799,13 +1655,20 @@ is-ci@^2.0.0: dependencies: ci-info "^2.0.0" -is-core-module@^2.2.0: - version "2.6.0" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.6.0.tgz#d7553b2526fe59b92ba3e40c8df757ec8a709e19" - integrity sha512-wShG8vs60jKfPWpF2KZRaAtvt3a20OAn7+IJ6hLPECpSABLcKtFKTTI4ZtH5QcBruBHlq+WsdHWyz0BCZW7svQ== +is-core-module@2.9.0, is-core-module@^2.8.1: + version "2.9.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.9.0.tgz#e1c34429cd51c6dd9e09e0799e396e27b19a9c69" + integrity sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A== dependencies: has "^1.0.3" +is-date-object@^1.0.1: + version "1.0.5" + resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.5.tgz#0841d5536e724c25597bf6ea62e1bd38298df31f" + integrity sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ== + dependencies: + has-tostringtag "^1.0.0" + is-extglob@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" @@ -1828,6 +1691,13 @@ is-fullwidth-code-point@^3.0.0: resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== +is-generator-function@^1.0.7: + version "1.0.10" + resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.0.10.tgz#f1558baf1ac17e0deea7c0415c438351ff2b3c72" + integrity sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A== + dependencies: + has-tostringtag "^1.0.0" + is-glob@^4.0.1, is-glob@~4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc" @@ -1835,18 +1705,30 @@ is-glob@^4.0.1, is-glob@~4.0.1: dependencies: is-extglob "^2.1.1" -is-installed-globally@^0.3.1: - version "0.3.2" - resolved "https://registry.yarnpkg.com/is-installed-globally/-/is-installed-globally-0.3.2.tgz#fd3efa79ee670d1187233182d5b0a1dd00313141" - integrity sha512-wZ8x1js7Ia0kecP/CHM/3ABkAmujX7WPvQk6uu3Fly/Mk44pySulQpnHG46OMjHGXApINnV4QhY3SWnECO2z5g== +is-installed-globally@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/is-installed-globally/-/is-installed-globally-0.4.0.tgz#9a0fd407949c30f86eb6959ef1b7994ed0b7b520" + integrity sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ== dependencies: - global-dirs "^2.0.1" - is-path-inside "^3.0.1" + global-dirs "^3.0.0" + is-path-inside "^3.0.2" -is-npm@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/is-npm/-/is-npm-4.0.0.tgz#c90dd8380696df87a7a6d823c20d0b12bbe3c84d" - integrity sha512-96ECIfh9xtDDlPylNPXhzjsykHsMJZ18ASpaWzQyBr4YRTcVjUvzaHayDAES2oU/3KpljhHUjtSRNiDwi0F0ig== +is-negative-zero@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.2.tgz#7bf6f03a28003b8b3965de3ac26f664d765f3150" + integrity sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA== + +is-npm@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/is-npm/-/is-npm-5.0.0.tgz#43e8d65cc56e1b67f8d47262cf667099193f45a8" + integrity sha512-WW/rQLOazUq+ST/bCAVBp/2oMERWLsR7OrKyt052dNDk4DHcDE0/7QSXITlmi+VBcV13DfIbysG3tZJm5RfdBA== + +is-number-object@^1.0.4: + version "1.0.7" + resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.7.tgz#59d50ada4c45251784e9904f5246c742f07a42fc" + integrity sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ== + dependencies: + has-tostringtag "^1.0.0" is-number@^7.0.0: version "7.0.0" @@ -1858,10 +1740,25 @@ is-obj@^2.0.0: resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-2.0.0.tgz#473fb05d973705e3fd9620545018ca8e22ef4982" integrity sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w== -is-path-inside@^3.0.1: - version "3.0.2" - resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.2.tgz#f5220fc82a3e233757291dddc9c5877f2a1f3017" - integrity sha512-/2UGPSgmtqwo1ktx8NDHjuPwZWmHhO+gj0f93EkhLB5RgW9RZevWYYlIkS6zePc6U2WpOdQYIwHe9YC4DWEBVg== +is-path-inside@^3.0.2: + version "3.0.3" + resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" + integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== + +is-regex@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958" + integrity sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg== + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + +is-shared-array-buffer@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz#8f259c573b60b6a32d4058a1a07430c0a7344c79" + integrity sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA== + dependencies: + call-bind "^1.0.2" is-stream-ended@^0.1.4: version "0.1.4" @@ -1873,11 +1770,43 @@ is-stream@^2.0.0: resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.0.tgz#bde9c32680d6fae04129d6ac9d921ce7815f78e3" integrity sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw== -is-typedarray@^1.0.0, is-typedarray@~1.0.0: +is-string@^1.0.5, is-string@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.7.tgz#0dd12bf2006f255bb58f695110eff7491eebc0fd" + integrity sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg== + dependencies: + has-tostringtag "^1.0.0" + +is-symbol@^1.0.2, is-symbol@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.4.tgz#a6dac93b635b063ca6872236de88910a57af139c" + integrity sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg== + dependencies: + has-symbols "^1.0.2" + +is-typed-array@^1.1.3, is-typed-array@^1.1.9: + version "1.1.9" + resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.9.tgz#246d77d2871e7d9f5aeb1d54b9f52c71329ece67" + integrity sha512-kfrlnTTn8pZkfpJMUgYD7YZ3qzeJgWUn8XfVYBARc4wnmNOmLbmuuaAs3q5fvB0UJOn6yHAKaGTPM7d6ezoD/A== + dependencies: + available-typed-arrays "^1.0.5" + call-bind "^1.0.2" + es-abstract "^1.20.0" + for-each "^0.3.3" + has-tostringtag "^1.0.0" + +is-typedarray@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= +is-weakref@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.0.2.tgz#9529f383a9338205e89765e0392efc2f100f06f2" + integrity sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ== + dependencies: + call-bind "^1.0.2" + is-yarn-global@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/is-yarn-global/-/is-yarn-global-0.3.0.tgz#d502d3382590ea3004893746754c89139973e232" @@ -1893,35 +1822,17 @@ isarray@^1.0.0, isarray@~1.0.0: resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= -isstream@~0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" - integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo= - -jmespath@0.15.0: - version "0.15.0" - resolved "https://registry.yarnpkg.com/jmespath/-/jmespath-0.15.0.tgz#a3f222a9aae9f966f5d27c796510e28091764217" - integrity sha1-o/Iiqarp+Wb10nx5ZRDigJF2Qhc= - -js-yaml@^3.14.0: - version "3.14.0" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.0.tgz#a7a34170f26a21bb162424d8adacb4113a69e482" - integrity sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A== - dependencies: - argparse "^1.0.7" - esprima "^4.0.0" - -jsbn@~0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" - integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM= +jmespath@0.16.0: + version "0.16.0" + resolved "https://registry.yarnpkg.com/jmespath/-/jmespath-0.16.0.tgz#b15b0a85dfd4d930d43e69ed605943c802785076" + integrity sha512-9FzQjJ7MATs1tSpnco1K6ayiYE3figslrXA72G2HQ/n76RzvYlofyi5QM+iX4YRs/pu3yzxlVQSST23+dMDknw== -json-bigint@^0.3.0: - version "0.3.1" - resolved "https://registry.yarnpkg.com/json-bigint/-/json-bigint-0.3.1.tgz#0c1729d679f580d550899d6a2226c228564afe60" - integrity sha512-DGWnSzmusIreWlEupsUelHrhwmPPE+FiQvg+drKfk2p+bdEYa5mp4PJ8JsCWqae0M2jQNb0HPvnwvf1qOTThzQ== +js-yaml@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" + integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== dependencies: - bignumber.js "^9.0.0" + argparse "^2.0.1" json-bigint@^1.0.0: version "1.0.0" @@ -1935,21 +1846,6 @@ json-buffer@3.0.0: resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.0.tgz#5b1f397afc75d677bde8bcfc0e47e1f9a3d9a898" integrity sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg= -json-schema-traverse@^0.4.1: - version "0.4.1" - resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" - integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== - -json-schema@0.2.3: - version "0.2.3" - resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" - integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM= - -json-stringify-safe@~5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" - integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= - json5@^2.1.1: version "2.1.3" resolved "https://registry.yarnpkg.com/json5/-/json5-2.1.3.tgz#c9b0f7fa9233bfe5807fe66fcf3a5617ed597d43" @@ -1966,20 +1862,10 @@ jsonfile@^6.0.1: optionalDependencies: graceful-fs "^4.1.6" -jsprim@^1.2.2: - version "1.4.1" - resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" - integrity sha1-MT5mvB5cwG5Di8G3SZwuXFastqI= - dependencies: - assert-plus "1.0.0" - extsprintf "1.3.0" - json-schema "0.2.3" - verror "1.10.0" - -jssha@^2.3.1: - version "2.4.2" - resolved "https://registry.yarnpkg.com/jssha/-/jssha-2.4.2.tgz#d950b095634928bd6b2bda1d42da9a3a762d65e9" - integrity sha512-/jsi/9C0S70zfkT/4UlKQa5E1xKurDnXcQizcww9JSR/Fv+uIbWM2btG+bFcL3iNoK9jIGS0ls9HWLr1iw0kFg== +jssha@^3.1.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/jssha/-/jssha-3.2.0.tgz#88ec50b866dd1411deaddbe6b3e3692e4c710f16" + integrity sha512-QuruyBENDWdN4tZwJbQq7/eAK85FqrI4oDbXjy5IBhYD+2pTJyBUWZe8ctWaCkrV0gy6AaelgOZZBMeswEa/6Q== jwa@^2.0.0: version "2.0.0" @@ -1998,10 +1884,10 @@ jws@^4.0.0: jwa "^2.0.0" safe-buffer "^5.0.1" -kafkajs@^1.15.0: - version "1.15.0" - resolved "https://registry.yarnpkg.com/kafkajs/-/kafkajs-1.15.0.tgz#a5ada0d933edca2149177393562be6fb0875ec3a" - integrity sha512-yjPyEnQCkPxAuQLIJnY5dI+xnmmgXmhuOQ1GVxClG5KTOV/rJcW1qA3UfvyEJKTp/RTSqQnUR3HJsKFvHyTpNg== +kafkajs@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/kafkajs/-/kafkajs-2.0.2.tgz#cdfc8f57aa4fd69f6d9ca1cce4ee89bbc2a3a1f9" + integrity sha512-g6CM3fAenofOjR1bfOAqeZUEaSGhNtBscNokybSdW1rmIKYNwBPC9xQzwulFJm36u/xcxXUiCl/L/qfslapihA== keyv@^3.0.0: version "3.1.0" @@ -2015,7 +1901,7 @@ kuler@^2.0.0: resolved "https://registry.yarnpkg.com/kuler/-/kuler-2.0.0.tgz#e2c570a3800388fb44407e851531c1d670b061b3" integrity sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A== -latest-version@^5.0.0: +latest-version@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/latest-version/-/latest-version-5.1.0.tgz#119dfe908fe38d15dfa43ecd13fa12ec8832face" integrity sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA== @@ -2030,47 +1916,25 @@ levn@~0.3.0: prelude-ls "~1.1.2" type-check "~0.3.2" -locate-path@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" - integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== - dependencies: - p-locate "^4.1.0" - -lodash.at@^4.6.0: - version "4.6.0" - resolved "https://registry.yarnpkg.com/lodash.at/-/lodash.at-4.6.0.tgz#93cdce664f0a1994ea33dd7cd40e23afd11b0ff8" - integrity sha1-k83OZk8KGZTqM9181A4jr9EbD/g= - lodash.camelcase@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6" integrity sha1-soqmKIorn8ZRA1x3EfZathkDMaY= -lodash.has@^4.5.2: - version "4.5.2" - resolved "https://registry.yarnpkg.com/lodash.has/-/lodash.has-4.5.2.tgz#d19f4dc1095058cccbe2b0cdf4ee0fe4aa37c862" - integrity sha1-0Z9NwQlQWMzL4rDN9O4P5Ko3yGI= - lodash.snakecase@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz#39d714a35357147837aefd64b5dcbb16becd8f8d" integrity sha1-OdcUo1NXFHg3rv1ktdy7Fr7Nj40= -lodash@^4.17.19: - version "4.17.21" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" - integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== - -logform@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/logform/-/logform-2.2.0.tgz#40f036d19161fc76b68ab50fdc7fe495544492f2" - integrity sha512-N0qPlqfypFx7UHNn4B3lzS/b0uLqt2hmuoa+PpuXNYgozdJYAyauF5Ky0BWVjrxDlMWiT3qN4zPq3vVAfZy7Yg== +logform@^2.3.2, logform@^2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/logform/-/logform-2.4.0.tgz#131651715a17d50f09c2a2c1a524ff1a4164bcfe" + integrity sha512-CPSJw4ftjf517EhXZGGvTHHkYobo7ZCc0kvwUoOYcjfR2UVrI66RHj8MCrfAdEitdmFqbu2BYdYs8FHHZSb6iw== dependencies: - colors "^1.2.1" - fast-safe-stringify "^2.0.4" + "@colors/colors" "1.5.0" fecha "^4.2.0" ms "^2.1.1" + safe-stable-stringify "^2.3.1" triple-beam "^1.3.0" long@^4.0.0: @@ -2078,6 +1942,11 @@ long@^4.0.0: resolved "https://registry.yarnpkg.com/long/-/long-4.0.0.tgz#9a7b71cfb7d361a194ea555241c92f7468d5bf28" integrity sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA== +long@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/long/-/long-5.2.0.tgz#2696dadf4b4da2ce3f6f6b89186085d94d52fd61" + integrity sha512-9RTUNjK60eJbx3uz+TEGF7fUr29ZDxR5QzXcyDpeSfeH28S9ycINflOgOlppit5U+4kNTe83KQnMEerw7GmE8w== + lowercase-keys@^1.0.0, lowercase-keys@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f" @@ -2112,7 +1981,7 @@ merge-descriptors@1.0.1: resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" integrity sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E= -merge2@^1.3.0: +merge2@^1.3.0, merge2@^1.4.1: version "1.4.1" resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== @@ -2122,13 +1991,13 @@ methods@~1.1.2: resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4= -micromatch@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.2.tgz#4fcb0999bf9fbc2fcbdd212f6d629b9a56c39259" - integrity sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q== +micromatch@^4.0.4: + version "4.0.5" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6" + integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA== dependencies: - braces "^3.0.1" - picomatch "^2.0.5" + braces "^3.0.2" + picomatch "^2.3.1" mime-db@1.44.0: version "1.44.0" @@ -2140,7 +2009,12 @@ mime-db@1.48.0: resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.48.0.tgz#e35b31045dd7eada3aaad537ed88a33afbef2d1d" integrity sha512-FM3QwxV+TnZYQ2aRqhlKBMHxk10lTbMt3bBkMAp54ddrNeVSfcQYOOKuGuy3Ddrm38I04If834fOUSq1yzslJQ== -mime-types@^2.1.12, mime-types@~2.1.19: +mime-db@1.52.0: + version "1.52.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" + integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== + +mime-types@^2.1.12: version "2.1.27" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.27.tgz#47949f98e279ea53119f5722e0f34e529bec009f" integrity sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w== @@ -2154,16 +2028,18 @@ mime-types@~2.1.24: dependencies: mime-db "1.48.0" +mime-types@~2.1.34: + version "2.1.35" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" + integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== + dependencies: + mime-db "1.52.0" + mime@1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== -mime@^2.2.0: - version "2.4.6" - resolved "https://registry.yarnpkg.com/mime/-/mime-2.4.6.tgz#e5b407c90db442f2beb5b162373d07b69affa4d1" - integrity sha512-RZKhC3EmpBchfTGBVb8fb+RL2cWyw/32lshnsETttkBAyAUXSGHxbEJWWRXc751DrIxG1q04b8QwMbAwkRPpUA== - mimic-response@^1.0.0, mimic-response@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b" @@ -2181,35 +2057,30 @@ minimatch@^3.0.4: dependencies: brace-expansion "^1.1.7" -minimist@^1.2.0, minimist@^1.2.3, minimist@^1.2.5: - version "1.2.5" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" - integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== +minimist@^1.2.0, minimist@^1.2.3, minimist@^1.2.5, minimist@^1.2.6: + version "1.2.6" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44" + integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q== mkdirp-classic@^0.5.2, mkdirp-classic@^0.5.3: version "0.5.3" resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113" integrity sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A== -moment@^2.11.2: - version "2.27.0" - resolved "https://registry.yarnpkg.com/moment/-/moment-2.27.0.tgz#8bff4e3e26a236220dfe3e36de756b6ebaa0105d" - integrity sha512-al0MUK7cpIcglMv3YF13qSgdAIqxHTO7brRtaz3DlSULbqfazqkc5kEjNrLDOM7fsjshoFIihnU8snrP7zUvhQ== - -mpns@2.1.3: - version "2.1.3" - resolved "https://registry.yarnpkg.com/mpns/-/mpns-2.1.3.tgz#4328bb23ca79669e3383389074c898713e22ccb9" - integrity sha512-gPLNoVqwYoKUmNYZ2shMSdaE2XvHSRxWNzyG4DUi6Av7MSujyeOw/nj61nnQeuV/vke5E0Dni468xn0qxTHIZQ== +moment@^2.29.1: + version "2.29.3" + resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.3.tgz#edd47411c322413999f7a5940d526de183c031f3" + integrity sha512-c6YRvhEo//6T2Jz/vVtYzqBzwvPT95JBQ+smCytzf7c50oMZRsR/a4w88aD34I+/QVSfnoAnSBFPJHItlOMJVw== ms@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= -ms@2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" - integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== +ms@2.1.3: + version "2.1.3" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== ms@^2.1.1: version "2.1.2" @@ -2229,53 +2100,45 @@ napi-build-utils@^1.0.1: resolved "https://registry.yarnpkg.com/napi-build-utils/-/napi-build-utils-1.0.2.tgz#b1fddc0b2c46e380a0b7a76f984dd47c41a13806" integrity sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg== -negotiator@0.6.2: - version "0.6.2" - resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" - integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw== +negotiator@0.6.3: + version "0.6.3" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" + integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== -node-abi@^2.7.0: +node-abi@^2.21.0: version "2.30.1" resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-2.30.1.tgz#c437d4b1fe0e285aaf290d45b45d4d7afedac4cf" integrity sha512-/2D0wOQPgaUWzVSVgRMx+trKJRC2UG4SUc4oCJoXx9Uxjtp0Vy3/kt7zcbxHF8+Z/pK3UloLWzBISg72brfy1w== dependencies: semver "^5.4.1" -node-fetch@^2.3.0, node-fetch@^2.6.0: - version "2.6.0" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.0.tgz#e633456386d4aa55863f676a7ab0daa8fdecb0fd" - integrity sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA== - -node-fetch@^2.6.1: - version "2.6.2" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.2.tgz#986996818b73785e47b1965cc34eb093a1d464d0" - integrity sha512-aLoxToI6RfZ+0NOjmWAgn9+LEd30YCkJKFSyWacNZdEKTit/ZMcKjGkTRo8uWEsnIb/hfKecNPEbln02PdWbcA== +node-fetch@^2.6.1, node-fetch@^2.6.6, node-fetch@^2.6.7: + version "2.6.7" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad" + integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ== + dependencies: + whatwg-url "^5.0.0" -node-forge@^0.9.0: - version "0.9.1" - resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.9.1.tgz#775368e6846558ab6676858a4d8c6e8d16c677b5" - integrity sha512-G6RlQt5Sb4GMBzXvhfkeFmbqR6MzhtnT7VTHuLadjkii3rdYHNdw0m8zA4BTxVIh68FicCQ2NSUANpsqkr9jvQ== +node-forge@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-1.3.1.tgz#be8da2af243b2417d5f646a770663a92b7e9ded3" + integrity sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA== -nodemon@^2.0.12: - version "2.0.12" - resolved "https://registry.yarnpkg.com/nodemon/-/nodemon-2.0.12.tgz#5dae4e162b617b91f1873b3bfea215dd71e144d5" - integrity sha512-egCTmNZdObdBxUBw6ZNwvZ/xzk24CKRs5K6d+5zbmrMr7rOpPmfPeF6OxM3DDpaRx331CQRFEktn+wrFFfBSOA== +nodemon@^2.0.16: + version "2.0.16" + resolved "https://registry.yarnpkg.com/nodemon/-/nodemon-2.0.16.tgz#d71b31bfdb226c25de34afea53486c8ef225fdef" + integrity sha512-zsrcaOfTWRuUzBn3P44RDliLlp263Z/76FPoHFr3cFFkOz0lTPAcIw8dCzfdVIx/t3AtDYCZRCDkoCojJqaG3w== dependencies: - chokidar "^3.2.2" - debug "^3.2.6" + chokidar "^3.5.2" + debug "^3.2.7" ignore-by-default "^1.0.1" minimatch "^3.0.4" - pstree.remy "^1.1.7" + pstree.remy "^1.1.8" semver "^5.7.1" supports-color "^5.5.0" touch "^3.1.0" - undefsafe "^2.0.3" - update-notifier "^4.1.0" - -noop-logger@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/noop-logger/-/noop-logger-0.1.1.tgz#94a2b1633c4f1317553007d8966fd0e841b6a4c2" - integrity sha1-lKKxYzxPExdVMAfYlm/Q6EG2pMI= + undefsafe "^2.0.5" + update-notifier "^5.1.0" nopt@~1.0.10: version "1.0.10" @@ -2309,11 +2172,6 @@ number-is-nan@^1.0.0: resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0= -oauth-sign@~0.9.0: - version "0.9.0" - resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" - integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== - object-assign@^4.1.0: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" @@ -2324,10 +2182,35 @@ object-hash@^2.0.1: resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-2.0.3.tgz#d12db044e03cd2ca3d77c0570d87225b02e1e6ea" integrity sha512-JPKn0GMu+Fa3zt3Bmr66JhokJU5BaNBIh4ZeTlaCBzrBsOeXzwcKKAK1tbLiPKgvwmPXsDvvLHoWh5Bm7ofIYg== -on-finished@~2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" - integrity sha1-IPEzZIGwg811M3mSoWlxqi2QaUc= +object-hash@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-3.0.0.tgz#73f97f753e7baffc0e2cc9d6e079079744ac82e9" + integrity sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw== + +object-inspect@^1.12.0, object-inspect@^1.9.0: + version "1.12.2" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.2.tgz#c0641f26394532f28ab8d796ab954e43c009a8ea" + integrity sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ== + +object-keys@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" + integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== + +object.assign@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.2.tgz#0ed54a342eceb37b38ff76eb831a0e788cb63940" + integrity sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ== + dependencies: + call-bind "^1.0.0" + define-properties "^1.1.3" + has-symbols "^1.0.1" + object-keys "^1.1.1" + +on-finished@2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f" + integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg== dependencies: ee-first "1.1.1" @@ -2372,25 +2255,6 @@ p-is-promise@^3.0.0: resolved "https://registry.yarnpkg.com/p-is-promise/-/p-is-promise-3.0.0.tgz#58e78c7dfe2e163cf2a04ff869e7c1dba64a5971" integrity sha512-Wo8VsW4IRQSKVXsJCn7TomUaVtyfjVDn3nUP7kE967BQk0CwFpdbZs0X0uk5sW9mkBa9eNM7hCMaG93WUAwxYQ== -p-limit@^2.2.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" - integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== - dependencies: - p-try "^2.0.0" - -p-locate@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" - integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== - dependencies: - p-limit "^2.2.0" - -p-try@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" - integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== - package-json@^6.3.0: version "6.5.0" resolved "https://registry.yarnpkg.com/package-json/-/package-json-6.5.0.tgz#6feedaca35e75725876d0b0e64974697fed145b0" @@ -2406,15 +2270,10 @@ parseurl@~1.3.3: resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== -path-exists@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" - integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== - -path-parse@^1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" - integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw== +path-parse@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" + integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== path-to-regexp@0.1.7: version "0.1.7" @@ -2426,54 +2285,54 @@ path-type@^4.0.0: resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== -performance-now@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" - integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= - -picomatch@^2.0.4, picomatch@^2.0.5, picomatch@^2.2.1: +picomatch@^2.0.4, picomatch@^2.2.1: version "2.2.2" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.2.tgz#21f333e9b6b8eaff02468f5146ea406d345f4dad" integrity sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg== -pkg-fetch@3.2.2: - version "3.2.2" - resolved "https://registry.yarnpkg.com/pkg-fetch/-/pkg-fetch-3.2.2.tgz#33f391eb176c1844e93189a32f2279b36a1ec949" - integrity sha512-bLhFNT4cNnONxzbHo1H2mCCKuQkCR4dgQtv0gUZnWtp8TDP0v0UAXKHG7DXhAoTC5IYP3slLsFJtIda9ksny8g== +picomatch@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== + +pkg-fetch@3.4.1: + version "3.4.1" + resolved "https://registry.yarnpkg.com/pkg-fetch/-/pkg-fetch-3.4.1.tgz#be68bb9f7fdb0f6ed995abc518ab2e35aa64d2fd" + integrity sha512-fS4cdayCa1r4jHkOKGPJKnS9PEs6OWZst+s+m0+CmhmPZObMnxoRnf9T9yUWl+lzM2b5aJF7cnQIySCT7Hq8Dg== dependencies: - chalk "^4.1.0" + chalk "^4.1.2" fs-extra "^9.1.0" https-proxy-agent "^5.0.0" - node-fetch "^2.6.1" + node-fetch "^2.6.6" progress "^2.0.3" semver "^7.3.5" + tar-fs "^2.1.1" yargs "^16.2.0" -pkg@^5.3.1: - version "5.3.1" - resolved "https://registry.yarnpkg.com/pkg/-/pkg-5.3.1.tgz#8f81671613b9e5bb1d83c39b2eed4799e1e679fe" - integrity sha512-jT/sptM1ZG++FNk+jnJYNoWLDQXYd7hqpnBhd5j18SNW1jJzNYo55RahuCiD0KN0PX9mb53GWCqKM0ia/mJytA== +pkg@^5.7.0: + version "5.7.0" + resolved "https://registry.yarnpkg.com/pkg/-/pkg-5.7.0.tgz#6422df05e8aa147764be6ef912921d0fa719ea95" + integrity sha512-PTiAjNq/CGAtK5qUBR6pjheqnipTFjeecgSgIKEcAOJA4GpmZeOZC8pMOoT0rfes5vHsmcFo7wbSRTAmXQurrg== dependencies: - "@babel/parser" "7.13.13" - "@babel/types" "7.13.12" - chalk "^4.1.0" + "@babel/parser" "7.17.10" + "@babel/types" "7.17.10" + chalk "^4.1.2" escodegen "^2.0.0" fs-extra "^9.1.0" - globby "^11.0.3" + globby "^11.1.0" into-stream "^6.0.0" - minimist "^1.2.5" + is-core-module "2.9.0" + minimist "^1.2.6" multistream "^4.1.0" - pkg-fetch "3.2.2" - prebuild-install "6.0.1" - progress "^2.0.3" - resolve "^1.20.0" + pkg-fetch "3.4.1" + prebuild-install "6.1.4" + resolve "^1.22.0" stream-meter "^1.0.4" - tslib "2.1.0" -prebuild-install@6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-6.0.1.tgz#5902172f7a40eb67305b96c2a695db32636ee26d" - integrity sha512-7GOJrLuow8yeiyv75rmvZyeMGzl8mdEX5gY69d6a6bHWmiPevwqFw+tQavhK0EYMaSg3/KD24cWqeQv1EWsqDQ== +prebuild-install@6.1.4: + version "6.1.4" + resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-6.1.4.tgz#ae3c0142ad611d58570b89af4986088a4937e00f" + integrity sha512-Z4vpywnK1lBg+zdPCVCsKq0xO66eEV9rWo2zrROGGiRS4JtueBOdlB1FnY8lcy7JsUud/Q3ijUxyWN26Ika0vQ== dependencies: detect-libc "^1.0.3" expand-template "^2.0.3" @@ -2481,15 +2340,13 @@ prebuild-install@6.0.1: minimist "^1.2.3" mkdirp-classic "^0.5.3" napi-build-utils "^1.0.1" - node-abi "^2.7.0" - noop-logger "^0.1.1" + node-abi "^2.21.0" npmlog "^4.0.1" pump "^3.0.0" rc "^1.2.7" simple-get "^3.0.3" tar-fs "^2.0.0" tunnel-agent "^0.6.0" - which-pm-runs "^1.0.0" prelude-ls@~1.1.2: version "1.1.2" @@ -2516,10 +2373,17 @@ progress@^2.0.3: resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== -protobufjs@^6.8.6, protobufjs@^6.9.0: - version "6.10.1" - resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-6.10.1.tgz#e6a484dd8f04b29629e9053344e3970cccf13cd2" - integrity sha512-pb8kTchL+1Ceg4lFd5XUpK8PdWacbvV5SK2ULH2ebrYtl4GjJmS24m6CKME67jzV53tbJxHlnNOSqQHbTsR9JQ== +proto3-json-serializer@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/proto3-json-serializer/-/proto3-json-serializer-1.0.1.tgz#5d2b0b3c85568edb62984c94ce2e726985de44be" + integrity sha512-jtnGKL8EE1mTOl2qgMZJQXbS22dlb2K07i4b5vp8yMGMJ6mFSgBg4Ossu/g9NCuamdYXHjp3XxyWw4tJ0G/uKw== + dependencies: + protobufjs "^6.11.3" + +protobufjs@6.11.3, protobufjs@^6.11.3: + version "6.11.3" + resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-6.11.3.tgz#637a527205a35caa4f3e2a9a4a13ddffe0e7af74" + integrity sha512-xL96WDdCZYdU7Slin569tFX712BxsxslWwAfAhCYjQKGTq7dAU91Lomy6nLLhh/dyGhk/YH4TwTSRxTzhuHyZg== dependencies: "@protobufjs/aspromise" "^1.1.2" "@protobufjs/base64" "^1.1.2" @@ -2532,10 +2396,10 @@ protobufjs@^6.8.6, protobufjs@^6.9.0: "@protobufjs/pool" "^1.1.0" "@protobufjs/utf8" "^1.1.0" "@types/long" "^4.0.1" - "@types/node" "^13.7.0" + "@types/node" ">=13.7.0" long "^4.0.0" -proxy-addr@~2.0.5: +proxy-addr@~2.0.7: version "2.0.7" resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025" integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg== @@ -2543,12 +2407,12 @@ proxy-addr@~2.0.5: forwarded "0.2.0" ipaddr.js "1.9.1" -psl@^1.1.28, psl@^1.1.33: +psl@^1.1.33: version "1.8.0" resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24" integrity sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ== -pstree.remy@^1.1.7: +pstree.remy@^1.1.8: version "1.1.8" resolved "https://registry.yarnpkg.com/pstree.remy/-/pstree.remy-1.1.8.tgz#c242224f4a67c21f686839bbdb4ac282b8373d3a" integrity sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w== @@ -2566,27 +2430,24 @@ punycode@1.3.2: resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" integrity sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0= -punycode@^2.1.0, punycode@^2.1.1: +punycode@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== -pupa@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/pupa/-/pupa-2.0.1.tgz#dbdc9ff48ffbea4a26a069b6f9f7abb051008726" - integrity sha512-hEJH0s8PXLY/cdXh66tNEQGndDrIKNqNC5xmrysZy3i5C3oEoLna7YAOad+7u125+zH1HNXUmGEkrhb3c2VriA== +pupa@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/pupa/-/pupa-2.1.1.tgz#f5e8fd4afc2c5d97828faa523549ed8744a20d62" + integrity sha512-l1jNAspIBSFqbT+y+5FosojNpVpF94nlI+wDUpqP9enwOTfHx9f0gh5nB96vl+6yTpsJsypeNrwfzPrKuHB41A== dependencies: escape-goat "^2.0.0" -qs@6.7.0: - version "6.7.0" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc" - integrity sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ== - -qs@~6.5.2: - version "6.5.2" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" - integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA== +qs@6.10.3: + version "6.10.3" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.10.3.tgz#d6cde1b2ffca87b5aa57889816c5f81535e22e8e" + integrity sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ== + dependencies: + side-channel "^1.0.4" querystring@0.2.0: version "0.2.0" @@ -2603,13 +2464,13 @@ range-parser@~1.2.1: resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== -raw-body@2.4.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.0.tgz#a1ce6fb9c9bc356ca52e89256ab59059e13d0332" - integrity sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q== +raw-body@2.5.1: + version "2.5.1" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.1.tgz#fe1b1628b181b700215e5fd42389f98b71392857" + integrity sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig== dependencies: - bytes "3.1.0" - http-errors "1.7.2" + bytes "3.1.2" + http-errors "2.0.0" iconv-lite "0.4.24" unpipe "1.0.0" @@ -2633,7 +2494,7 @@ rc@^1.2.7, rc@^1.2.8: isarray "0.0.1" string_decoder "~0.10.x" -readable-stream@^2.0.0, readable-stream@^2.0.2, readable-stream@^2.0.6, readable-stream@^2.1.4, readable-stream@^2.3.7: +readable-stream@^2.0.0, readable-stream@^2.0.6, readable-stream@^2.1.4, readable-stream@^2.3.7: version "2.3.7" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== @@ -2655,13 +2516,22 @@ readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.6.0: string_decoder "^1.1.1" util-deprecate "^1.0.1" -readdirp@~3.4.0: - version "3.4.0" - resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.4.0.tgz#9fdccdf9e9155805449221ac645e8303ab5b9ada" - integrity sha512-0xe001vZBnJEK+uKcj8qOhyAKPzIT+gStxWr3LCB0DwcXR5NZJ3IaC+yGnHCYzB/S7ov3m3EEbZI2zeNvX+hGQ== +readdirp@~3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" + integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== dependencies: picomatch "^2.2.1" +regexp.prototype.flags@^1.4.3: + version "1.4.3" + resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz#87cab30f80f66660181a3bb7bf5981a872b367ac" + integrity sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + functions-have-names "^1.2.2" + registry-auth-token@^4.0.0: version "4.2.0" resolved "https://registry.yarnpkg.com/registry-auth-token/-/registry-auth-token-4.2.0.tgz#1d37dffda72bbecd0f581e4715540213a65eb7da" @@ -2676,54 +2546,24 @@ registry-url@^5.0.0: dependencies: rc "^1.2.8" -request@^2.81.0: - version "2.88.2" - resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3" - integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw== - dependencies: - aws-sign2 "~0.7.0" - aws4 "^1.8.0" - caseless "~0.12.0" - combined-stream "~1.0.6" - extend "~3.0.2" - forever-agent "~0.6.1" - form-data "~2.3.2" - har-validator "~5.1.3" - http-signature "~1.2.0" - is-typedarray "~1.0.0" - isstream "~0.1.2" - json-stringify-safe "~5.0.1" - mime-types "~2.1.19" - oauth-sign "~0.9.0" - performance-now "^2.1.0" - qs "~6.5.2" - safe-buffer "^5.1.2" - tough-cookie "~2.5.0" - tunnel-agent "^0.6.0" - uuid "^3.3.2" - require-directory@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= -require-main-filename@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" - integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== - requires-port@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" integrity sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8= -resolve@^1.20.0: - version "1.20.0" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975" - integrity sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A== +resolve@^1.22.0: + version "1.22.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.0.tgz#5e0b8c67c15df57a89bdbabe603a002f21731198" + integrity sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw== dependencies: - is-core-module "^2.2.0" - path-parse "^1.0.6" + is-core-module "^2.8.1" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" responselike@^1.0.2: version "1.0.2" @@ -2732,31 +2572,32 @@ responselike@^1.0.2: dependencies: lowercase-keys "^1.0.0" -retry-request@^4.0.0: - version "4.1.3" - resolved "https://registry.yarnpkg.com/retry-request/-/retry-request-4.1.3.tgz#d5f74daf261372cff58d08b0a1979b4d7cab0fde" - integrity sha512-QnRZUpuPNgX0+D1xVxul6DbJ9slvo4Rm6iV/dn63e048MvGbUZiKySVt6Tenp04JqmchxjiLltGerOJys7kJYQ== +retry-request@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/retry-request/-/retry-request-5.0.0.tgz#886ff8ec0e77fffbe66a4d5e90fd8f6646b6eae4" + integrity sha512-vBZdBxUordje9253imlmGtppC5gdcwZmNz7JnU2ui+KKFPk25keR+0c020AVV20oesYxIFOI0Kh3HE88/59ieg== dependencies: debug "^4.1.1" + extend "^3.0.2" reusify@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== -rhea-promise@^0.1.15: - version "0.1.15" - resolved "https://registry.yarnpkg.com/rhea-promise/-/rhea-promise-0.1.15.tgz#00175324352224424d59b7712faf14097a84e8f2" - integrity sha512-+6uilZXSJGyiqVeHQI3Krv6NTAd8cWRCY2uyCxmzR4/5IFtBqqFem1HV2OiwSj0Gu7OFChIJDfH2JyjN7J0vRA== +rhea-promise@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/rhea-promise/-/rhea-promise-2.1.0.tgz#540e97f62302ad54b368b79a3560a51eefd2b063" + integrity sha512-CRMwdJ/o4oO/xKcvAwAsd0AHy5fVvSlqso7AadRmaaLGzAzc9LCoW7FOFnucI8THasVmOeCnv5c/fH/n7FcNaA== dependencies: debug "^3.1.0" - rhea "^1.0.4" - tslib "^1.9.3" + rhea "^2.0.3" + tslib "^2.2.0" -rhea@^1.0.18, rhea@^1.0.23, rhea@^1.0.4: - version "1.0.24" - resolved "https://registry.yarnpkg.com/rhea/-/rhea-1.0.24.tgz#7084e4c635aa8ba0faf2d9602ac9f97d26aedc00" - integrity sha512-PEl62U2EhxCO5wMUZ2/bCBcXAVKN9AdMSNQOrp3+R5b77TEaOSiy16MQ0sIOmzj/iqsgIAgPs1mt3FYfu1vIXA== +rhea@^2.0.3: + version "2.0.8" + resolved "https://registry.yarnpkg.com/rhea/-/rhea-2.0.8.tgz#2ac355fad923e36a995defffdf314bb1cbe562de" + integrity sha512-IgwlP4D2lzinBSll5f35tAWa30dGCZhG9Ujd1DiaB7MUGegIjAaLzqATCw3ha+h9oq9mXcitqayBbNIXYdvtFg== dependencies: debug "0.8.0 - 3.5.0" @@ -2765,17 +2606,22 @@ run-parallel@^1.1.9: resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.1.9.tgz#c9dd3a7cf9f4b2c4b6244e173a6ed866e61dd679" integrity sha512-DEqnSRTDw/Tc3FXf49zedI638Z9onwUotBMiUFKmrO2sdFKIbXamXGQ3Axd4qgphxKB4kw/qP1w5kTxnfU1B9Q== -safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1, safe-buffer@~5.1.2: +safe-buffer@5.2.1, safe-buffer@^5.0.1, safe-buffer@^5.1.2, safe-buffer@~5.2.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + +safe-buffer@~5.1.0, safe-buffer@~5.1.1, safe-buffer@~5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== -safe-buffer@^5.0.1, safe-buffer@^5.1.2, safe-buffer@~5.2.0: - version "5.2.1" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" - integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== +safe-stable-stringify@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/safe-stable-stringify/-/safe-stable-stringify-2.3.1.tgz#ab67cbe1fe7d40603ca641c5e765cb942d04fc73" + integrity sha512-kYBSfT+troD9cDA85VDnHZ1rpHC50O0g1e6WlGHVCz/g+JS+9WKLj+XwFYyR8UbrZN8ll9HUpDAAddY58MGisg== -"safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: +"safer-buffer@>= 2.1.2 < 3": version "2.1.2" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== @@ -2807,10 +2653,12 @@ semver@^6.0.0, semver@^6.2.0, semver@^6.3.0: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== -semver@^7.1.3: - version "7.3.2" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.2.tgz#604962b052b81ed0786aae84389ffba70ffd3938" - integrity sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ== +semver@^7.3.4: + version "7.3.7" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.7.tgz#12c5b649afdbf9049707796e22a4028814ce523f" + integrity sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g== + dependencies: + lru-cache "^6.0.0" semver@^7.3.5: version "7.3.5" @@ -2819,44 +2667,53 @@ semver@^7.3.5: dependencies: lru-cache "^6.0.0" -send@0.17.1: - version "0.17.1" - resolved "https://registry.yarnpkg.com/send/-/send-0.17.1.tgz#c1d8b059f7900f7466dd4938bdc44e11ddb376c8" - integrity sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg== +send@0.18.0: + version "0.18.0" + resolved "https://registry.yarnpkg.com/send/-/send-0.18.0.tgz#670167cc654b05f5aa4a767f9113bb371bc706be" + integrity sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg== dependencies: debug "2.6.9" - depd "~1.1.2" - destroy "~1.0.4" + depd "2.0.0" + destroy "1.2.0" encodeurl "~1.0.2" escape-html "~1.0.3" etag "~1.8.1" fresh "0.5.2" - http-errors "~1.7.2" + http-errors "2.0.0" mime "1.6.0" - ms "2.1.1" - on-finished "~2.3.0" + ms "2.1.3" + on-finished "2.4.1" range-parser "~1.2.1" - statuses "~1.5.0" + statuses "2.0.1" -serve-static@1.14.1: - version "1.14.1" - resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.14.1.tgz#666e636dc4f010f7ef29970a88a674320898b2f9" - integrity sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg== +serve-static@1.15.0: + version "1.15.0" + resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.15.0.tgz#faaef08cffe0a1a62f60cad0c4e513cff0ac9540" + integrity sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g== dependencies: encodeurl "~1.0.2" escape-html "~1.0.3" parseurl "~1.3.3" - send "0.17.1" + send "0.18.0" -set-blocking@^2.0.0, set-blocking@~2.0.0: +set-blocking@~2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= -setprototypeof@1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.1.tgz#7e95acb24aa92f5885e0abef5ba131330d4ae683" - integrity sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw== +setprototypeof@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" + integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== + +side-channel@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" + integrity sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw== + dependencies: + call-bind "^1.0.0" + get-intrinsic "^1.0.2" + object-inspect "^1.9.0" signal-exit@^3.0.0, signal-exit@^3.0.2: version "3.0.3" @@ -2894,43 +2751,15 @@ source-map@~0.6.1: resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== -sprintf-js@~1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" - integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= - -sshpk@^1.7.0: - version "1.16.1" - resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877" - integrity sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg== - dependencies: - asn1 "~0.2.3" - assert-plus "^1.0.0" - bcrypt-pbkdf "^1.0.0" - dashdash "^1.12.0" - ecc-jsbn "~0.1.1" - getpass "^0.1.1" - jsbn "~0.1.0" - safer-buffer "^2.0.2" - tweetnacl "~0.14.0" - stack-trace@0.0.x: version "0.0.10" resolved "https://registry.yarnpkg.com/stack-trace/-/stack-trace-0.0.10.tgz#547c70b347e8d32b4e108ea1a2a159e5fdde19c0" integrity sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA= -"statuses@>= 1.5.0 < 2", statuses@~1.5.0: - version "1.5.0" - resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" - integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= - -stream-browserify@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-2.0.2.tgz#87521d38a44aa7ee91ce1cd2a47df0cb49dd660b" - integrity sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg== - dependencies: - inherits "~2.0.1" - readable-stream "^2.0.2" +statuses@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63" + integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== stream-meter@^1.0.4: version "1.0.4" @@ -2979,6 +2808,33 @@ string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.0" +string-width@^4.2.2: + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +string.prototype.trimend@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.5.tgz#914a65baaab25fbdd4ee291ca7dde57e869cb8d0" + integrity sha512-I7RGvmjV4pJ7O3kdf+LXFpVfdNOxtCW/2C8f6jNiW4+PQchwxkCDzlk1/7p+Wl4bqFIZeF47qAHXLuHHWKAxog== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.19.5" + +string.prototype.trimstart@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.5.tgz#5466d93ba58cfa2134839f81d7f42437e8c01fef" + integrity sha512-THx16TJCGlsN0o6dl2o6ncWUsdgnLRSA23rRE5pyGBw/mLr3Ej/R2LaqCtgP8VNMGZsvMWnf9ooZPyY2bHvUFg== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.19.5" + string_decoder@^1.1.1: version "1.3.0" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" @@ -3026,6 +2882,13 @@ strip-ansi@^6.0.0: dependencies: ansi-regex "^5.0.0" +strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + strip-json-comments@~2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" @@ -3045,7 +2908,12 @@ supports-color@^7.1.0: dependencies: has-flag "^4.0.0" -tar-fs@^2.0.0: +supports-preserve-symlinks-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" + integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== + +tar-fs@^2.0.0, tar-fs@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-2.1.1.tgz#489a15ab85f1f0befabb370b7de4f9eb5cbe8784" integrity sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng== @@ -3066,21 +2934,11 @@ tar-stream@^2.1.4: inherits "^2.0.3" readable-stream "^3.1.1" -term-size@^2.1.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/term-size/-/term-size-2.2.0.tgz#1f16adedfe9bdc18800e1776821734086fcc6753" - integrity sha512-a6sumDlzyHVJWb8+YofY4TW112G6p2FCPEAFk+59gIYHv3XHRhm9ltVQ9kli4hNWeQBwSpe8cRN25x0ROunMOw== - text-hex@1.0.x: version "1.0.0" resolved "https://registry.yarnpkg.com/text-hex/-/text-hex-1.0.0.tgz#69dc9c1b17446ee79a92bf5b884bb4b9127506f5" integrity sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg== -through@~2.3.4: - version "2.3.8" - resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" - integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= - to-fast-properties@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" @@ -3098,10 +2956,10 @@ to-regex-range@^5.0.1: dependencies: is-number "^7.0.0" -toidentifier@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553" - integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw== +toidentifier@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" + integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== touch@^3.1.0: version "3.1.0" @@ -3119,33 +2977,25 @@ tough-cookie@^4.0.0: punycode "^2.1.1" universalify "^0.1.2" -tough-cookie@~2.5.0: - version "2.5.0" - resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" - integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g== - dependencies: - psl "^1.1.28" - punycode "^2.1.1" +tr46@~0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" + integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== triple-beam@^1.2.0, triple-beam@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/triple-beam/-/triple-beam-1.3.0.tgz#a595214c7298db8339eeeee083e4d10bd8cb8dd9" integrity sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw== -tslib@2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.1.0.tgz#da60860f1c2ecaa5703ab7d39bc05b6bf988b97a" - integrity sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A== - -tslib@^1.10.0, tslib@^1.9.3: +tslib@^1.9.3: version "1.13.0" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.13.0.tgz#c881e13cc7015894ed914862d276436fa9a47043" integrity sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q== -tslib@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.0.1.tgz#410eb0d113e5b6356490eec749603725b021b43e" - integrity sha512-SgIkNheinmEBgx1IUNirK0TUD4X9yjjBRTqqjggWCU3pUEqIk3/Uwl3yRixYKT6WjQuGiwDv4NomL3wqRCj+CQ== +tslib@^2.2.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3" + integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ== tunnel-agent@^0.6.0: version "0.6.0" @@ -3154,16 +3004,11 @@ tunnel-agent@^0.6.0: dependencies: safe-buffer "^5.0.1" -tunnel@^0.0.6, tunnel@~0.0.2: +tunnel@^0.0.6: version "0.0.6" resolved "https://registry.yarnpkg.com/tunnel/-/tunnel-0.0.6.tgz#72f1314b34a5b192db012324df2cc587ca47f92c" integrity sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg== -tweetnacl@^0.14.3, tweetnacl@~0.14.0: - version "0.14.5" - resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" - integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q= - type-check@~0.3.2: version "0.3.2" resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" @@ -3171,12 +3016,12 @@ type-check@~0.3.2: dependencies: prelude-ls "~1.1.2" -type-fest@^0.8.1: - version "0.8.1" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" - integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== +type-fest@^0.20.2: + version "0.20.2" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" + integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== -type-is@~1.6.17, type-is@~1.6.18: +type-is@~1.6.18: version "1.6.18" resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== @@ -3191,22 +3036,20 @@ typedarray-to-buffer@^3.1.5: dependencies: is-typedarray "^1.0.0" -undefsafe@^2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/undefsafe/-/undefsafe-2.0.3.tgz#6b166e7094ad46313b2202da7ecc2cd7cc6e7aae" - integrity sha512-nrXZwwXrD/T/JXeygJqdCO6NZZ1L66HrxM/Z7mIq2oPanoN0F1nLx3lwJMu6AwJY69hdixaFQOuoYsMjE5/C2A== +unbox-primitive@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.2.tgz#29032021057d5e6cdbd08c5129c226dff8ed6f9e" + integrity sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw== dependencies: - debug "^2.2.0" - -underscore@1.4.x: - version "1.4.4" - resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.4.4.tgz#61a6a32010622afa07963bf325203cf12239d604" - integrity sha1-YaajIBBiKvoHljvzJSA88SI51gQ= + call-bind "^1.0.2" + has-bigints "^1.0.2" + has-symbols "^1.0.3" + which-boxed-primitive "^1.0.2" -underscore@^1.8.3: - version "1.10.2" - resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.10.2.tgz#73d6aa3668f3188e4adb0f1943bd12cfd7efaaaf" - integrity sha512-N4P+Q/BuyuEKFJ43B9gYuOj4TQUHXX+j2FqguVOpjkssLUUrnJofCcBccJSCoeturDoZU6GorDTHSvUDlSQbTg== +undefsafe@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/undefsafe/-/undefsafe-2.0.5.tgz#38733b9327bdcd226db889fb723a6efd162e6e2c" + integrity sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA== unique-string@^2.0.0: version "2.0.0" @@ -3235,32 +3078,26 @@ unpipe@1.0.0, unpipe@~1.0.0: resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw= -update-notifier@^4.1.0: - version "4.1.3" - resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-4.1.3.tgz#be86ee13e8ce48fb50043ff72057b5bd598e1ea3" - integrity sha512-Yld6Z0RyCYGB6ckIjffGOSOmHXj1gMeE7aROz4MG+XMkmixBX4jUngrGXNYz7wPKBmtoD4MnBa2Anu7RSKht/A== +update-notifier@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-5.1.0.tgz#4ab0d7c7f36a231dd7316cf7729313f0214d9ad9" + integrity sha512-ItnICHbeMh9GqUy31hFPrD1kcuZ3rpxDZbf4KUDavXwS0bW5m7SLbDQpGX3UYr072cbrF5hFUs3r5tUsPwjfHw== dependencies: - boxen "^4.2.0" - chalk "^3.0.0" + boxen "^5.0.0" + chalk "^4.1.0" configstore "^5.0.1" has-yarn "^2.1.0" import-lazy "^2.1.0" is-ci "^2.0.0" - is-installed-globally "^0.3.1" - is-npm "^4.0.0" + is-installed-globally "^0.4.0" + is-npm "^5.0.0" is-yarn-global "^0.3.0" - latest-version "^5.0.0" - pupa "^2.0.1" + latest-version "^5.1.0" + pupa "^2.1.1" + semver "^7.3.4" semver-diff "^3.1.1" xdg-basedir "^4.0.0" -uri-js@^4.2.2: - version "4.2.2" - resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0" - integrity sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ== - dependencies: - punycode "^2.1.0" - url-parse-lax@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-3.0.0.tgz#16b5cafc07dbe3676c1b1999177823d6503acb0c" @@ -3268,10 +3105,10 @@ url-parse-lax@^3.0.0: dependencies: prepend-http "^2.0.0" -url-parse@~1.4.3: - version "1.4.7" - resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.4.7.tgz#a8a83535e8c00a316e403a5db4ac1b9b853ae278" - integrity sha512-d3uaVyzDB9tQoSXFvuSUNFibTd9zxd2bkVrDRvF5TmvWWQwqE4lgYJ5m+x1DbecWkw+LK4RNl2CU1hHuOKPVlg== +url-parse@~1.5.10: + version "1.5.10" + resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.10.tgz#9d3c2f736c1d75dd3bd2be507dcc111f1e2ea9c1" + integrity sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ== dependencies: querystringify "^2.1.1" requires-port "^1.0.0" @@ -3297,12 +3134,17 @@ util-deprecate@^1.0.1, util-deprecate@~1.0.1: resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= -util@^0.11.1: - version "0.11.1" - resolved "https://registry.yarnpkg.com/util/-/util-0.11.1.tgz#3236733720ec64bb27f6e26f421aaa2e1b588d61" - integrity sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ== +util@^0.12.1: + version "0.12.4" + resolved "https://registry.yarnpkg.com/util/-/util-0.12.4.tgz#66121a31420df8f01ca0c464be15dfa1d1850253" + integrity sha512-bxZ9qtSlGUWSOy9Qa9Xgk11kSslpuZwaxCg4sNIDj6FLucDab2JxnHwyNTCpHMtK1MjoQiWQ6DiUMZYbSrO+Sw== dependencies: - inherits "2.0.3" + inherits "^2.0.3" + is-arguments "^1.0.4" + is-generator-function "^1.0.7" + is-typed-array "^1.1.3" + safe-buffer "^5.1.2" + which-typed-array "^1.1.2" utils-merge@1.0.1: version "1.0.1" @@ -3319,54 +3161,56 @@ uuid-random@^1.3.2: resolved "https://registry.yarnpkg.com/uuid-random/-/uuid-random-1.3.2.tgz#96715edbaef4e84b1dcf5024b00d16f30220e2d0" integrity sha512-UOzej0Le/UgkbWEO8flm+0y+G+ljUon1QWTEZOq1rnMAsxo2+SckbiZdKzAHHlVh6gJqI1TjC/xwgR50MuCrBQ== -uuid@3.3.2: - version "3.3.2" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.2.tgz#1b4af4955eb3077c501c23872fc6513811587131" - integrity sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA== - -uuid@^3.3.2: - version "3.4.0" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" - integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== - -uuid@^8.1.0: - version "8.3.0" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.0.tgz#ab738085ca22dc9a8c92725e459b1d507df5d6ea" - integrity sha512-fX6Z5o4m6XsXBdli9g7DtWgAx+osMsRRZFKma1mIUsLCz6vRvv+pz5VNbyu9UEDzpMWulZfvpgb/cmDXVulYFQ== +uuid@8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.0.0.tgz#bc6ccf91b5ff0ac07bbcdbf1c7c4e150db4dbb6c" + integrity sha512-jOXGuXZAWdsTH7eZLtyXMqUb9EcWMGZNbL9YcGBJl4MH4nrxHmZJhEHvyLFrkxo+28uLb/NYRcStH48fnD0Vzw== -validator@^9.4.1: - version "9.4.1" - resolved "https://registry.yarnpkg.com/validator/-/validator-9.4.1.tgz#abf466d398b561cd243050112c6ff1de6cc12663" - integrity sha512-YV5KjzvRmSyJ1ee/Dm5UED0G+1L4GZnLN3w6/T+zZm8scVua4sOhYKWTUrKa0H/tMiJyO9QLHMPN+9mB/aMunA== +uuid@^8.3.0: + version "8.3.2" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" + integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== vary@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw= -verror@1.10.0: - version "1.10.0" - resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" - integrity sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA= - dependencies: - assert-plus "^1.0.0" - core-util-is "1.0.2" - extsprintf "^1.2.0" +webidl-conversions@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" + integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ== -walkdir@^0.4.0: - version "0.4.1" - resolved "https://registry.yarnpkg.com/walkdir/-/walkdir-0.4.1.tgz#dc119f83f4421df52e3061e514228a2db20afa39" - integrity sha512-3eBwRyEln6E1MSzcxcVpQIhRG8Q1jLvEqRmCZqS3dsfXEDR/AhOF4d+jHg1qvDCpYaVRZjENPQyrVxAkQqxPgQ== +whatwg-url@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" + integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw== + dependencies: + tr46 "~0.0.3" + webidl-conversions "^3.0.0" -which-module@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" - integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= +which-boxed-primitive@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6" + integrity sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg== + dependencies: + is-bigint "^1.0.1" + is-boolean-object "^1.1.0" + is-number-object "^1.0.4" + is-string "^1.0.5" + is-symbol "^1.0.3" -which-pm-runs@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/which-pm-runs/-/which-pm-runs-1.0.0.tgz#670b3afbc552e0b55df6b7780ca74615f23ad1cb" - integrity sha1-Zws6+8VS4LVd9rd4DKdGFfI60cs= +which-typed-array@^1.1.2: + version "1.1.8" + resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.8.tgz#0cfd53401a6f334d90ed1125754a42ed663eb01f" + integrity sha512-Jn4e5PItbcAHyLoRDwvPj1ypu27DJbtdYXUa5zsinrUx77Uvfb0cXwwnGMTn7cjUfhhqgVQnVJCwF+7cgU7tpw== + dependencies: + available-typed-arrays "^1.0.5" + call-bind "^1.0.2" + es-abstract "^1.20.0" + for-each "^0.3.3" + has-tostringtag "^1.0.0" + is-typed-array "^1.1.9" wide-align@^1.1.0: version "1.1.3" @@ -3382,17 +3226,17 @@ widest-line@^3.1.0: dependencies: string-width "^4.0.0" -winston-daily-rotate-file@^4.5.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/winston-daily-rotate-file/-/winston-daily-rotate-file-4.5.0.tgz#3914ac57c4bdae1138170bec85af0c2217b253b1" - integrity sha512-/HqeWiU48dzGqcrABRlxYWVMdL6l3uKCtFSJyrqK+E2rLnSFNsgYpvwx15EgTitBLNzH69lQd/+z2ASryV2aqw== +winston-daily-rotate-file@^4.7.1: + version "4.7.1" + resolved "https://registry.yarnpkg.com/winston-daily-rotate-file/-/winston-daily-rotate-file-4.7.1.tgz#f60a643af87f8867f23170d8cd87dbe3603a625f" + integrity sha512-7LGPiYGBPNyGHLn9z33i96zx/bd71pjBn9tqQzO3I4Tayv94WPmBNwKC7CO1wPHdP9uvu+Md/1nr6VSH9h0iaA== dependencies: - file-stream-rotator "^0.5.7" + file-stream-rotator "^0.6.1" object-hash "^2.0.1" triple-beam "^1.3.0" - winston-transport "^4.2.0" + winston-transport "^4.4.0" -winston-transport@^4.2.0, winston-transport@^4.4.0: +winston-transport@^4.4.0: version "4.4.0" resolved "https://registry.yarnpkg.com/winston-transport/-/winston-transport-4.4.0.tgz#17af518daa690d5b2ecccaa7acf7b20ca7925e59" integrity sha512-Lc7/p3GtqtqPBYYtS6KCN3c77/2QCev51DvcJKbkFPQNoj1sinkGwLGFDxkXY9J6p9+EPnYs+D90uwbnaiURTw== @@ -3400,40 +3244,36 @@ winston-transport@^4.2.0, winston-transport@^4.4.0: readable-stream "^2.3.7" triple-beam "^1.2.0" -winston@^3.3.3: - version "3.3.3" - resolved "https://registry.yarnpkg.com/winston/-/winston-3.3.3.tgz#ae6172042cafb29786afa3d09c8ff833ab7c9170" - integrity sha512-oEXTISQnC8VlSAKf1KYSSd7J6IWuRPQqDdo8eoRNaYKLvwSb5+79Z3Yi1lrl6KDpU6/VWaxpakDAtb1oQ4n9aw== +winston-transport@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/winston-transport/-/winston-transport-4.5.0.tgz#6e7b0dd04d393171ed5e4e4905db265f7ab384fa" + integrity sha512-YpZzcUzBedhlTAfJg6vJDlyEai/IFMIVcaEZZyl3UXIl4gmqRpU7AE89AHLkbzLUsv0NVmw7ts+iztqKxxPW1Q== + dependencies: + logform "^2.3.2" + readable-stream "^3.6.0" + triple-beam "^1.3.0" + +winston@^3.7.2: + version "3.7.2" + resolved "https://registry.yarnpkg.com/winston/-/winston-3.7.2.tgz#95b4eeddbec902b3db1424932ac634f887c400b1" + integrity sha512-QziIqtojHBoyzUOdQvQiar1DH0Xp9nF1A1y7NVy2DGEsz82SBDtOalS0ulTRGVT14xPX3WRWkCsdcJKqNflKng== dependencies: "@dabh/diagnostics" "^2.0.2" - async "^3.1.0" + async "^3.2.3" is-stream "^2.0.0" - logform "^2.2.0" + logform "^2.4.0" one-time "^1.0.0" readable-stream "^3.4.0" + safe-stable-stringify "^2.3.1" stack-trace "0.0.x" triple-beam "^1.3.0" - winston-transport "^4.4.0" - -wns@~0.5.3: - version "0.5.4" - resolved "https://registry.yarnpkg.com/wns/-/wns-0.5.4.tgz#ad8e2ee60e675557da9610d94444a7f59eceaf78" - integrity sha512-WYiJ7khIwUGBD5KAm+YYmwJDDRzFRs4YGAjtbFSoRIdbn9Jcix3p9khJmpvBTXGommaKkvduAn+pc9l4d9yzVQ== + winston-transport "^4.5.0" word-wrap@~1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== -wrap-ansi@^6.2.0: - version "6.2.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" - integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - wrap-ansi@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" @@ -3479,11 +3319,6 @@ xml2js@^0.4.19: sax ">=0.6.0" xmlbuilder "~11.0.0" -xmlbuilder@15.1.1: - version "15.1.1" - resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-15.1.1.tgz#9dcdce49eea66d8d10b42cae94a79c3c8d0c2ec5" - integrity sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg== - xmlbuilder@~11.0.0: version "11.0.1" resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-11.0.1.tgz#be9bae1c8a046e76b31127726347d0ad7002beb3" @@ -3494,11 +3329,6 @@ xmlbuilder@~9.0.1: resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-9.0.7.tgz#132ee63d2ec5565c557e20f4c22df9aca686b10d" integrity sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0= -y18n@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b" - integrity sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w== - y18n@^5.0.5: version "5.0.8" resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" @@ -3509,36 +3339,11 @@ yallist@^4.0.0: resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== -yargs-parser@^18.1.2: - version "18.1.3" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0" - integrity sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ== - dependencies: - camelcase "^5.0.0" - decamelize "^1.2.0" - yargs-parser@^20.2.2: version "20.2.9" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== -yargs@^15.3.1: - version "15.4.1" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.4.1.tgz#0d87a16de01aee9d8bec2bfbf74f67851730f4f8" - integrity sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A== - dependencies: - cliui "^6.0.0" - decamelize "^1.2.0" - find-up "^4.1.0" - get-caller-file "^2.0.1" - require-directory "^2.1.1" - require-main-filename "^2.0.0" - set-blocking "^2.0.0" - string-width "^4.2.0" - which-module "^2.0.0" - y18n "^4.0.0" - yargs-parser "^18.1.2" - yargs@^16.2.0: version "16.2.0" resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" From eab4e333a9c6d4fe487343c8004484d12c0652ff Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Tue, 14 Jun 2022 12:03:05 +0300 Subject: [PATCH 791/798] refactoring: alarm - add Mockito to create --- .../controller/BaseAlarmControllerTest.java | 83 ++++++++++++++++++- 1 file changed, 79 insertions(+), 4 deletions(-) diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseAlarmControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseAlarmControllerTest.java index d82f4175b5..ee1a952546 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseAlarmControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseAlarmControllerTest.java @@ -128,8 +128,14 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest { @Test public void testUpdateAlarmViaDifferentTenant() throws Exception { loginTenantAdmin(); + + Mockito.reset(tbClusterService, auditLogService); + Alarm alarm = createAlarm(TEST_ALARM_TYPE); + testNotifyEntityOne(alarm, alarm.getId(), alarm.getOriginator(), + tenantId, customerId, tenantAdminUserId, TENANT_ADMIN_EMAIL, ActionType.ADDED); + alarm.setSeverity(AlarmSeverity.MAJOR); loginDifferentTenant(); @@ -144,8 +150,14 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest { @Test public void testUpdateAlarmViaDifferentCustomer() throws Exception { loginCustomerUser(); + + Mockito.reset(tbClusterService, auditLogService); + Alarm alarm = createAlarm(TEST_ALARM_TYPE); + testNotifyEntityOne(alarm, alarm.getId(), alarm.getOriginator(), + tenantId, customerId, customerUserId, CUSTOMER_USER_EMAIL, ActionType.ADDED); + loginDifferentCustomer(); alarm.setSeverity(AlarmSeverity.MAJOR); @@ -160,10 +172,14 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest { @Test public void testDeleteAlarmViaCustomer() throws Exception { loginCustomerUser(); - Alarm alarm = createAlarm(TEST_ALARM_TYPE); Mockito.reset(tbClusterService, auditLogService); + Alarm alarm = createAlarm(TEST_ALARM_TYPE); + + testNotifyEntityOne(alarm, alarm.getId(), alarm.getOriginator(), + tenantId, customerId, customerUserId, CUSTOMER_USER_EMAIL, ActionType.ADDED); + doDelete("/api/alarm/" + alarm.getId()).andExpect(status().isOk()); testNotifyEntityOneMsgToEdgeServiceNever(alarm, alarm.getId(), alarm.getOriginator(), @@ -174,10 +190,14 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest { @Test public void testDeleteAlarmViaTenant() throws Exception { loginTenantAdmin(); - Alarm alarm = createAlarm(TEST_ALARM_TYPE); Mockito.reset(tbClusterService, auditLogService); + Alarm alarm = createAlarm(TEST_ALARM_TYPE); + + testNotifyEntityOne(alarm, alarm.getId(), alarm.getOriginator(), + tenantId, customerId, tenantAdminUserId, TENANT_ADMIN_EMAIL, ActionType.ADDED); + doDelete("/api/alarm/" + alarm.getId()).andExpect(status().isOk()); testNotifyEntityOneMsgToEdgeServiceNever(alarm, alarm.getId(), alarm.getOriginator(), @@ -188,8 +208,14 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest { @Test public void testDeleteAlarmViaDifferentTenant() throws Exception { loginTenantAdmin(); + + Mockito.reset(tbClusterService, auditLogService); + Alarm alarm = createAlarm(TEST_ALARM_TYPE); + testNotifyEntityOne(alarm, alarm.getId(), alarm.getOriginator(), + tenantId, customerId, tenantAdminUserId, TENANT_ADMIN_EMAIL, ActionType.ADDED); + loginDifferentTenant(); Mockito.reset(tbClusterService, auditLogService); @@ -204,7 +230,14 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest { @Test public void testDeleteAlarmViaAnotherCustomer() throws Exception { loginCustomerUser(); + + Mockito.reset(tbClusterService, auditLogService); + Alarm alarm = createAlarm(TEST_ALARM_TYPE); + + testNotifyEntityOne(alarm, alarm.getId(), alarm.getOriginator(), + tenantId, customerId, customerUserId, CUSTOMER_USER_EMAIL, ActionType.ADDED); + loginDifferentCustomer(); Mockito.reset(tbClusterService, auditLogService); @@ -219,10 +252,14 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest { @Test public void testClearAlarmViaCustomer() throws Exception { loginCustomerUser(); - Alarm alarm = createAlarm(TEST_ALARM_TYPE); Mockito.reset(tbClusterService, auditLogService); + Alarm alarm = createAlarm(TEST_ALARM_TYPE); + + testNotifyEntityOne(alarm, alarm.getId(), alarm.getOriginator(), + tenantId, customerId, customerUserId, CUSTOMER_USER_EMAIL, ActionType.ADDED); + doPost("/api/alarm/" + alarm.getId() + "/clear").andExpect(status().isOk()); Alarm foundAlarm = doGet("/api/alarm/" + alarm.getId(), Alarm.class); @@ -237,8 +274,14 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest { @Test public void testClearAlarmViaTenant() throws Exception { loginTenantAdmin(); + + Mockito.reset(tbClusterService, auditLogService); + Alarm alarm = createAlarm(TEST_ALARM_TYPE); + testNotifyEntityOne(alarm, alarm.getId(), alarm.getOriginator(), + tenantId, customerId, tenantAdminUserId, TENANT_ADMIN_EMAIL, ActionType.ADDED); + Mockito.reset(tbClusterService, auditLogService); doPost("/api/alarm/" + alarm.getId() + "/clear").andExpect(status().isOk()); @@ -254,10 +297,14 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest { @Test public void testAcknowledgeAlarmViaCustomer() throws Exception { loginCustomerUser(); - Alarm alarm = createAlarm(TEST_ALARM_TYPE); Mockito.reset(tbClusterService, auditLogService); + Alarm alarm = createAlarm(TEST_ALARM_TYPE); + + testNotifyEntityOne(alarm, alarm.getId(), alarm.getOriginator(), + tenantId, customerId, customerUserId, CUSTOMER_USER_EMAIL, ActionType.ADDED); + doPost("/api/alarm/" + alarm.getId() + "/ack").andExpect(status().isOk()); Alarm foundAlarm = doGet("/api/alarm/" + alarm.getId(), Alarm.class); @@ -272,7 +319,14 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest { @Test public void testClearAlarmViaDifferentCustomer() throws Exception { loginCustomerUser(); + + Mockito.reset(tbClusterService, auditLogService); + Alarm alarm = createAlarm(TEST_ALARM_TYPE); + + testNotifyEntityOne(alarm, alarm.getId(), alarm.getOriginator(), + tenantId, customerId, customerUserId, CUSTOMER_USER_EMAIL, ActionType.ADDED); + loginDifferentCustomer(); Mockito.reset(tbClusterService, auditLogService); @@ -286,7 +340,14 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest { @Test public void testClearAlarmViaDifferentTenant() throws Exception { loginTenantAdmin(); + + Mockito.reset(tbClusterService, auditLogService); + Alarm alarm = createAlarm(TEST_ALARM_TYPE); + + testNotifyEntityOne(alarm, alarm.getId(), alarm.getOriginator(), + tenantId, customerId, tenantAdminUserId, TENANT_ADMIN_EMAIL, ActionType.ADDED); + loginDifferentTenant(); Mockito.reset(tbClusterService, auditLogService); @@ -300,7 +361,14 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest { @Test public void testAcknowledgeAlarmViaDifferentCustomer() throws Exception { loginCustomerUser(); + + Mockito.reset(tbClusterService, auditLogService); + Alarm alarm = createAlarm(TEST_ALARM_TYPE); + + testNotifyEntityOne(alarm, alarm.getId(), alarm.getOriginator(), + tenantId, customerId, customerUserId, CUSTOMER_USER_EMAIL, ActionType.ADDED); + loginDifferentCustomer(); Mockito.reset(tbClusterService, auditLogService); @@ -314,7 +382,14 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest { @Test public void testAcknowledgeAlarmViaDifferentTenant() throws Exception { loginTenantAdmin(); + + Mockito.reset(tbClusterService, auditLogService); + Alarm alarm = createAlarm(TEST_ALARM_TYPE); + + testNotifyEntityOne(alarm, alarm.getId(), alarm.getOriginator(), + tenantId, customerId, tenantAdminUserId, TENANT_ADMIN_EMAIL, ActionType.ADDED); + loginDifferentTenant(); Mockito.reset(tbClusterService, auditLogService); From 8bccd857ebd741ade07e3c5f82ea2e5de83ea56c Mon Sep 17 00:00:00 2001 From: fe-dev Date: Tue, 14 Jun 2022 12:06:46 +0300 Subject: [PATCH 792/798] UI: Add input placeholder --- .../authentication-dialog/sms-auth-dialog.component.html | 6 +++++- ui-ngx/src/app/shared/components/phone-input.component.html | 4 ++-- ui-ngx/src/app/shared/components/phone-input.component.ts | 1 + 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/ui-ngx/src/app/modules/home/pages/security/authentication-dialog/sms-auth-dialog.component.html b/ui-ngx/src/app/modules/home/pages/security/authentication-dialog/sms-auth-dialog.component.html index bb547f56a3..e216c22d3e 100644 --- a/ui-ngx/src/app/modules/home/pages/security/authentication-dialog/sms-auth-dialog.component.html +++ b/ui-ngx/src/app/modules/home/pages/security/authentication-dialog/sms-auth-dialog.component.html @@ -37,7 +37,11 @@

security.2fa.dialog.sms-step-description

- + +
- security.2fa.dialog.sms-step-label + {{ placeholder | translate }} = this.countryCodeData.allCountries; phonePlaceholder: string; From 6d2d876d64c81ffe4320378b48891a9fd952bf09 Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Tue, 14 Jun 2022 14:45:42 +0300 Subject: [PATCH 793/798] refactoring: commit1 --- .../controller/AbstractNotifyEntityTest.java | 4 - .../controller/BaseAlarmControllerTest.java | 75 +------------ .../BaseCustomerControllerTest.java | 105 +++++++++--------- 3 files changed, 57 insertions(+), 127 deletions(-) diff --git a/application/src/test/java/org/thingsboard/server/controller/AbstractNotifyEntityTest.java b/application/src/test/java/org/thingsboard/server/controller/AbstractNotifyEntityTest.java index 6fb7b93bae..40a3bac5de 100644 --- a/application/src/test/java/org/thingsboard/server/controller/AbstractNotifyEntityTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/AbstractNotifyEntityTest.java @@ -56,7 +56,6 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest { Mockito.reset(tbClusterService, auditLogService); } - protected void testNotifyEntityDeleteOneMsgToEdgeServiceNever(HasName entity, EntityId entityId, EntityId originatorId, TenantId tenantId, CustomerId customerId, UserId userId, String userName, ActionType actionType, Object... additionalInfo) { @@ -64,7 +63,6 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest { testLogEntityActionOne(entity, originatorId, tenantId, customerId, userId, userName, actionType, additionalInfo); testPushMsgToRuleEngineOne(entityId, tenantId); testBroadcastEntityStateChangeEventOne(entityId, tenantId); - } protected void testNotifyEntityOneMsgToEdgeServiceNever(HasName entity, EntityId entityId, EntityId originatorId, @@ -172,6 +170,4 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest { } return result; } - - } diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseAlarmControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseAlarmControllerTest.java index ee1a952546..03d1223cc0 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseAlarmControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseAlarmControllerTest.java @@ -87,12 +87,9 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest { public void testUpdateAlarmViaCustomer() throws Exception { loginCustomerUser(); - Mockito.reset(tbClusterService, auditLogService); - Alarm alarm = createAlarm(TEST_ALARM_TYPE); - testNotifyEntityOne(alarm, alarm.getId(), alarm.getOriginator(), - tenantId, customerId, customerUserId, CUSTOMER_USER_EMAIL, ActionType.ADDED); + Mockito.reset(tbClusterService, auditLogService); alarm.setSeverity(AlarmSeverity.MAJOR); Alarm updatedAlarm = doPost("/api/alarm", alarm, Alarm.class); @@ -108,12 +105,9 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest { public void testUpdateAlarmViaTenant() throws Exception { loginTenantAdmin(); - Mockito.reset(tbClusterService, auditLogService); - Alarm alarm = createAlarm(TEST_ALARM_TYPE); - testNotifyEntityOne(alarm, alarm.getId(), alarm.getOriginator(), - tenantId, customerId, tenantAdminUserId, TENANT_ADMIN_EMAIL, ActionType.ADDED); + Mockito.reset(tbClusterService, auditLogService); alarm.setSeverity(AlarmSeverity.MAJOR); Alarm updatedAlarm = doPost("/api/alarm", alarm, Alarm.class); @@ -129,13 +123,8 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest { public void testUpdateAlarmViaDifferentTenant() throws Exception { loginTenantAdmin(); - Mockito.reset(tbClusterService, auditLogService); - Alarm alarm = createAlarm(TEST_ALARM_TYPE); - testNotifyEntityOne(alarm, alarm.getId(), alarm.getOriginator(), - tenantId, customerId, tenantAdminUserId, TENANT_ADMIN_EMAIL, ActionType.ADDED); - alarm.setSeverity(AlarmSeverity.MAJOR); loginDifferentTenant(); @@ -151,13 +140,8 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest { public void testUpdateAlarmViaDifferentCustomer() throws Exception { loginCustomerUser(); - Mockito.reset(tbClusterService, auditLogService); - Alarm alarm = createAlarm(TEST_ALARM_TYPE); - testNotifyEntityOne(alarm, alarm.getId(), alarm.getOriginator(), - tenantId, customerId, customerUserId, CUSTOMER_USER_EMAIL, ActionType.ADDED); - loginDifferentCustomer(); alarm.setSeverity(AlarmSeverity.MAJOR); @@ -173,12 +157,9 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest { public void testDeleteAlarmViaCustomer() throws Exception { loginCustomerUser(); - Mockito.reset(tbClusterService, auditLogService); - Alarm alarm = createAlarm(TEST_ALARM_TYPE); - testNotifyEntityOne(alarm, alarm.getId(), alarm.getOriginator(), - tenantId, customerId, customerUserId, CUSTOMER_USER_EMAIL, ActionType.ADDED); + Mockito.reset(tbClusterService, auditLogService); doDelete("/api/alarm/" + alarm.getId()).andExpect(status().isOk()); @@ -191,12 +172,9 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest { public void testDeleteAlarmViaTenant() throws Exception { loginTenantAdmin(); - Mockito.reset(tbClusterService, auditLogService); - Alarm alarm = createAlarm(TEST_ALARM_TYPE); - testNotifyEntityOne(alarm, alarm.getId(), alarm.getOriginator(), - tenantId, customerId, tenantAdminUserId, TENANT_ADMIN_EMAIL, ActionType.ADDED); + Mockito.reset(tbClusterService, auditLogService); doDelete("/api/alarm/" + alarm.getId()).andExpect(status().isOk()); @@ -209,13 +187,8 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest { public void testDeleteAlarmViaDifferentTenant() throws Exception { loginTenantAdmin(); - Mockito.reset(tbClusterService, auditLogService); - Alarm alarm = createAlarm(TEST_ALARM_TYPE); - testNotifyEntityOne(alarm, alarm.getId(), alarm.getOriginator(), - tenantId, customerId, tenantAdminUserId, TENANT_ADMIN_EMAIL, ActionType.ADDED); - loginDifferentTenant(); Mockito.reset(tbClusterService, auditLogService); @@ -231,13 +204,8 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest { public void testDeleteAlarmViaAnotherCustomer() throws Exception { loginCustomerUser(); - Mockito.reset(tbClusterService, auditLogService); - Alarm alarm = createAlarm(TEST_ALARM_TYPE); - testNotifyEntityOne(alarm, alarm.getId(), alarm.getOriginator(), - tenantId, customerId, customerUserId, CUSTOMER_USER_EMAIL, ActionType.ADDED); - loginDifferentCustomer(); Mockito.reset(tbClusterService, auditLogService); @@ -253,12 +221,9 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest { public void testClearAlarmViaCustomer() throws Exception { loginCustomerUser(); - Mockito.reset(tbClusterService, auditLogService); - Alarm alarm = createAlarm(TEST_ALARM_TYPE); - testNotifyEntityOne(alarm, alarm.getId(), alarm.getOriginator(), - tenantId, customerId, customerUserId, CUSTOMER_USER_EMAIL, ActionType.ADDED); + Mockito.reset(tbClusterService, auditLogService); doPost("/api/alarm/" + alarm.getId() + "/clear").andExpect(status().isOk()); @@ -275,13 +240,8 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest { public void testClearAlarmViaTenant() throws Exception { loginTenantAdmin(); - Mockito.reset(tbClusterService, auditLogService); - Alarm alarm = createAlarm(TEST_ALARM_TYPE); - testNotifyEntityOne(alarm, alarm.getId(), alarm.getOriginator(), - tenantId, customerId, tenantAdminUserId, TENANT_ADMIN_EMAIL, ActionType.ADDED); - Mockito.reset(tbClusterService, auditLogService); doPost("/api/alarm/" + alarm.getId() + "/clear").andExpect(status().isOk()); @@ -298,12 +258,9 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest { public void testAcknowledgeAlarmViaCustomer() throws Exception { loginCustomerUser(); - Mockito.reset(tbClusterService, auditLogService); - Alarm alarm = createAlarm(TEST_ALARM_TYPE); - testNotifyEntityOne(alarm, alarm.getId(), alarm.getOriginator(), - tenantId, customerId, customerUserId, CUSTOMER_USER_EMAIL, ActionType.ADDED); + Mockito.reset(tbClusterService, auditLogService); doPost("/api/alarm/" + alarm.getId() + "/ack").andExpect(status().isOk()); @@ -320,13 +277,8 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest { public void testClearAlarmViaDifferentCustomer() throws Exception { loginCustomerUser(); - Mockito.reset(tbClusterService, auditLogService); - Alarm alarm = createAlarm(TEST_ALARM_TYPE); - testNotifyEntityOne(alarm, alarm.getId(), alarm.getOriginator(), - tenantId, customerId, customerUserId, CUSTOMER_USER_EMAIL, ActionType.ADDED); - loginDifferentCustomer(); Mockito.reset(tbClusterService, auditLogService); @@ -341,13 +293,8 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest { public void testClearAlarmViaDifferentTenant() throws Exception { loginTenantAdmin(); - Mockito.reset(tbClusterService, auditLogService); - Alarm alarm = createAlarm(TEST_ALARM_TYPE); - testNotifyEntityOne(alarm, alarm.getId(), alarm.getOriginator(), - tenantId, customerId, tenantAdminUserId, TENANT_ADMIN_EMAIL, ActionType.ADDED); - loginDifferentTenant(); Mockito.reset(tbClusterService, auditLogService); @@ -362,13 +309,8 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest { public void testAcknowledgeAlarmViaDifferentCustomer() throws Exception { loginCustomerUser(); - Mockito.reset(tbClusterService, auditLogService); - Alarm alarm = createAlarm(TEST_ALARM_TYPE); - testNotifyEntityOne(alarm, alarm.getId(), alarm.getOriginator(), - tenantId, customerId, customerUserId, CUSTOMER_USER_EMAIL, ActionType.ADDED); - loginDifferentCustomer(); Mockito.reset(tbClusterService, auditLogService); @@ -383,13 +325,8 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest { public void testAcknowledgeAlarmViaDifferentTenant() throws Exception { loginTenantAdmin(); - Mockito.reset(tbClusterService, auditLogService); - Alarm alarm = createAlarm(TEST_ALARM_TYPE); - testNotifyEntityOne(alarm, alarm.getId(), alarm.getOriginator(), - tenantId, customerId, tenantAdminUserId, TENANT_ADMIN_EMAIL, ActionType.ADDED); - loginDifferentTenant(); Mockito.reset(tbClusterService, auditLogService); diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseCustomerControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseCustomerControllerTest.java index 0aa81c2d18..2ad239f354 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseCustomerControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseCustomerControllerTest.java @@ -93,14 +93,16 @@ public abstract class BaseCustomerControllerTest extends AbstractControllerTest Mockito.reset(tbClusterService, auditLogService); Customer savedCustomer = doPost("/api/customer", customer, Customer.class); + + testNotifyEntityOneMsgToEdgeServiceNever(savedCustomer, savedCustomer.getId(), savedCustomer.getId(), + savedCustomer.getTenantId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), + ActionType.ADDED); + Assert.assertNotNull(savedCustomer); Assert.assertNotNull(savedCustomer.getId()); Assert.assertTrue(savedCustomer.getCreatedTime() > 0); Assert.assertEquals(customer.getTitle(), savedCustomer.getTitle()); - testNotifyEntityOneMsgToEdgeServiceNever(savedCustomer, savedCustomer.getId(), savedCustomer.getId(), - savedCustomer.getTenantId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), - tenantAdmin.getEmail(), ActionType.ADDED); savedCustomer.setTitle("My new customer"); @@ -108,8 +110,9 @@ public abstract class BaseCustomerControllerTest extends AbstractControllerTest doPost("/api/customer", savedCustomer, Customer.class); - testNotifyEntityOne(savedCustomer, savedCustomer.getId(), savedCustomer.getId(), - savedCustomer.getTenantId(), savedCustomer.getId(), tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.UPDATED); + testNotifyEntityOne(savedCustomer, savedCustomer.getId(), savedCustomer.getId(), savedCustomer.getTenantId(), + savedCustomer.getId(), tenantAdmin.getId(), tenantAdmin.getEmail(), + ActionType.UPDATED); Customer foundCustomer = doGet("/api/customer/" + savedCustomer.getId().getId().toString(), Customer.class); Assert.assertEquals(foundCustomer.getTitle(), savedCustomer.getTitle()); @@ -117,89 +120,65 @@ public abstract class BaseCustomerControllerTest extends AbstractControllerTest doDelete("/api/customer/" + savedCustomer.getId().getId().toString()) .andExpect(status().isOk()); - testNotifyEntityBroadcastEntityStateChangeEventOneMsgToEdgeServiceNever(savedCustomer, savedCustomer.getId(), savedCustomer.getId(), - savedCustomer.getTenantId(), savedCustomer.getId(), tenantAdmin.getId(), tenantAdmin.getEmail(), - ActionType.DELETED, savedCustomer.getId().getId().toString()); + testNotifyEntityBroadcastEntityStateChangeEventOneMsgToEdgeServiceNever(savedCustomer, savedCustomer.getId(), + savedCustomer.getId(), savedCustomer.getTenantId(), savedCustomer.getId(), tenantAdmin.getId(), + tenantAdmin.getEmail(), ActionType.DELETED, savedCustomer.getId().getId().toString()); } @Test public void testSaveCustomerWithViolationOfValidation() throws Exception { Customer customer = new Customer(); + customer.setTitle(RandomStringUtils.randomAlphabetic(300)); Mockito.reset(tbClusterService, auditLogService); - doPost("/api/customer", customer) - .andExpect(status().isBadRequest()) - .andExpect(statusReason(containsString("Customer title should be specified"))); - - testNotifyEntityError(customer, savedTenant.getId(), - tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, new DataValidationException("Test: empty title")); - - customer.setTitle(RandomStringUtils.randomAlphabetic(300)); - doPost("/api/customer", customer).andExpect(statusReason(containsString("length of title must be equal or less than 255"))); testNotifyEntityError(customer, savedTenant.getId(), - tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, new DataValidationException("Test: bad title")); + tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, new DataValidationException("")); customer.setTitle("Normal title"); - customer.setEmail("invalid@mail"); - doPost("/api/customer", customer) - .andExpect(status().isBadRequest()) - .andExpect(statusReason(containsString("Invalid email address format 'invalid@mail'"))); - - testNotifyEntityError(customer, savedTenant.getId(), - tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, new DataValidationException("Test: invalid email")); - - customer.setEmail("normal@mail.com.ua"); - customer.setCity(RandomStringUtils.randomAlphabetic(300)); + doPost("/api/customer", customer).andExpect(statusReason(containsString("length of city must be equal or less than 255"))); testNotifyEntityError(customer, savedTenant.getId(), - tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, new DataValidationException("Test: bad City")); + tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, new DataValidationException("")); customer.setCity("Normal city"); customer.setCountry(RandomStringUtils.randomAlphabetic(300)); doPost("/api/customer", customer).andExpect(statusReason(containsString("length of country must be equal or less than 255"))); testNotifyEntityError(customer, savedTenant.getId(), - tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, new DataValidationException("Test: bad Country")); + tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, new DataValidationException("")); customer.setCountry("Ukraine"); customer.setPhone(RandomStringUtils.randomAlphabetic(300)); doPost("/api/customer", customer).andExpect(statusReason(containsString("length of phone must be equal or less than 255"))); testNotifyEntityError(customer, savedTenant.getId(), - tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, new DataValidationException("Test: bad Phone")); + tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, new DataValidationException("")); customer.setPhone("+3892555554512"); customer.setState(RandomStringUtils.randomAlphabetic(300)); doPost("/api/customer", customer).andExpect(statusReason(containsString("length of state must be equal or less than 255"))); testNotifyEntityError(customer, savedTenant.getId(), - tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, new DataValidationException("Test: bad state")); + tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, new DataValidationException("")); customer.setState("Normal state"); customer.setZip(RandomStringUtils.randomAlphabetic(300)); doPost("/api/customer", customer).andExpect(statusReason(containsString("length of zip or postal code must be equal or less than 255"))); testNotifyEntityError(customer, savedTenant.getId(), - tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, new DataValidationException("Test: bad Zip")); + tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, new DataValidationException("")); } @Test public void testUpdateCustomerFromDifferentTenant() throws Exception { Customer customer = new Customer(); customer.setTitle("My customer"); - - Mockito.reset(tbClusterService, auditLogService); - Customer savedCustomer = doPost("/api/customer", customer, Customer.class); - - testNotifyEntityOneMsgToEdgeServiceNever(savedCustomer, savedCustomer.getId(), savedCustomer.getId(), - savedCustomer.getTenantId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED); - doPost("/api/customer", savedCustomer, Customer.class); loginDifferentTenant(); @@ -211,7 +190,6 @@ public abstract class BaseCustomerControllerTest extends AbstractControllerTest testNotifyEntityNever(savedCustomer.getId(), savedCustomer); deleteDifferentTenant(); - login(tenantAdmin.getName(), "testPassword1"); Mockito.reset(tbClusterService, auditLogService); @@ -228,25 +206,20 @@ public abstract class BaseCustomerControllerTest extends AbstractControllerTest public void testFindCustomerById() throws Exception { Customer customer = new Customer(); customer.setTitle("My customer"); - - Mockito.reset(tbClusterService, auditLogService); - Customer savedCustomer = doPost("/api/customer", customer, Customer.class); - testNotifyEntityOneMsgToEdgeServiceNever(savedCustomer, savedCustomer.getId(), savedCustomer.getId(), - savedCustomer.getTenantId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), - tenantAdmin.getEmail(), ActionType.ADDED); - Customer foundCustomer = doGet("/api/customer/" + savedCustomer.getId().getId().toString(), Customer.class); Assert.assertNotNull(foundCustomer); Assert.assertEquals(savedCustomer, foundCustomer); + Mockito.reset(tbClusterService, auditLogService); + doDelete("/api/customer/" + savedCustomer.getId().getId().toString()) .andExpect(status().isOk()); - testNotifyEntityBroadcastEntityStateChangeEventOneMsgToEdgeServiceNever(savedCustomer, savedCustomer.getId(), savedCustomer.getId(), - savedCustomer.getTenantId(), savedCustomer.getId(), tenantAdmin.getId(), tenantAdmin.getEmail(), - ActionType.DELETED, savedCustomer.getId().getId().toString()); + testNotifyEntityBroadcastEntityStateChangeEventOneMsgToEdgeServiceNever(savedCustomer, savedCustomer.getId(), + savedCustomer.getId(), savedCustomer.getTenantId(), savedCustomer.getId(), tenantAdmin.getId(), + tenantAdmin.getEmail(), ActionType.DELETED, savedCustomer.getId().getId().toString()); } @Test @@ -260,14 +233,38 @@ public abstract class BaseCustomerControllerTest extends AbstractControllerTest doDelete("/api/customer/" + savedCustomer.getId().getId().toString()) .andExpect(status().isOk()); - testNotifyEntityBroadcastEntityStateChangeEventOneMsgToEdgeServiceNever(savedCustomer, savedCustomer.getId(), savedCustomer.getId(), - savedCustomer.getTenantId(), savedCustomer.getId(), tenantAdmin.getId(), tenantAdmin.getEmail(), - ActionType.DELETED, savedCustomer.getId().getId().toString()); + testNotifyEntityBroadcastEntityStateChangeEventOneMsgToEdgeServiceNever(savedCustomer, savedCustomer.getId(), + savedCustomer.getId(), savedCustomer.getTenantId(), savedCustomer.getId(), tenantAdmin.getId(), + tenantAdmin.getEmail(), ActionType.DELETED, savedCustomer.getId().getId().toString()); doGet("/api/customer/" + savedCustomer.getId().getId().toString()) .andExpect(status().isNotFound()); } + @Test + public void testSaveCustomerWithEmptyTitle() throws Exception { + Customer customer = new Customer(); + doPost("/api/customer", customer) + .andExpect(status().isBadRequest()) + .andExpect(statusReason(containsString("Customer title should be specified"))); + } + + @Test + public void testSaveCustomerWithInvalidEmail() throws Exception { + Customer customer = new Customer(); + customer.setTitle("My customer"); + customer.setEmail("invalid@mail"); + + Mockito.reset(tbClusterService, auditLogService); + + doPost("/api/customer", customer) + .andExpect(status().isBadRequest()) + .andExpect(statusReason(containsString("Invalid email address format 'invalid@mail'"))); + + testNotifyEntityError(customer, savedTenant.getId(), + tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, new DataValidationException("")); + } + @Test public void testFindCustomers() throws Exception { TenantId tenantId = savedTenant.getId(); From 51e05d1a9ad7bc60d824949d57639c56dc4d0666 Mon Sep 17 00:00:00 2001 From: fe-dev Date: Tue, 14 Jun 2022 16:14:01 +0300 Subject: [PATCH 794/798] UI: Refactoring phone input and add to test sms dialog and contact --- .../admin/send-test-sms-dialog.component.html | 16 ++++------ .../sms-auth-dialog.component.html | 2 +- .../shared/components/contact.component.html | 12 ++++---- .../components/phone-input.component.html | 2 +- .../components/phone-input.component.scss | 1 - .../components/phone-input.component.ts | 30 ++++++++++++++----- 6 files changed, 35 insertions(+), 28 deletions(-) diff --git a/ui-ngx/src/app/modules/home/pages/admin/send-test-sms-dialog.component.html b/ui-ngx/src/app/modules/home/pages/admin/send-test-sms-dialog.component.html index cc7b4d21f3..40cc60f730 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/send-test-sms-dialog.component.html +++ b/ui-ngx/src/app/modules/home/pages/admin/send-test-sms-dialog.component.html @@ -30,17 +30,11 @@
- - admin.number-to - - - {{ 'admin.number-to-required' | translate }} - - - {{ 'admin.phone-number-pattern' | translate }} - - - + + admin.sms-message diff --git a/ui-ngx/src/app/modules/home/pages/security/authentication-dialog/sms-auth-dialog.component.html b/ui-ngx/src/app/modules/home/pages/security/authentication-dialog/sms-auth-dialog.component.html index e216c22d3e..aace1d7152 100644 --- a/ui-ngx/src/app/modules/home/pages/security/authentication-dialog/sms-auth-dialog.component.html +++ b/ui-ngx/src/app/modules/home/pages/security/authentication-dialog/sms-auth-dialog.component.html @@ -36,7 +36,7 @@ {{ 'security.2fa.dialog.sms-step-label' | translate }}

security.2fa.dialog.sms-step-description

-
+
contact.address2 - - contact.phone - - - {{ 'contact.phone-max-length' | translate }} - - + + contact.email diff --git a/ui-ngx/src/app/shared/components/phone-input.component.html b/ui-ngx/src/app/shared/components/phone-input.component.html index c5b2ae7bc1..83db450d70 100644 --- a/ui-ngx/src/app/shared/components/phone-input.component.html +++ b/ui-ngx/src/app/shared/components/phone-input.component.html @@ -28,7 +28,7 @@
- {{ placeholder | translate }} + {{ label | translate }} = this.countryCodeData.allCountries; phonePlaceholder: string; From c9558b5e08916882ba41aabb0bc23d67548a68f0 Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Tue, 14 Jun 2022 16:59:21 +0300 Subject: [PATCH 795/798] refactoring: commit2 (error) --- .../controller/AbstractNotifyEntityTest.java | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/application/src/test/java/org/thingsboard/server/controller/AbstractNotifyEntityTest.java b/application/src/test/java/org/thingsboard/server/controller/AbstractNotifyEntityTest.java index 40a3bac5de..38139d91d4 100644 --- a/application/src/test/java/org/thingsboard/server/controller/AbstractNotifyEntityTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/AbstractNotifyEntityTest.java @@ -32,6 +32,8 @@ import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.dao.audit.AuditLogService; import org.thingsboard.server.dao.model.ModelConstants; +import java.io.PrintWriter; +import java.io.StringWriter; import java.util.Locale; import static org.mockito.Mockito.never; @@ -96,12 +98,14 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest { Mockito.verify(auditLogService, times(1)).logEntityAction(Mockito.eq(tenantId), Mockito.eq(customer_NULL_UUID), Mockito.eq(userId), Mockito.eq(userName), Mockito.eq(entity_NULL_UUID), Mockito.any(entity.getClass()), Mockito.eq(actionType), - Mockito.any(exp.getClass()), Mockito.eq(additionalInfo)); + Mockito.argThat(argument -> + argument.getMessage().equals(exp.getMessage())), Mockito.eq(additionalInfo)); } else { Mockito.verify(auditLogService, times(1)).logEntityAction(Mockito.eq(tenantId), Mockito.eq(customer_NULL_UUID), Mockito.eq(userId), Mockito.eq(userName), Mockito.eq(entity_NULL_UUID), Mockito.any(entity.getClass()), Mockito.eq(actionType), - Mockito.any(exp.getClass())); + Mockito.argThat(argument -> + argument.getMessage().equals(exp.getMessage()))); } Mockito.verify(tbClusterService, never()).pushMsgToRuleEngine(Mockito.any(), Mockito.any(entity_NULL_UUID.getClass()), Mockito.any(), Mockito.any()); @@ -170,4 +174,10 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest { } return result; } + + private String getFailureStack(Exception e) { + StringWriter sw = new StringWriter(); + e.printStackTrace(new PrintWriter(sw)); + return sw.toString(); + } } From 281c266d26d0d73c6a9379f006e855ddf7adf7ef Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Tue, 14 Jun 2022 17:00:55 +0300 Subject: [PATCH 796/798] refactoring: commit3 (error) --- .../BaseCustomerControllerTest.java | 47 ++++++++++++------- 1 file changed, 31 insertions(+), 16 deletions(-) diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseCustomerControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseCustomerControllerTest.java index 2ad239f354..c3ae7d76f9 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseCustomerControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseCustomerControllerTest.java @@ -128,50 +128,57 @@ public abstract class BaseCustomerControllerTest extends AbstractControllerTest @Test public void testSaveCustomerWithViolationOfValidation() throws Exception { Customer customer = new Customer(); + String validationError = "Validation error: "; customer.setTitle(RandomStringUtils.randomAlphabetic(300)); Mockito.reset(tbClusterService, auditLogService); - doPost("/api/customer", customer).andExpect(statusReason(containsString("length of title must be equal or less than 255"))); + String msgError = "length of title must be equal or less than 255"; + doPost("/api/customer", customer).andExpect(statusReason(containsString(msgError))); testNotifyEntityError(customer, savedTenant.getId(), - tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, new DataValidationException("")); + tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, new DataValidationException(validationError + msgError)); customer.setTitle("Normal title"); customer.setCity(RandomStringUtils.randomAlphabetic(300)); - - doPost("/api/customer", customer).andExpect(statusReason(containsString("length of city must be equal or less than 255"))); + msgError = "length of city must be equal or less than 255"; + doPost("/api/customer", customer).andExpect(statusReason(containsString(msgError))); testNotifyEntityError(customer, savedTenant.getId(), - tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, new DataValidationException("")); + tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, new DataValidationException(validationError + msgError)); customer.setCity("Normal city"); customer.setCountry(RandomStringUtils.randomAlphabetic(300)); - doPost("/api/customer", customer).andExpect(statusReason(containsString("length of country must be equal or less than 255"))); + msgError = "length of country must be equal or less than 255"; + doPost("/api/customer", customer).andExpect(statusReason(containsString(msgError))); testNotifyEntityError(customer, savedTenant.getId(), - tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, new DataValidationException("")); + tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, new DataValidationException(validationError + msgError)); customer.setCountry("Ukraine"); customer.setPhone(RandomStringUtils.randomAlphabetic(300)); - doPost("/api/customer", customer).andExpect(statusReason(containsString("length of phone must be equal or less than 255"))); + msgError = "length of phone must be equal or less than 255"; + doPost("/api/customer", customer).andExpect(statusReason(containsString(msgError))); testNotifyEntityError(customer, savedTenant.getId(), - tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, new DataValidationException("")); + tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, new DataValidationException(validationError + msgError)); customer.setPhone("+3892555554512"); customer.setState(RandomStringUtils.randomAlphabetic(300)); - doPost("/api/customer", customer).andExpect(statusReason(containsString("length of state must be equal or less than 255"))); + msgError = "length of state must be equal or less than 255"; + doPost("/api/customer", customer).andExpect(statusReason(containsString(msgError))); testNotifyEntityError(customer, savedTenant.getId(), - tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, new DataValidationException("")); + tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, new DataValidationException(validationError + msgError)); customer.setState("Normal state"); customer.setZip(RandomStringUtils.randomAlphabetic(300)); - doPost("/api/customer", customer).andExpect(statusReason(containsString("length of zip or postal code must be equal or less than 255"))); + msgError = "length of zip or postal code must be equal or less than 255"; + doPost("/api/customer", customer).andExpect(statusReason(containsString(msgError))); testNotifyEntityError(customer, savedTenant.getId(), - tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, new DataValidationException("")); + tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, new DataValidationException(validationError + msgError)); + } @Test @@ -244,14 +251,22 @@ public abstract class BaseCustomerControllerTest extends AbstractControllerTest @Test public void testSaveCustomerWithEmptyTitle() throws Exception { Customer customer = new Customer(); + String msgError = "Customer title should be specified"; + + Mockito.reset(tbClusterService, auditLogService); + doPost("/api/customer", customer) .andExpect(status().isBadRequest()) - .andExpect(statusReason(containsString("Customer title should be specified"))); + .andExpect(statusReason(containsString(msgError))); + + testNotifyEntityError(customer, savedTenant.getId(), + tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, new DataValidationException(msgError + "!")); } @Test public void testSaveCustomerWithInvalidEmail() throws Exception { Customer customer = new Customer(); + String msgError = "Invalid email address format 'invalid@mail'"; customer.setTitle("My customer"); customer.setEmail("invalid@mail"); @@ -259,10 +274,10 @@ public abstract class BaseCustomerControllerTest extends AbstractControllerTest doPost("/api/customer", customer) .andExpect(status().isBadRequest()) - .andExpect(statusReason(containsString("Invalid email address format 'invalid@mail'"))); + .andExpect(statusReason(containsString(msgError))); testNotifyEntityError(customer, savedTenant.getId(), - tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, new DataValidationException("")); + tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, new DataValidationException(msgError + "!")); } @Test From 835e8d5d1eb1d8e430655858f8e255421e482f18 Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Wed, 15 Jun 2022 12:57:14 +0300 Subject: [PATCH 797/798] refactoring: commit4 --- .../server/controller/AlarmController.java | 2 +- .../DefaultTbNotificationEntityService.java | 24 +++++++++++++++++-- .../entitiy/alarm/DefaultTbAlarmService.java | 10 ++++---- .../service/entitiy/alarm/TbAlarmService.java | 3 +-- 4 files changed, 28 insertions(+), 11 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/controller/AlarmController.java b/application/src/main/java/org/thingsboard/server/controller/AlarmController.java index 2651277ccc..939e9da8a0 100644 --- a/application/src/main/java/org/thingsboard/server/controller/AlarmController.java +++ b/application/src/main/java/org/thingsboard/server/controller/AlarmController.java @@ -147,7 +147,7 @@ public class AlarmController extends BaseController { try { AlarmId alarmId = new AlarmId(toUUID(strAlarmId)); Alarm alarm = checkAlarmId(alarmId, Operation.WRITE); - return tbAlarmService.delete(alarm, getCurrentUser().getCustomerId(), getCurrentUser()); + return tbAlarmService.delete(alarm, getCurrentUser()); } catch (Exception e) { throw handleException(e); } diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/DefaultTbNotificationEntityService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/DefaultTbNotificationEntityService.java index 707747ae91..c7e4f2cd0b 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/DefaultTbNotificationEntityService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/DefaultTbNotificationEntityService.java @@ -52,7 +52,6 @@ import org.thingsboard.server.service.gateway_device.GatewayNotificationsService import org.thingsboard.server.service.security.model.SecurityUser; import java.util.List; -import java.util.Locale; @Slf4j @Service @@ -337,6 +336,27 @@ public class DefaultTbNotificationEntityService implements TbNotificationEntityS } public static EdgeEventActionType edgeTypeByActionType(ActionType actionType) { - return EdgeEventActionType.valueOf(actionType.toString().toUpperCase(Locale.ENGLISH)); + switch (actionType) { + case ADDED: + return EdgeEventActionType.ADDED; + case UPDATED: + return EdgeEventActionType.UPDATED; + case ALARM_ACK: + return EdgeEventActionType.ALARM_ACK; + case ALARM_CLEAR: + return EdgeEventActionType.ALARM_CLEAR; + case DELETED: + return EdgeEventActionType.DELETED; + case RELATION_ADD_OR_UPDATE: + return EdgeEventActionType.RELATION_ADD_OR_UPDATE; + case RELATION_DELETED: + return EdgeEventActionType.RELATION_DELETED; + case ASSIGNED_TO_EDGE: + return EdgeEventActionType.ASSIGNED_TO_EDGE; + case UNASSIGNED_FROM_EDGE: + return EdgeEventActionType.UNASSIGNED_FROM_EDGE; + default: + return null; + } } } diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/alarm/DefaultTbAlarmService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/alarm/DefaultTbAlarmService.java index 305fda2b2b..4930f54a5d 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/alarm/DefaultTbAlarmService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/alarm/DefaultTbAlarmService.java @@ -23,7 +23,6 @@ import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.alarm.AlarmStatus; import org.thingsboard.server.common.data.audit.ActionType; 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.TenantId; import org.thingsboard.server.queue.util.TbCoreComponent; @@ -78,13 +77,12 @@ public class DefaultTbAlarmService extends AbstractTbEntityService implements Tb } @Override - public Boolean delete(Alarm alarm, CustomerId customerId, SecurityUser user) throws ThingsboardException { - TenantId tenantId = alarm.getTenantId(); + public Boolean delete(Alarm alarm, SecurityUser user) throws ThingsboardException { try { - List relatedEdgeIds = findRelatedEdgeIds(tenantId, alarm.getOriginator()); - notificationEntityService.notifyDeleteAlarm(tenantId, alarm, alarm.getOriginator(), customerId, + List relatedEdgeIds = findRelatedEdgeIds(user.getTenantId(), alarm.getOriginator()); + notificationEntityService.notifyDeleteAlarm(user.getTenantId(), alarm, alarm.getOriginator(), user.getCustomerId(), relatedEdgeIds, user, JacksonUtil.OBJECT_MAPPER.writeValueAsString(alarm)); - return alarmService.deleteAlarm(tenantId, alarm.getId()).isSuccessful(); + return alarmService.deleteAlarm(user.getTenantId(), alarm.getId()).isSuccessful(); } catch (Exception e) { throw handleException(e); } diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/alarm/TbAlarmService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/alarm/TbAlarmService.java index f17fcb3b70..7a6d7f6a81 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/alarm/TbAlarmService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/alarm/TbAlarmService.java @@ -17,7 +17,6 @@ package org.thingsboard.server.service.entitiy.alarm; import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.exception.ThingsboardException; -import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.service.security.model.SecurityUser; public interface TbAlarmService { @@ -28,5 +27,5 @@ public interface TbAlarmService { void clear(Alarm alarm, SecurityUser user) throws ThingsboardException; - Boolean delete(Alarm alarm, CustomerId customerId, SecurityUser user) throws ThingsboardException; + Boolean delete(Alarm alarm, SecurityUser user) throws ThingsboardException; } From c4bdee92dbc0d04054a71f95f754ccb5278e7e69 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Wed, 15 Jun 2022 14:04:13 +0300 Subject: [PATCH 798/798] PSQL annotation and dialect cleanup. --- ...va => SqlEntityDatabaseSchemaService.java} | 6 +- ...e.java => SqlTsDatabaseSchemaService.java} | 6 +- ....java => SqlTsDatabaseUpgradeService.java} | 4 +- .../TimescaleTsDatabaseSchemaService.java | 6 - .../TimescaleTsDatabaseUpgradeService.java | 2 - .../src/main/resources/thingsboard.yml | 3 - ...> SqlEntityDatabaseSchemaServiceTest.java} | 16 +-- .../thingsboard/server/dao/util/HsqlDao.java | 26 ---- .../thingsboard/server/dao/util/PsqlDao.java | 26 ---- .../server/dao/util/PsqlTsLatestAnyDao.java | 27 ---- .../server/dao/HsqlTsLatestDaoConfig.java | 37 ----- .../server/dao/PsqlTsDaoConfig.java | 37 ----- ...qlTsDaoConfig.java => SqlTsDaoConfig.java} | 8 +- ...oConfig.java => SqlTsLatestDaoConfig.java} | 8 +- .../server/dao/TimescaleDaoConfig.java | 2 - .../dao/TimescaleTsLatestDaoConfig.java | 4 +- .../HsqlAttributesInsertRepository.java | 76 ---------- ...ava => SqlAttributesInsertRepository.java} | 4 +- ...qlComponentDescriptorInsertRepository.java | 65 --------- ...lComponentDescriptorInsertRepository.java} | 4 +- .../sql/edge/EdgeEventInsertRepository.java | 3 +- .../dao/sql/event/EventInsertRepository.java | 2 - ...ry.java => SqlEventCleanupRepository.java} | 4 +- .../HsqlRelationInsertRepository.java | 63 --------- ....java => SqlRelationInsertRepository.java} | 4 +- .../dao/sqlts/hsql/JpaHsqlTimeseriesDao.java | 62 -------- .../insert/hsql/HsqlInsertTsRepository.java | 87 ------------ .../hsql/HsqlLatestInsertTsRepository.java | 85 ----------- .../SqlLatestInsertTsRepository.java} | 8 +- .../SqlInsertTsRepository.java} | 6 +- .../SqlPartitioningRepository.java} | 10 +- .../TimescaleInsertTsRepository.java | 2 - .../JpaSqlTimeseriesDao.java} | 32 ++--- .../{PsqlPartition.java => SqlPartition.java} | 4 +- .../server/dao/AbstractDaoServiceTest.java | 2 +- .../server/dao/AbstractJpaDaoTest.java | 2 +- .../server/dao/util/DaoTestUtil.java | 40 ------ .../server/dao/util/SqlDbType.java | 20 --- dao/src/test/resources/nosql-test.properties | 1 - dao/src/test/resources/sql-test.properties | 2 - docker/tb-node.hybrid.env | 1 - docker/tb-node.postgres.env | 1 - msa/tb/docker-cassandra/Dockerfile | 1 - msa/tb/docker-postgres/Dockerfile | 1 - msa/tb/docker-tb/Dockerfile | 61 -------- msa/tb/docker-tb/start-db.sh | 18 --- msa/tb/docker-tb/stop-db.sh | 18 --- msa/tb/pom.xml | 132 +----------------- pom.xml | 6 - 49 files changed, 55 insertions(+), 990 deletions(-) rename application/src/main/java/org/thingsboard/server/service/install/{PsqlEntityDatabaseSchemaService.java => SqlEntityDatabaseSchemaService.java} (88%) rename application/src/main/java/org/thingsboard/server/service/install/{PsqlTsDatabaseSchemaService.java => SqlTsDatabaseSchemaService.java} (85%) rename application/src/main/java/org/thingsboard/server/service/install/{PsqlTsDatabaseUpgradeService.java => SqlTsDatabaseUpgradeService.java} (98%) rename application/src/test/java/org/thingsboard/server/service/install/{PsqlEntityDatabaseSchemaServiceTest.java => SqlEntityDatabaseSchemaServiceTest.java} (66%) delete mode 100644 common/dao-api/src/main/java/org/thingsboard/server/dao/util/HsqlDao.java delete mode 100644 common/dao-api/src/main/java/org/thingsboard/server/dao/util/PsqlDao.java delete mode 100644 common/dao-api/src/main/java/org/thingsboard/server/dao/util/PsqlTsLatestAnyDao.java delete mode 100644 dao/src/main/java/org/thingsboard/server/dao/HsqlTsLatestDaoConfig.java delete mode 100644 dao/src/main/java/org/thingsboard/server/dao/PsqlTsDaoConfig.java rename dao/src/main/java/org/thingsboard/server/dao/{HsqlTsDaoConfig.java => SqlTsDaoConfig.java} (87%) rename dao/src/main/java/org/thingsboard/server/dao/{PsqlTsLatestDaoConfig.java => SqlTsLatestDaoConfig.java} (86%) delete mode 100644 dao/src/main/java/org/thingsboard/server/dao/sql/attributes/HsqlAttributesInsertRepository.java rename dao/src/main/java/org/thingsboard/server/dao/sql/attributes/{PsqlAttributesInsertRepository.java => SqlAttributesInsertRepository.java} (85%) delete mode 100644 dao/src/main/java/org/thingsboard/server/dao/sql/component/HsqlComponentDescriptorInsertRepository.java rename dao/src/main/java/org/thingsboard/server/dao/sql/component/{PsqlComponentDescriptorInsertRepository.java => SqlComponentDescriptorInsertRepository.java} (94%) rename dao/src/main/java/org/thingsboard/server/dao/sql/event/{PsqlEventCleanupRepository.java => SqlEventCleanupRepository.java} (91%) delete mode 100644 dao/src/main/java/org/thingsboard/server/dao/sql/relation/HsqlRelationInsertRepository.java rename dao/src/main/java/org/thingsboard/server/dao/sql/relation/{PsqlRelationInsertRepository.java => SqlRelationInsertRepository.java} (90%) delete mode 100644 dao/src/main/java/org/thingsboard/server/dao/sqlts/hsql/JpaHsqlTimeseriesDao.java delete mode 100644 dao/src/main/java/org/thingsboard/server/dao/sqlts/insert/hsql/HsqlInsertTsRepository.java delete mode 100644 dao/src/main/java/org/thingsboard/server/dao/sqlts/insert/latest/hsql/HsqlLatestInsertTsRepository.java rename dao/src/main/java/org/thingsboard/server/dao/sqlts/insert/latest/{psql/PsqlLatestInsertTsRepository.java => sql/SqlLatestInsertTsRepository.java} (96%) rename dao/src/main/java/org/thingsboard/server/dao/sqlts/insert/{psql/PsqlInsertTsRepository.java => sql/SqlInsertTsRepository.java} (93%) rename dao/src/main/java/org/thingsboard/server/dao/sqlts/insert/{psql/PsqlPartitioningRepository.java => sql/SqlPartitioningRepository.java} (80%) rename dao/src/main/java/org/thingsboard/server/dao/sqlts/{psql/JpaPsqlTimeseriesDao.java => sql/JpaSqlTimeseriesDao.java} (85%) rename dao/src/main/java/org/thingsboard/server/dao/timeseries/{PsqlPartition.java => SqlPartition.java} (92%) delete mode 100644 dao/src/test/java/org/thingsboard/server/dao/util/DaoTestUtil.java delete mode 100644 dao/src/test/java/org/thingsboard/server/dao/util/SqlDbType.java delete mode 100644 msa/tb/docker-tb/Dockerfile delete mode 100644 msa/tb/docker-tb/start-db.sh delete mode 100644 msa/tb/docker-tb/stop-db.sh diff --git a/application/src/main/java/org/thingsboard/server/service/install/PsqlEntityDatabaseSchemaService.java b/application/src/main/java/org/thingsboard/server/service/install/SqlEntityDatabaseSchemaService.java similarity index 88% rename from application/src/main/java/org/thingsboard/server/service/install/PsqlEntityDatabaseSchemaService.java rename to application/src/main/java/org/thingsboard/server/service/install/SqlEntityDatabaseSchemaService.java index eaa9d708be..7ae155402a 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/PsqlEntityDatabaseSchemaService.java +++ b/application/src/main/java/org/thingsboard/server/service/install/SqlEntityDatabaseSchemaService.java @@ -18,19 +18,17 @@ package org.thingsboard.server.service.install; import lombok.extern.slf4j.Slf4j; import org.springframework.context.annotation.Profile; import org.springframework.stereotype.Service; -import org.thingsboard.server.dao.util.PsqlDao; @Service -@PsqlDao @Profile("install") @Slf4j -public class PsqlEntityDatabaseSchemaService extends SqlAbstractDatabaseSchemaService +public class SqlEntityDatabaseSchemaService extends SqlAbstractDatabaseSchemaService implements EntityDatabaseSchemaService { public static final String SCHEMA_ENTITIES_SQL = "schema-entities.sql"; public static final String SCHEMA_ENTITIES_IDX_SQL = "schema-entities-idx.sql"; public static final String SCHEMA_ENTITIES_IDX_PSQL_ADDON_SQL = "schema-entities-idx-psql-addon.sql"; - public PsqlEntityDatabaseSchemaService() { + public SqlEntityDatabaseSchemaService() { super(SCHEMA_ENTITIES_SQL, SCHEMA_ENTITIES_IDX_SQL); } diff --git a/application/src/main/java/org/thingsboard/server/service/install/PsqlTsDatabaseSchemaService.java b/application/src/main/java/org/thingsboard/server/service/install/SqlTsDatabaseSchemaService.java similarity index 85% rename from application/src/main/java/org/thingsboard/server/service/install/PsqlTsDatabaseSchemaService.java rename to application/src/main/java/org/thingsboard/server/service/install/SqlTsDatabaseSchemaService.java index 0e7cff7d63..f3ede624ff 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/PsqlTsDatabaseSchemaService.java +++ b/application/src/main/java/org/thingsboard/server/service/install/SqlTsDatabaseSchemaService.java @@ -18,19 +18,17 @@ package org.thingsboard.server.service.install; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Profile; import org.springframework.stereotype.Service; -import org.thingsboard.server.dao.util.PsqlDao; import org.thingsboard.server.dao.util.SqlTsDao; @Service @SqlTsDao -@PsqlDao @Profile("install") -public class PsqlTsDatabaseSchemaService extends SqlAbstractDatabaseSchemaService implements TsDatabaseSchemaService { +public class SqlTsDatabaseSchemaService extends SqlAbstractDatabaseSchemaService implements TsDatabaseSchemaService { @Value("${sql.postgres.ts_key_value_partitioning:MONTHS}") private String partitionType; - public PsqlTsDatabaseSchemaService() { + public SqlTsDatabaseSchemaService() { super("schema-ts-psql.sql", null); } diff --git a/application/src/main/java/org/thingsboard/server/service/install/PsqlTsDatabaseUpgradeService.java b/application/src/main/java/org/thingsboard/server/service/install/SqlTsDatabaseUpgradeService.java similarity index 98% rename from application/src/main/java/org/thingsboard/server/service/install/PsqlTsDatabaseUpgradeService.java rename to application/src/main/java/org/thingsboard/server/service/install/SqlTsDatabaseUpgradeService.java index 55fa73fb27..c1d41a6f6e 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/PsqlTsDatabaseUpgradeService.java +++ b/application/src/main/java/org/thingsboard/server/service/install/SqlTsDatabaseUpgradeService.java @@ -21,7 +21,6 @@ import org.apache.commons.lang3.SystemUtils; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Profile; import org.springframework.stereotype.Service; -import org.thingsboard.server.dao.util.PsqlDao; import org.thingsboard.server.dao.util.SqlTsDao; import java.io.File; @@ -36,8 +35,7 @@ import java.sql.DriverManager; @Profile("install") @Slf4j @SqlTsDao -@PsqlDao -public class PsqlTsDatabaseUpgradeService extends AbstractSqlTsDatabaseUpgradeService implements DatabaseTsUpgradeService { +public class SqlTsDatabaseUpgradeService extends AbstractSqlTsDatabaseUpgradeService implements DatabaseTsUpgradeService { @Value("${sql.postgres.ts_key_value_partitioning:MONTHS}") private String partitionType; diff --git a/application/src/main/java/org/thingsboard/server/service/install/TimescaleTsDatabaseSchemaService.java b/application/src/main/java/org/thingsboard/server/service/install/TimescaleTsDatabaseSchemaService.java index a0257ba0e4..409498cfd3 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/TimescaleTsDatabaseSchemaService.java +++ b/application/src/main/java/org/thingsboard/server/service/install/TimescaleTsDatabaseSchemaService.java @@ -19,16 +19,10 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Profile; import org.springframework.stereotype.Service; -import org.thingsboard.server.dao.util.PsqlDao; import org.thingsboard.server.dao.util.TimescaleDBTsDao; -import java.sql.Connection; -import java.sql.DriverManager; -import java.sql.SQLException; - @Service @TimescaleDBTsDao -@PsqlDao @Profile("install") @Slf4j public class TimescaleTsDatabaseSchemaService extends SqlAbstractDatabaseSchemaService implements TsDatabaseSchemaService { diff --git a/application/src/main/java/org/thingsboard/server/service/install/TimescaleTsDatabaseUpgradeService.java b/application/src/main/java/org/thingsboard/server/service/install/TimescaleTsDatabaseUpgradeService.java index 1834ca6552..245dc4503d 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/TimescaleTsDatabaseUpgradeService.java +++ b/application/src/main/java/org/thingsboard/server/service/install/TimescaleTsDatabaseUpgradeService.java @@ -22,7 +22,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Profile; import org.springframework.stereotype.Service; -import org.thingsboard.server.dao.util.PsqlDao; import org.thingsboard.server.dao.util.TimescaleDBTsDao; import java.io.File; @@ -37,7 +36,6 @@ import java.sql.DriverManager; @Profile("install") @Slf4j @TimescaleDBTsDao -@PsqlDao public class TimescaleTsDatabaseUpgradeService extends AbstractSqlTsDatabaseUpgradeService implements DatabaseTsUpgradeService { @Value("${sql.timescale.chunk_time_interval:86400000}") diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index 4f11b6880c..9f6f3099ef 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -176,8 +176,6 @@ database: ts_latest: type: "${DATABASE_TS_LATEST_TYPE:sql}" # cassandra, sql, or timescale (for hybrid mode, DATABASE_TS_TYPE value should be cassandra, or timescale) -# note: timescale works only with postgreSQL database for DATABASE_ENTITIES_TYPE. - # Cassandra driver configuration parameters cassandra: # Thingsboard cluster name @@ -535,7 +533,6 @@ spring: open-in-view: "false" hibernate: ddl-auto: "none" - database-platform: "${SPRING_JPA_DATABASE_PLATFORM:org.hibernate.dialect.PostgreSQL10Dialect}" datasource: driverClassName: "${SPRING_DRIVER_CLASS_NAME:org.postgresql.Driver}" url: "${SPRING_DATASOURCE_URL:jdbc:postgresql://localhost:5432/thingsboard}" diff --git a/application/src/test/java/org/thingsboard/server/service/install/PsqlEntityDatabaseSchemaServiceTest.java b/application/src/test/java/org/thingsboard/server/service/install/SqlEntityDatabaseSchemaServiceTest.java similarity index 66% rename from application/src/test/java/org/thingsboard/server/service/install/PsqlEntityDatabaseSchemaServiceTest.java rename to application/src/test/java/org/thingsboard/server/service/install/SqlEntityDatabaseSchemaServiceTest.java index eaea437bc7..07a32d5cdc 100644 --- a/application/src/test/java/org/thingsboard/server/service/install/PsqlEntityDatabaseSchemaServiceTest.java +++ b/application/src/test/java/org/thingsboard/server/service/install/SqlEntityDatabaseSchemaServiceTest.java @@ -23,31 +23,31 @@ import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; -public class PsqlEntityDatabaseSchemaServiceTest { +public class SqlEntityDatabaseSchemaServiceTest { @Test public void givenPsqlDbSchemaService_whenCreateDatabaseSchema_thenVerifyPsqlIndexSpecificCall() throws Exception { - PsqlEntityDatabaseSchemaService service = spy(new PsqlEntityDatabaseSchemaService()); + SqlEntityDatabaseSchemaService service = spy(new SqlEntityDatabaseSchemaService()); willDoNothing().given(service).executeQueryFromFile(anyString()); service.createDatabaseSchema(); verify(service, times(1)).createDatabaseIndexes(); - verify(service, times(1)).executeQueryFromFile(PsqlEntityDatabaseSchemaService.SCHEMA_ENTITIES_SQL); - verify(service, times(1)).executeQueryFromFile(PsqlEntityDatabaseSchemaService.SCHEMA_ENTITIES_IDX_SQL); - verify(service, times(1)).executeQueryFromFile(PsqlEntityDatabaseSchemaService.SCHEMA_ENTITIES_IDX_PSQL_ADDON_SQL); + verify(service, times(1)).executeQueryFromFile(SqlEntityDatabaseSchemaService.SCHEMA_ENTITIES_SQL); + verify(service, times(1)).executeQueryFromFile(SqlEntityDatabaseSchemaService.SCHEMA_ENTITIES_IDX_SQL); + verify(service, times(1)).executeQueryFromFile(SqlEntityDatabaseSchemaService.SCHEMA_ENTITIES_IDX_PSQL_ADDON_SQL); verify(service, times(3)).executeQueryFromFile(anyString()); } @Test public void givenPsqlDbSchemaService_whenCreateDatabaseIndexes_thenVerifyPsqlIndexSpecificCall() throws Exception { - PsqlEntityDatabaseSchemaService service = spy(new PsqlEntityDatabaseSchemaService()); + SqlEntityDatabaseSchemaService service = spy(new SqlEntityDatabaseSchemaService()); willDoNothing().given(service).executeQueryFromFile(anyString()); service.createDatabaseIndexes(); - verify(service, times(1)).executeQueryFromFile(PsqlEntityDatabaseSchemaService.SCHEMA_ENTITIES_IDX_SQL); - verify(service, times(1)).executeQueryFromFile(PsqlEntityDatabaseSchemaService.SCHEMA_ENTITIES_IDX_PSQL_ADDON_SQL); + verify(service, times(1)).executeQueryFromFile(SqlEntityDatabaseSchemaService.SCHEMA_ENTITIES_IDX_SQL); + verify(service, times(1)).executeQueryFromFile(SqlEntityDatabaseSchemaService.SCHEMA_ENTITIES_IDX_PSQL_ADDON_SQL); verify(service, times(2)).executeQueryFromFile(anyString()); } diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/util/HsqlDao.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/util/HsqlDao.java deleted file mode 100644 index 949c8d3569..0000000000 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/util/HsqlDao.java +++ /dev/null @@ -1,26 +0,0 @@ -/** - * Copyright © 2016-2022 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.dao.util; - -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -@Retention(RetentionPolicy.RUNTIME) -@ConditionalOnProperty(prefix = "spring.jpa", value = "database-platform", havingValue = "org.hibernate.dialect.HSQLDialect") -public @interface HsqlDao { -} \ No newline at end of file diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/util/PsqlDao.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/util/PsqlDao.java deleted file mode 100644 index ba30f8975a..0000000000 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/util/PsqlDao.java +++ /dev/null @@ -1,26 +0,0 @@ -/** - * Copyright © 2016-2022 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.dao.util; - -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -@Retention(RetentionPolicy.RUNTIME) -@ConditionalOnProperty(prefix = "spring.jpa", value = "database-platform", havingValue = "org.hibernate.dialect.PostgreSQL10Dialect") -public @interface PsqlDao { -} \ No newline at end of file diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/util/PsqlTsLatestAnyDao.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/util/PsqlTsLatestAnyDao.java deleted file mode 100644 index 368f550c14..0000000000 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/util/PsqlTsLatestAnyDao.java +++ /dev/null @@ -1,27 +0,0 @@ -/** - * Copyright © 2016-2022 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.dao.util; - -import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -@Retention(RetentionPolicy.RUNTIME) -@ConditionalOnExpression("('${database.ts_latest.type}'=='sql' || '${database.ts_latest.type}'=='timescale') " + - "&& '${spring.jpa.database-platform}'=='org.hibernate.dialect.PostgreSQL10Dialect'") -public @interface PsqlTsLatestAnyDao { -} diff --git a/dao/src/main/java/org/thingsboard/server/dao/HsqlTsLatestDaoConfig.java b/dao/src/main/java/org/thingsboard/server/dao/HsqlTsLatestDaoConfig.java deleted file mode 100644 index 2012ca5a82..0000000000 --- a/dao/src/main/java/org/thingsboard/server/dao/HsqlTsLatestDaoConfig.java +++ /dev/null @@ -1,37 +0,0 @@ -/** - * Copyright © 2016-2022 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.dao; - -import org.springframework.boot.autoconfigure.domain.EntityScan; -import org.springframework.context.annotation.ComponentScan; -import org.springframework.context.annotation.Configuration; -import org.springframework.data.jpa.repository.config.EnableJpaRepositories; -import org.springframework.transaction.annotation.EnableTransactionManagement; -import org.thingsboard.server.dao.util.HsqlDao; -import org.thingsboard.server.dao.util.SqlTsLatestDao; -import org.thingsboard.server.dao.util.TbAutoConfiguration; - -@Configuration -@TbAutoConfiguration -@ComponentScan({"org.thingsboard.server.dao.sqlts.hsql"}) -@EnableJpaRepositories({"org.thingsboard.server.dao.sqlts.insert.latest.hsql", "org.thingsboard.server.dao.sqlts.latest"}) -@EntityScan({"org.thingsboard.server.dao.model.sqlts.latest"}) -@EnableTransactionManagement -@SqlTsLatestDao -@HsqlDao -public class HsqlTsLatestDaoConfig { - -} diff --git a/dao/src/main/java/org/thingsboard/server/dao/PsqlTsDaoConfig.java b/dao/src/main/java/org/thingsboard/server/dao/PsqlTsDaoConfig.java deleted file mode 100644 index 06c8cf4a5b..0000000000 --- a/dao/src/main/java/org/thingsboard/server/dao/PsqlTsDaoConfig.java +++ /dev/null @@ -1,37 +0,0 @@ -/** - * Copyright © 2016-2022 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.dao; - -import org.springframework.boot.autoconfigure.domain.EntityScan; -import org.springframework.context.annotation.ComponentScan; -import org.springframework.context.annotation.Configuration; -import org.springframework.data.jpa.repository.config.EnableJpaRepositories; -import org.springframework.transaction.annotation.EnableTransactionManagement; -import org.thingsboard.server.dao.util.PsqlDao; -import org.thingsboard.server.dao.util.SqlTsDao; -import org.thingsboard.server.dao.util.TbAutoConfiguration; - -@Configuration -@TbAutoConfiguration -@ComponentScan({"org.thingsboard.server.dao.sqlts.psql", "org.thingsboard.server.dao.sqlts.insert.psql"}) -@EnableJpaRepositories({"org.thingsboard.server.dao.sqlts.ts", "org.thingsboard.server.dao.sqlts.insert.psql"}) -@EntityScan({"org.thingsboard.server.dao.model.sqlts.ts"}) -@EnableTransactionManagement -@PsqlDao -@SqlTsDao -public class PsqlTsDaoConfig { - -} diff --git a/dao/src/main/java/org/thingsboard/server/dao/HsqlTsDaoConfig.java b/dao/src/main/java/org/thingsboard/server/dao/SqlTsDaoConfig.java similarity index 87% rename from dao/src/main/java/org/thingsboard/server/dao/HsqlTsDaoConfig.java rename to dao/src/main/java/org/thingsboard/server/dao/SqlTsDaoConfig.java index 932312f96e..cd1401d60c 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/HsqlTsDaoConfig.java +++ b/dao/src/main/java/org/thingsboard/server/dao/SqlTsDaoConfig.java @@ -20,18 +20,16 @@ import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.data.jpa.repository.config.EnableJpaRepositories; import org.springframework.transaction.annotation.EnableTransactionManagement; -import org.thingsboard.server.dao.util.HsqlDao; import org.thingsboard.server.dao.util.SqlTsDao; import org.thingsboard.server.dao.util.TbAutoConfiguration; @Configuration @TbAutoConfiguration -@ComponentScan({"org.thingsboard.server.dao.sqlts.hsql"}) -@EnableJpaRepositories({"org.thingsboard.server.dao.sqlts.ts", "org.thingsboard.server.dao.sqlts.insert.hsql"}) +@ComponentScan({"org.thingsboard.server.dao.sqlts.sql", "org.thingsboard.server.dao.sqlts.insert.sql"}) +@EnableJpaRepositories({"org.thingsboard.server.dao.sqlts.ts", "org.thingsboard.server.dao.sqlts.insert.sql"}) @EntityScan({"org.thingsboard.server.dao.model.sqlts.ts"}) @EnableTransactionManagement @SqlTsDao -@HsqlDao -public class HsqlTsDaoConfig { +public class SqlTsDaoConfig { } diff --git a/dao/src/main/java/org/thingsboard/server/dao/PsqlTsLatestDaoConfig.java b/dao/src/main/java/org/thingsboard/server/dao/SqlTsLatestDaoConfig.java similarity index 86% rename from dao/src/main/java/org/thingsboard/server/dao/PsqlTsLatestDaoConfig.java rename to dao/src/main/java/org/thingsboard/server/dao/SqlTsLatestDaoConfig.java index 9d8d625e78..4fe4aaed43 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/PsqlTsLatestDaoConfig.java +++ b/dao/src/main/java/org/thingsboard/server/dao/SqlTsLatestDaoConfig.java @@ -20,18 +20,16 @@ import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.data.jpa.repository.config.EnableJpaRepositories; import org.springframework.transaction.annotation.EnableTransactionManagement; -import org.thingsboard.server.dao.util.PsqlDao; import org.thingsboard.server.dao.util.SqlTsLatestDao; import org.thingsboard.server.dao.util.TbAutoConfiguration; @Configuration @TbAutoConfiguration -@ComponentScan({"org.thingsboard.server.dao.sqlts.psql"}) -@EnableJpaRepositories({"org.thingsboard.server.dao.sqlts.insert.latest.psql", "org.thingsboard.server.dao.sqlts.latest"}) +@ComponentScan({"org.thingsboard.server.dao.sqlts.sql"}) +@EnableJpaRepositories({"org.thingsboard.server.dao.sqlts.insert.latest.sql", "org.thingsboard.server.dao.sqlts.latest"}) @EntityScan({"org.thingsboard.server.dao.model.sqlts.latest"}) @EnableTransactionManagement @SqlTsLatestDao -@PsqlDao -public class PsqlTsLatestDaoConfig { +public class SqlTsLatestDaoConfig { } diff --git a/dao/src/main/java/org/thingsboard/server/dao/TimescaleDaoConfig.java b/dao/src/main/java/org/thingsboard/server/dao/TimescaleDaoConfig.java index 0e4d4750f9..6b11ff8005 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/TimescaleDaoConfig.java +++ b/dao/src/main/java/org/thingsboard/server/dao/TimescaleDaoConfig.java @@ -20,7 +20,6 @@ import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.data.jpa.repository.config.EnableJpaRepositories; import org.springframework.transaction.annotation.EnableTransactionManagement; -import org.thingsboard.server.dao.util.PsqlDao; import org.thingsboard.server.dao.util.TbAutoConfiguration; import org.thingsboard.server.dao.util.TimescaleDBTsDao; @@ -31,7 +30,6 @@ import org.thingsboard.server.dao.util.TimescaleDBTsDao; @EntityScan({"org.thingsboard.server.dao.model.sqlts.timescale"}) @EnableTransactionManagement @TimescaleDBTsDao -@PsqlDao public class TimescaleDaoConfig { } diff --git a/dao/src/main/java/org/thingsboard/server/dao/TimescaleTsLatestDaoConfig.java b/dao/src/main/java/org/thingsboard/server/dao/TimescaleTsLatestDaoConfig.java index 02082085ba..3fd452a1b1 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/TimescaleTsLatestDaoConfig.java +++ b/dao/src/main/java/org/thingsboard/server/dao/TimescaleTsLatestDaoConfig.java @@ -20,18 +20,16 @@ import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.data.jpa.repository.config.EnableJpaRepositories; import org.springframework.transaction.annotation.EnableTransactionManagement; -import org.thingsboard.server.dao.util.PsqlDao; import org.thingsboard.server.dao.util.TbAutoConfiguration; import org.thingsboard.server.dao.util.TimescaleDBTsLatestDao; @Configuration @TbAutoConfiguration @ComponentScan({"org.thingsboard.server.dao.sqlts.timescale"}) -@EnableJpaRepositories({"org.thingsboard.server.dao.sqlts.insert.latest.psql", "org.thingsboard.server.dao.sqlts.latest"}) +@EnableJpaRepositories({"org.thingsboard.server.dao.sqlts.insert.latest.sql", "org.thingsboard.server.dao.sqlts.latest"}) @EntityScan({"org.thingsboard.server.dao.model.sqlts.latest"}) @EnableTransactionManagement @TimescaleDBTsLatestDao -@PsqlDao public class TimescaleTsLatestDaoConfig { } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/attributes/HsqlAttributesInsertRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/attributes/HsqlAttributesInsertRepository.java deleted file mode 100644 index 24465e0742..0000000000 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/attributes/HsqlAttributesInsertRepository.java +++ /dev/null @@ -1,76 +0,0 @@ -/** - * Copyright © 2016-2022 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.dao.sql.attributes; - -import org.springframework.stereotype.Repository; -import org.springframework.transaction.annotation.Transactional; -import org.thingsboard.server.dao.model.sql.AttributeKvEntity; -import org.thingsboard.server.dao.util.HsqlDao; - -import java.sql.Types; -import java.util.List; - -@HsqlDao -@Repository -@Transactional -public class HsqlAttributesInsertRepository extends AttributeKvInsertRepository { - - private static final String INSERT_OR_UPDATE = - "MERGE INTO attribute_kv USING(VALUES ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) " + - "A (entity_type, entity_id, attribute_type, attribute_key, str_v, long_v, dbl_v, bool_v, json_v, last_update_ts) " + - "ON (attribute_kv.entity_type=A.entity_type " + - "AND attribute_kv.entity_id=A.entity_id " + - "AND attribute_kv.attribute_type=A.attribute_type " + - "AND attribute_kv.attribute_key=A.attribute_key) " + - "WHEN MATCHED THEN UPDATE SET attribute_kv.str_v = A.str_v, attribute_kv.long_v = A.long_v, attribute_kv.dbl_v = A.dbl_v, attribute_kv.bool_v = A.bool_v, attribute_kv.json_v = A.json_v, attribute_kv.last_update_ts = A.last_update_ts " + - "WHEN NOT MATCHED THEN INSERT (entity_type, entity_id, attribute_type, attribute_key, str_v, long_v, dbl_v, bool_v, json_v, last_update_ts) " + - "VALUES (A.entity_type, A.entity_id, A.attribute_type, A.attribute_key, A.str_v, A.long_v, A.dbl_v, A.bool_v, A.json_v, A.last_update_ts)"; - - @Override - protected void saveOrUpdate(List entities) { - entities.forEach(entity -> { - jdbcTemplate.update(INSERT_OR_UPDATE, ps -> { - ps.setString(1, entity.getId().getEntityType().name()); - ps.setObject(2, entity.getId().getEntityId()); - ps.setString(3, entity.getId().getAttributeType()); - ps.setString(4, entity.getId().getAttributeKey()); - ps.setString(5, entity.getStrValue()); - - if (entity.getLongValue() != null) { - ps.setLong(6, entity.getLongValue()); - } else { - ps.setNull(6, Types.BIGINT); - } - - if (entity.getDoubleValue() != null) { - ps.setDouble(7, entity.getDoubleValue()); - } else { - ps.setNull(7, Types.DOUBLE); - } - - if (entity.getBooleanValue() != null) { - ps.setBoolean(8, entity.getBooleanValue()); - } else { - ps.setNull(8, Types.BOOLEAN); - } - - ps.setString(9, entity.getJsonValue()); - - ps.setLong(10, entity.getLastUpdateTs()); - }); - }); - } -} \ No newline at end of file diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/attributes/PsqlAttributesInsertRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/attributes/SqlAttributesInsertRepository.java similarity index 85% rename from dao/src/main/java/org/thingsboard/server/dao/sql/attributes/PsqlAttributesInsertRepository.java rename to dao/src/main/java/org/thingsboard/server/dao/sql/attributes/SqlAttributesInsertRepository.java index 2b5b53691a..e18f5178a8 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/attributes/PsqlAttributesInsertRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/attributes/SqlAttributesInsertRepository.java @@ -17,11 +17,9 @@ package org.thingsboard.server.dao.sql.attributes; import org.springframework.stereotype.Repository; import org.springframework.transaction.annotation.Transactional; -import org.thingsboard.server.dao.util.PsqlDao; -@PsqlDao @Repository @Transactional -public class PsqlAttributesInsertRepository extends AttributeKvInsertRepository { +public class SqlAttributesInsertRepository extends AttributeKvInsertRepository { } \ No newline at end of file diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/component/HsqlComponentDescriptorInsertRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/component/HsqlComponentDescriptorInsertRepository.java deleted file mode 100644 index e735596de3..0000000000 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/component/HsqlComponentDescriptorInsertRepository.java +++ /dev/null @@ -1,65 +0,0 @@ -/** - * Copyright © 2016-2022 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.dao.sql.component; - -import org.springframework.stereotype.Repository; -import org.thingsboard.server.dao.model.sql.ComponentDescriptorEntity; -import org.thingsboard.server.dao.util.HsqlDao; - -import javax.persistence.Query; - -@HsqlDao -@Repository -public class HsqlComponentDescriptorInsertRepository extends AbstractComponentDescriptorInsertRepository { - - private static final String P_KEY_CONFLICT_STATEMENT = "(component_descriptor.id=UUID(I.id))"; - private static final String UNQ_KEY_CONFLICT_STATEMENT = "(component_descriptor.clazz=I.clazz)"; - - private static final String INSERT_OR_UPDATE_ON_P_KEY_CONFLICT = getInsertString(P_KEY_CONFLICT_STATEMENT); - private static final String INSERT_OR_UPDATE_ON_UNQ_KEY_CONFLICT = getInsertString(UNQ_KEY_CONFLICT_STATEMENT); - - @Override - public ComponentDescriptorEntity saveOrUpdate(ComponentDescriptorEntity entity) { - return saveAndGet(entity, INSERT_OR_UPDATE_ON_P_KEY_CONFLICT, INSERT_OR_UPDATE_ON_UNQ_KEY_CONFLICT); - } - - @Override - protected Query getQuery(ComponentDescriptorEntity entity, String query) { - return entityManager.createNativeQuery(query, ComponentDescriptorEntity.class) - .setParameter("id", entity.getUuid().toString()) - .setParameter("created_time", entity.getCreatedTime()) - .setParameter("actions", entity.getActions()) - .setParameter("clazz", entity.getClazz()) - .setParameter("configuration_descriptor", entity.getConfigurationDescriptor().toString()) - .setParameter("name", entity.getName()) - .setParameter("scope", entity.getScope().name()) - .setParameter("search_text", entity.getSearchText()) - .setParameter("type", entity.getType().name()); - } - - @Override - protected ComponentDescriptorEntity doProcessSaveOrUpdate(ComponentDescriptorEntity entity, String query) { - getQuery(entity, query).executeUpdate(); - return entityManager.find(ComponentDescriptorEntity.class, entity.getUuid()); - } - - private static String getInsertString(String conflictStatement) { - return "MERGE INTO component_descriptor USING (VALUES :id, :created_time, :actions, :clazz, :configuration_descriptor, :name, :scope, :search_text, :type) I (id, created_time, actions, clazz, configuration_descriptor, name, scope, search_text, type) ON " - + conflictStatement - + " WHEN MATCHED THEN UPDATE SET component_descriptor.id = UUID(I.id), component_descriptor.actions = I.actions, component_descriptor.clazz = I.clazz, component_descriptor.configuration_descriptor = I.configuration_descriptor, component_descriptor.name = I.name, component_descriptor.scope = I.scope, component_descriptor.search_text = I.search_text, component_descriptor.type = I.type" + - " WHEN NOT MATCHED THEN INSERT (id, created_time, actions, clazz, configuration_descriptor, name, scope, search_text, type) VALUES (UUID(I.id), I.created_time, I.actions, I.clazz, I.configuration_descriptor, I.name, I.scope, I.search_text, I.type)"; - } -} diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/component/PsqlComponentDescriptorInsertRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/component/SqlComponentDescriptorInsertRepository.java similarity index 94% rename from dao/src/main/java/org/thingsboard/server/dao/sql/component/PsqlComponentDescriptorInsertRepository.java rename to dao/src/main/java/org/thingsboard/server/dao/sql/component/SqlComponentDescriptorInsertRepository.java index 1886dd3b35..593cccee2a 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/component/PsqlComponentDescriptorInsertRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/component/SqlComponentDescriptorInsertRepository.java @@ -17,11 +17,9 @@ package org.thingsboard.server.dao.sql.component; import org.springframework.stereotype.Repository; import org.thingsboard.server.dao.model.sql.ComponentDescriptorEntity; -import org.thingsboard.server.dao.util.PsqlDao; -@PsqlDao @Repository -public class PsqlComponentDescriptorInsertRepository extends AbstractComponentDescriptorInsertRepository { +public class SqlComponentDescriptorInsertRepository extends AbstractComponentDescriptorInsertRepository { private static final String ID = "id = :id"; private static final String CLAZZ_CLAZZ = "clazz = :clazz"; diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/edge/EdgeEventInsertRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/edge/EdgeEventInsertRepository.java index c67aaabbb1..694d68556f 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/edge/EdgeEventInsertRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/edge/EdgeEventInsertRepository.java @@ -24,13 +24,12 @@ import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.support.TransactionCallbackWithoutResult; import org.springframework.transaction.support.TransactionTemplate; import org.thingsboard.server.dao.model.sql.EdgeEventEntity; -import org.thingsboard.server.dao.util.PsqlDao; import java.sql.PreparedStatement; import java.sql.SQLException; import java.util.List; -@PsqlDao + @Repository @Transactional public class EdgeEventInsertRepository { diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/event/EventInsertRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/event/EventInsertRepository.java index 1f2d4c01be..6234db58da 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/event/EventInsertRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/event/EventInsertRepository.java @@ -25,14 +25,12 @@ import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.support.TransactionCallbackWithoutResult; import org.springframework.transaction.support.TransactionTemplate; import org.thingsboard.server.dao.model.sql.EventEntity; -import org.thingsboard.server.dao.util.PsqlDao; import java.sql.PreparedStatement; import java.sql.SQLException; import java.util.List; import java.util.regex.Pattern; -@PsqlDao @Repository @Transactional public class EventInsertRepository { diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/event/PsqlEventCleanupRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/event/SqlEventCleanupRepository.java similarity index 91% rename from dao/src/main/java/org/thingsboard/server/dao/sql/event/PsqlEventCleanupRepository.java rename to dao/src/main/java/org/thingsboard/server/dao/sql/event/SqlEventCleanupRepository.java index 664a148948..6a17f7c740 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/event/PsqlEventCleanupRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/event/SqlEventCleanupRepository.java @@ -18,7 +18,6 @@ package org.thingsboard.server.dao.sql.event; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Repository; import org.thingsboard.server.dao.sql.JpaAbstractDaoListeningExecutorService; -import org.thingsboard.server.dao.util.PsqlDao; import java.sql.Connection; import java.sql.PreparedStatement; @@ -27,9 +26,8 @@ import java.sql.SQLException; import java.util.concurrent.TimeUnit; @Slf4j -@PsqlDao @Repository -public class PsqlEventCleanupRepository extends JpaAbstractDaoListeningExecutorService implements EventCleanupRepository { +public class SqlEventCleanupRepository extends JpaAbstractDaoListeningExecutorService implements EventCleanupRepository { @Override public void cleanupEvents(long regularEventStartTs, long regularEventEndTs, long debugEventStartTs, long debugEventEndTs) { diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/relation/HsqlRelationInsertRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/relation/HsqlRelationInsertRepository.java deleted file mode 100644 index 5da0a8a2de..0000000000 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/relation/HsqlRelationInsertRepository.java +++ /dev/null @@ -1,63 +0,0 @@ -/** - * Copyright © 2016-2022 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.dao.sql.relation; - -import org.springframework.stereotype.Repository; -import org.springframework.transaction.annotation.Transactional; -import org.thingsboard.server.dao.model.sql.RelationCompositeKey; -import org.thingsboard.server.dao.model.sql.RelationEntity; -import org.thingsboard.server.dao.util.HsqlDao; - -import javax.persistence.Query; - -@HsqlDao -@Repository -@Transactional -public class HsqlRelationInsertRepository extends AbstractRelationInsertRepository implements RelationInsertRepository { - - private static final String INSERT_ON_CONFLICT_DO_UPDATE = "MERGE INTO relation USING (VALUES :fromId, :fromType, :toId, :toType, :relationTypeGroup, :relationType, :additionalInfo) R " + - "(from_id, from_type, to_id, to_type, relation_type_group, relation_type, additional_info) " + - "ON (relation.from_id = UUID(R.from_id) AND relation.from_type = R.from_type AND relation.relation_type_group = R.relation_type_group AND relation.relation_type = R.relation_type AND relation.to_id = UUID(R.to_id) AND relation.to_type = R.to_type) " + - "WHEN MATCHED THEN UPDATE SET relation.additional_info = R.additional_info " + - "WHEN NOT MATCHED THEN INSERT (from_id, from_type, to_id, to_type, relation_type_group, relation_type, additional_info) VALUES (UUID(R.from_id), R.from_type, UUID(R.to_id), R.to_type, R.relation_type_group, R.relation_type, R.additional_info)"; - - protected Query getQuery(RelationEntity entity, String query) { - Query nativeQuery = entityManager.createNativeQuery(query, RelationEntity.class); - if (entity.getAdditionalInfo() == null) { - nativeQuery.setParameter("additionalInfo", null); - } else { - nativeQuery.setParameter("additionalInfo", entity.getAdditionalInfo().toString()); - } - return nativeQuery - .setParameter("fromId", entity.getFromId().toString()) - .setParameter("fromType", entity.getFromType()) - .setParameter("toId", entity.getToId().toString()) - .setParameter("toType", entity.getToType()) - .setParameter("relationTypeGroup", entity.getRelationTypeGroup()) - .setParameter("relationType", entity.getRelationType()); - } - - @Override - public RelationEntity saveOrUpdate(RelationEntity entity) { - return processSaveOrUpdate(entity); - } - - @Override - protected RelationEntity processSaveOrUpdate(RelationEntity entity) { - getQuery(entity, INSERT_ON_CONFLICT_DO_UPDATE).executeUpdate(); - return entityManager.find(RelationEntity.class, new RelationCompositeKey(entity.toData())); - } -} diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/relation/PsqlRelationInsertRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/relation/SqlRelationInsertRepository.java similarity index 90% rename from dao/src/main/java/org/thingsboard/server/dao/sql/relation/PsqlRelationInsertRepository.java rename to dao/src/main/java/org/thingsboard/server/dao/sql/relation/SqlRelationInsertRepository.java index a622d19e6d..23d24588b9 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/relation/PsqlRelationInsertRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/relation/SqlRelationInsertRepository.java @@ -18,12 +18,10 @@ package org.thingsboard.server.dao.sql.relation; import org.springframework.stereotype.Repository; import org.springframework.transaction.annotation.Transactional; import org.thingsboard.server.dao.model.sql.RelationEntity; -import org.thingsboard.server.dao.util.PsqlDao; -@PsqlDao @Repository @Transactional -public class PsqlRelationInsertRepository extends AbstractRelationInsertRepository implements RelationInsertRepository { +public class SqlRelationInsertRepository extends AbstractRelationInsertRepository implements RelationInsertRepository { private static final String INSERT_ON_CONFLICT_DO_UPDATE = "INSERT INTO relation (from_id, from_type, to_id, to_type, relation_type_group, relation_type, additional_info)" + " VALUES (:fromId, :fromType, :toId, :toType, :relationTypeGroup, :relationType, :additionalInfo) " + diff --git a/dao/src/main/java/org/thingsboard/server/dao/sqlts/hsql/JpaHsqlTimeseriesDao.java b/dao/src/main/java/org/thingsboard/server/dao/sqlts/hsql/JpaHsqlTimeseriesDao.java deleted file mode 100644 index 770c9142d4..0000000000 --- a/dao/src/main/java/org/thingsboard/server/dao/sqlts/hsql/JpaHsqlTimeseriesDao.java +++ /dev/null @@ -1,62 +0,0 @@ -/** - * Copyright © 2016-2022 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.dao.sqlts.hsql; - -import com.google.common.util.concurrent.Futures; -import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.MoreExecutors; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Component; -import org.thingsboard.server.common.data.id.EntityId; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.kv.TsKvEntry; -import org.thingsboard.server.dao.model.sqlts.ts.TsKvEntity; -import org.thingsboard.server.dao.sqlts.AbstractChunkedAggregationTimeseriesDao; -import org.thingsboard.server.dao.timeseries.TimeseriesDao; -import org.thingsboard.server.dao.util.HsqlDao; -import org.thingsboard.server.dao.util.SqlTsDao; - - -@Component -@Slf4j -@SqlTsDao -@HsqlDao -public class JpaHsqlTimeseriesDao extends AbstractChunkedAggregationTimeseriesDao implements TimeseriesDao { - - @Override - public ListenableFuture save(TenantId tenantId, EntityId entityId, TsKvEntry tsKvEntry, long ttl) { - int dataPointDays = getDataPointDays(tsKvEntry, computeTtl(ttl)); - String strKey = tsKvEntry.getKey(); - Integer keyId = getOrSaveKeyId(strKey); - TsKvEntity entity = new TsKvEntity(); - entity.setEntityId(entityId.getId()); - entity.setTs(tsKvEntry.getTs()); - entity.setKey(keyId); - entity.setStrValue(tsKvEntry.getStrValue().orElse(null)); - entity.setDoubleValue(tsKvEntry.getDoubleValue().orElse(null)); - entity.setLongValue(tsKvEntry.getLongValue().orElse(null)); - entity.setBooleanValue(tsKvEntry.getBooleanValue().orElse(null)); - entity.setJsonValue(tsKvEntry.getJsonValue().orElse(null)); - log.trace("Saving entity: {}", entity); - return Futures.transform(tsQueue.add(entity), v -> dataPointDays, MoreExecutors.directExecutor()); - } - - @Override - public void cleanup(long systemTtl) { - - } - -} diff --git a/dao/src/main/java/org/thingsboard/server/dao/sqlts/insert/hsql/HsqlInsertTsRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sqlts/insert/hsql/HsqlInsertTsRepository.java deleted file mode 100644 index 596c913ed8..0000000000 --- a/dao/src/main/java/org/thingsboard/server/dao/sqlts/insert/hsql/HsqlInsertTsRepository.java +++ /dev/null @@ -1,87 +0,0 @@ -/** - * Copyright © 2016-2022 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.dao.sqlts.insert.hsql; - -import org.springframework.jdbc.core.BatchPreparedStatementSetter; -import org.springframework.stereotype.Repository; -import org.springframework.transaction.annotation.Transactional; -import org.thingsboard.server.dao.model.sqlts.ts.TsKvEntity; -import org.thingsboard.server.dao.sqlts.insert.AbstractInsertRepository; -import org.thingsboard.server.dao.sqlts.insert.InsertTsRepository; -import org.thingsboard.server.dao.util.HsqlDao; -import org.thingsboard.server.dao.util.SqlTsDao; - -import java.sql.PreparedStatement; -import java.sql.SQLException; -import java.sql.Types; -import java.util.List; - -@SqlTsDao -@HsqlDao -@Repository -@Transactional -public class HsqlInsertTsRepository extends AbstractInsertRepository implements InsertTsRepository { - - private static final String INSERT_OR_UPDATE = - "MERGE INTO ts_kv USING(VALUES ?, ?, ?, ?, ?, ?, ?, ?) " + - "T (entity_id, key, ts, bool_v, str_v, long_v, dbl_v, json_v) " + - "ON (ts_kv.entity_id=T.entity_id " + - "AND ts_kv.key=T.key " + - "AND ts_kv.ts=T.ts) " + - "WHEN MATCHED THEN UPDATE SET ts_kv.bool_v = T.bool_v, ts_kv.str_v = T.str_v, ts_kv.long_v = T.long_v, ts_kv.dbl_v = T.dbl_v ,ts_kv.json_v = T.json_v " + - "WHEN NOT MATCHED THEN INSERT (entity_id, key, ts, bool_v, str_v, long_v, dbl_v, json_v) " + - "VALUES (T.entity_id, T.key, T.ts, T.bool_v, T.str_v, T.long_v, T.dbl_v, T.json_v);"; - - @Override - public void saveOrUpdate(List entities) { - jdbcTemplate.batchUpdate(INSERT_OR_UPDATE, new BatchPreparedStatementSetter() { - @Override - public void setValues(PreparedStatement ps, int i) throws SQLException { - TsKvEntity tsKvEntity = entities.get(i); - ps.setObject(1, tsKvEntity.getEntityId()); - ps.setInt(2, tsKvEntity.getKey()); - ps.setLong(3, tsKvEntity.getTs()); - - if (tsKvEntity.getBooleanValue() != null) { - ps.setBoolean(4, tsKvEntity.getBooleanValue()); - } else { - ps.setNull(4, Types.BOOLEAN); - } - - ps.setString(5, tsKvEntity.getStrValue()); - - if (tsKvEntity.getLongValue() != null) { - ps.setLong(6, tsKvEntity.getLongValue()); - } else { - ps.setNull(6, Types.BIGINT); - } - - if (tsKvEntity.getDoubleValue() != null) { - ps.setDouble(7, tsKvEntity.getDoubleValue()); - } else { - ps.setNull(7, Types.DOUBLE); - } - - ps.setString(8, tsKvEntity.getJsonValue()); - } - - @Override - public int getBatchSize() { - return entities.size(); - } - }); - } -} diff --git a/dao/src/main/java/org/thingsboard/server/dao/sqlts/insert/latest/hsql/HsqlLatestInsertTsRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sqlts/insert/latest/hsql/HsqlLatestInsertTsRepository.java deleted file mode 100644 index c69bb6b1d5..0000000000 --- a/dao/src/main/java/org/thingsboard/server/dao/sqlts/insert/latest/hsql/HsqlLatestInsertTsRepository.java +++ /dev/null @@ -1,85 +0,0 @@ -/** - * Copyright © 2016-2022 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.dao.sqlts.insert.latest.hsql; - -import org.springframework.jdbc.core.BatchPreparedStatementSetter; -import org.springframework.stereotype.Repository; -import org.springframework.transaction.annotation.Transactional; -import org.thingsboard.server.dao.model.sqlts.latest.TsKvLatestEntity; -import org.thingsboard.server.dao.sqlts.insert.AbstractInsertRepository; -import org.thingsboard.server.dao.sqlts.insert.latest.InsertLatestTsRepository; -import org.thingsboard.server.dao.util.HsqlDao; -import org.thingsboard.server.dao.util.SqlTsLatestDao; - -import java.sql.PreparedStatement; -import java.sql.SQLException; -import java.sql.Types; -import java.util.List; - -@SqlTsLatestDao -@HsqlDao -@Repository -@Transactional -public class HsqlLatestInsertTsRepository extends AbstractInsertRepository implements InsertLatestTsRepository { - - private static final String INSERT_OR_UPDATE = - "MERGE INTO ts_kv_latest USING(VALUES ?, ?, ?, ?, ?, ?, ?, ?) " + - "T (entity_id, key, ts, bool_v, str_v, long_v, dbl_v, json_v) " + - "ON (ts_kv_latest.entity_id=T.entity_id " + - "AND ts_kv_latest.key=T.key) " + - "WHEN MATCHED THEN UPDATE SET ts_kv_latest.ts = T.ts, ts_kv_latest.bool_v = T.bool_v, ts_kv_latest.str_v = T.str_v, ts_kv_latest.long_v = T.long_v, ts_kv_latest.dbl_v = T.dbl_v, ts_kv_latest.json_v = T.json_v " + - "WHEN NOT MATCHED THEN INSERT (entity_id, key, ts, bool_v, str_v, long_v, dbl_v, json_v) " + - "VALUES (T.entity_id, T.key, T.ts, T.bool_v, T.str_v, T.long_v, T.dbl_v, T.json_v);"; - - @Override - public void saveOrUpdate(List entities) { - jdbcTemplate.batchUpdate(INSERT_OR_UPDATE, new BatchPreparedStatementSetter() { - @Override - public void setValues(PreparedStatement ps, int i) throws SQLException { - ps.setObject(1, entities.get(i).getEntityId()); - ps.setInt(2, entities.get(i).getKey()); - ps.setLong(3, entities.get(i).getTs()); - - if (entities.get(i).getBooleanValue() != null) { - ps.setBoolean(4, entities.get(i).getBooleanValue()); - } else { - ps.setNull(4, Types.BOOLEAN); - } - - ps.setString(5, entities.get(i).getStrValue()); - - if (entities.get(i).getLongValue() != null) { - ps.setLong(6, entities.get(i).getLongValue()); - } else { - ps.setNull(6, Types.BIGINT); - } - - if (entities.get(i).getDoubleValue() != null) { - ps.setDouble(7, entities.get(i).getDoubleValue()); - } else { - ps.setNull(7, Types.DOUBLE); - } - - ps.setString(8, entities.get(i).getJsonValue()); - } - - @Override - public int getBatchSize() { - return entities.size(); - } - }); - } -} diff --git a/dao/src/main/java/org/thingsboard/server/dao/sqlts/insert/latest/psql/PsqlLatestInsertTsRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sqlts/insert/latest/sql/SqlLatestInsertTsRepository.java similarity index 96% rename from dao/src/main/java/org/thingsboard/server/dao/sqlts/insert/latest/psql/PsqlLatestInsertTsRepository.java rename to dao/src/main/java/org/thingsboard/server/dao/sqlts/insert/latest/sql/SqlLatestInsertTsRepository.java index 726cf56826..451ba29987 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sqlts/insert/latest/psql/PsqlLatestInsertTsRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sqlts/insert/latest/sql/SqlLatestInsertTsRepository.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.dao.sqlts.insert.latest.psql; +package org.thingsboard.server.dao.sqlts.insert.latest.sql; import org.springframework.beans.factory.annotation.Value; import org.springframework.jdbc.core.BatchPreparedStatementSetter; @@ -24,7 +24,7 @@ import org.springframework.transaction.support.TransactionCallbackWithoutResult; import org.thingsboard.server.dao.model.sqlts.latest.TsKvLatestEntity; import org.thingsboard.server.dao.sqlts.insert.AbstractInsertRepository; import org.thingsboard.server.dao.sqlts.insert.latest.InsertLatestTsRepository; -import org.thingsboard.server.dao.util.PsqlTsLatestAnyDao; +import org.thingsboard.server.dao.util.SqlTsLatestAnyDao; import java.sql.PreparedStatement; import java.sql.SQLException; @@ -33,10 +33,10 @@ import java.util.ArrayList; import java.util.List; -@PsqlTsLatestAnyDao +@SqlTsLatestAnyDao @Repository @Transactional -public class PsqlLatestInsertTsRepository extends AbstractInsertRepository implements InsertLatestTsRepository { +public class SqlLatestInsertTsRepository extends AbstractInsertRepository implements InsertLatestTsRepository { @Value("${sql.ts_latest.update_by_latest_ts:true}") private Boolean updateByLatestTs; diff --git a/dao/src/main/java/org/thingsboard/server/dao/sqlts/insert/psql/PsqlInsertTsRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sqlts/insert/sql/SqlInsertTsRepository.java similarity index 93% rename from dao/src/main/java/org/thingsboard/server/dao/sqlts/insert/psql/PsqlInsertTsRepository.java rename to dao/src/main/java/org/thingsboard/server/dao/sqlts/insert/sql/SqlInsertTsRepository.java index adae2c8f3b..c61c852d63 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sqlts/insert/psql/PsqlInsertTsRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sqlts/insert/sql/SqlInsertTsRepository.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.dao.sqlts.insert.psql; +package org.thingsboard.server.dao.sqlts.insert.sql; import org.springframework.jdbc.core.BatchPreparedStatementSetter; import org.springframework.stereotype.Repository; @@ -21,7 +21,6 @@ import org.springframework.transaction.annotation.Transactional; import org.thingsboard.server.dao.model.sqlts.ts.TsKvEntity; import org.thingsboard.server.dao.sqlts.insert.AbstractInsertRepository; import org.thingsboard.server.dao.sqlts.insert.InsertTsRepository; -import org.thingsboard.server.dao.util.PsqlDao; import org.thingsboard.server.dao.util.SqlTsDao; import java.sql.PreparedStatement; @@ -30,10 +29,9 @@ import java.sql.Types; import java.util.List; @SqlTsDao -@PsqlDao @Repository @Transactional -public class PsqlInsertTsRepository extends AbstractInsertRepository implements InsertTsRepository { +public class SqlInsertTsRepository extends AbstractInsertRepository implements InsertTsRepository { private static final String INSERT_ON_CONFLICT_DO_UPDATE = "INSERT INTO ts_kv (entity_id, key, ts, bool_v, str_v, long_v, dbl_v, json_v) VALUES (?, ?, ?, ?, ?, ?, ?, cast(? AS json)) " + "ON CONFLICT (entity_id, key, ts) DO UPDATE SET bool_v = ?, str_v = ?, long_v = ?, dbl_v = ?, json_v = cast(? AS json);"; diff --git a/dao/src/main/java/org/thingsboard/server/dao/sqlts/insert/psql/PsqlPartitioningRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sqlts/insert/sql/SqlPartitioningRepository.java similarity index 80% rename from dao/src/main/java/org/thingsboard/server/dao/sqlts/insert/psql/PsqlPartitioningRepository.java rename to dao/src/main/java/org/thingsboard/server/dao/sqlts/insert/sql/SqlPartitioningRepository.java index 5821b0f7c8..899a195538 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sqlts/insert/psql/PsqlPartitioningRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sqlts/insert/sql/SqlPartitioningRepository.java @@ -13,27 +13,25 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.dao.sqlts.insert.psql; +package org.thingsboard.server.dao.sqlts.insert.sql; import org.springframework.stereotype.Repository; import org.springframework.transaction.annotation.Transactional; -import org.thingsboard.server.dao.timeseries.PsqlPartition; -import org.thingsboard.server.dao.util.PsqlDao; +import org.thingsboard.server.dao.timeseries.SqlPartition; import org.thingsboard.server.dao.util.SqlTsDao; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; @SqlTsDao -@PsqlDao @Repository @Transactional -public class PsqlPartitioningRepository { +public class SqlPartitioningRepository { @PersistenceContext private EntityManager entityManager; - public void save(PsqlPartition partition) { + public void save(SqlPartition partition) { entityManager.createNativeQuery(partition.getQuery()) .executeUpdate(); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sqlts/insert/timescale/TimescaleInsertTsRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sqlts/insert/timescale/TimescaleInsertTsRepository.java index ae8f5d8376..02e71c1c7d 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sqlts/insert/timescale/TimescaleInsertTsRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sqlts/insert/timescale/TimescaleInsertTsRepository.java @@ -21,7 +21,6 @@ import org.springframework.transaction.annotation.Transactional; import org.thingsboard.server.dao.model.sqlts.timescale.ts.TimescaleTsKvEntity; import org.thingsboard.server.dao.sqlts.insert.AbstractInsertRepository; import org.thingsboard.server.dao.sqlts.insert.InsertTsRepository; -import org.thingsboard.server.dao.util.PsqlDao; import org.thingsboard.server.dao.util.TimescaleDBTsDao; import java.sql.PreparedStatement; @@ -30,7 +29,6 @@ import java.sql.Types; import java.util.List; @TimescaleDBTsDao -@PsqlDao @Repository @Transactional public class TimescaleInsertTsRepository extends AbstractInsertRepository implements InsertTsRepository { diff --git a/dao/src/main/java/org/thingsboard/server/dao/sqlts/psql/JpaPsqlTimeseriesDao.java b/dao/src/main/java/org/thingsboard/server/dao/sqlts/sql/JpaSqlTimeseriesDao.java similarity index 85% rename from dao/src/main/java/org/thingsboard/server/dao/sqlts/psql/JpaPsqlTimeseriesDao.java rename to dao/src/main/java/org/thingsboard/server/dao/sqlts/sql/JpaSqlTimeseriesDao.java index 22639dab5a..ce173e046b 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sqlts/psql/JpaPsqlTimeseriesDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sqlts/sql/JpaSqlTimeseriesDao.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.dao.sqlts.psql; +package org.thingsboard.server.dao.sqlts.sql; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; @@ -29,10 +29,9 @@ import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.kv.TsKvEntry; import org.thingsboard.server.dao.model.sqlts.ts.TsKvEntity; import org.thingsboard.server.dao.sqlts.AbstractChunkedAggregationTimeseriesDao; -import org.thingsboard.server.dao.sqlts.insert.psql.PsqlPartitioningRepository; -import org.thingsboard.server.dao.timeseries.PsqlPartition; +import org.thingsboard.server.dao.sqlts.insert.sql.SqlPartitioningRepository; +import org.thingsboard.server.dao.timeseries.SqlPartition; import org.thingsboard.server.dao.timeseries.SqlTsPartitionDate; -import org.thingsboard.server.dao.util.PsqlDao; import org.thingsboard.server.dao.util.SqlTsDao; import java.sql.Connection; @@ -52,15 +51,14 @@ import java.util.concurrent.locks.ReentrantLock; @Component @Slf4j -@PsqlDao @SqlTsDao -public class JpaPsqlTimeseriesDao extends AbstractChunkedAggregationTimeseriesDao { +public class JpaSqlTimeseriesDao extends AbstractChunkedAggregationTimeseriesDao { - private final Map partitions = new ConcurrentHashMap<>(); + private final Map partitions = new ConcurrentHashMap<>(); private static final ReentrantLock partitionCreationLock = new ReentrantLock(); @Autowired - private PsqlPartitioningRepository partitioningRepository; + private SqlPartitioningRepository partitioningRepository; private SqlTsPartitionDate tsFormat; @@ -134,24 +132,24 @@ public class JpaPsqlTimeseriesDao extends AbstractChunkedAggregationTimeseriesDa long partitionEndTs = toMills(localDateTimeEnd); ZonedDateTime zonedDateTime = localDateTimeStart.atZone(ZoneOffset.UTC); String partitionDate = zonedDateTime.format(DateTimeFormatter.ofPattern(tsFormat.getPattern())); - savePartition(new PsqlPartition(partitionStartTs, partitionEndTs, partitionDate)); + savePartition(new SqlPartition(partitionStartTs, partitionEndTs, partitionDate)); } } } - private void savePartition(PsqlPartition psqlPartition) { - if (!partitions.containsKey(psqlPartition.getStart())) { + private void savePartition(SqlPartition sqlPartition) { + if (!partitions.containsKey(sqlPartition.getStart())) { partitionCreationLock.lock(); try { - log.trace("Saving partition: {}", psqlPartition); - partitioningRepository.save(psqlPartition); - log.trace("Adding partition to Set: {}", psqlPartition); - partitions.put(psqlPartition.getStart(), psqlPartition); + log.trace("Saving partition: {}", sqlPartition); + partitioningRepository.save(sqlPartition); + log.trace("Adding partition to Set: {}", sqlPartition); + partitions.put(sqlPartition.getStart(), sqlPartition); } catch (DataIntegrityViolationException ex) { log.trace("Error occurred during partition save:", ex); if (ex.getCause() instanceof ConstraintViolationException) { - log.warn("Saving partition [{}] rejected. Timeseries data will save to the ts_kv_indefinite (DEFAULT) partition.", psqlPartition.getPartitionDate()); - partitions.put(psqlPartition.getStart(), psqlPartition); + log.warn("Saving partition [{}] rejected. Timeseries data will save to the ts_kv_indefinite (DEFAULT) partition.", sqlPartition.getPartitionDate()); + partitions.put(sqlPartition.getStart(), sqlPartition); } else { throw new RuntimeException(ex); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/timeseries/PsqlPartition.java b/dao/src/main/java/org/thingsboard/server/dao/timeseries/SqlPartition.java similarity index 92% rename from dao/src/main/java/org/thingsboard/server/dao/timeseries/PsqlPartition.java rename to dao/src/main/java/org/thingsboard/server/dao/timeseries/SqlPartition.java index fe3019ea25..5c47689b6d 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/timeseries/PsqlPartition.java +++ b/dao/src/main/java/org/thingsboard/server/dao/timeseries/SqlPartition.java @@ -18,7 +18,7 @@ package org.thingsboard.server.dao.timeseries; import lombok.Data; @Data -public class PsqlPartition { +public class SqlPartition { private static final String TABLE_REGEX = "ts_kv_"; @@ -27,7 +27,7 @@ public class PsqlPartition { private String partitionDate; private String query; - public PsqlPartition(long start, long end, String partitionDate) { + public SqlPartition(long start, long end, String partitionDate) { this.start = start; this.end = end; this.partitionDate = partitionDate; diff --git a/dao/src/test/java/org/thingsboard/server/dao/AbstractDaoServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/AbstractDaoServiceTest.java index d54933a19d..9d5e080e2a 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/AbstractDaoServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/AbstractDaoServiceTest.java @@ -28,7 +28,7 @@ import org.thingsboard.server.common.stats.StatsFactory; import org.thingsboard.server.dao.service.DaoSqlTest; @RunWith(SpringRunner.class) -@ContextConfiguration(classes = {JpaDaoConfig.class, PsqlTsDaoConfig.class, PsqlTsLatestDaoConfig.class, SqlTimeseriesDaoConfig.class}) +@ContextConfiguration(classes = {JpaDaoConfig.class, SqlTsDaoConfig.class, SqlTsLatestDaoConfig.class, SqlTimeseriesDaoConfig.class}) @DaoSqlTest @DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS) @TestExecutionListeners({ diff --git a/dao/src/test/java/org/thingsboard/server/dao/AbstractJpaDaoTest.java b/dao/src/test/java/org/thingsboard/server/dao/AbstractJpaDaoTest.java index 5f7fc7caa0..d99692cd5a 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/AbstractJpaDaoTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/AbstractJpaDaoTest.java @@ -30,7 +30,7 @@ import org.thingsboard.server.dao.service.DaoSqlTest; * Created by Valerii Sosliuk on 4/22/2017. */ @RunWith(SpringRunner.class) -@ContextConfiguration(classes = {JpaDaoConfig.class, PsqlTsDaoConfig.class, PsqlTsLatestDaoConfig.class, SqlTimeseriesDaoConfig.class}) +@ContextConfiguration(classes = {JpaDaoConfig.class, SqlTsDaoConfig.class, SqlTsLatestDaoConfig.class, SqlTimeseriesDaoConfig.class}) @DaoSqlTest @TestExecutionListeners({ DependencyInjectionTestExecutionListener.class, diff --git a/dao/src/test/java/org/thingsboard/server/dao/util/DaoTestUtil.java b/dao/src/test/java/org/thingsboard/server/dao/util/DaoTestUtil.java deleted file mode 100644 index 34cceb440b..0000000000 --- a/dao/src/test/java/org/thingsboard/server/dao/util/DaoTestUtil.java +++ /dev/null @@ -1,40 +0,0 @@ -/** - * Copyright © 2016-2022 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.dao.util; - -import org.springframework.jdbc.core.JdbcTemplate; - -import java.sql.DriverManager; - -public class DaoTestUtil { - private static final String POSTGRES_DRIVER_CLASS = "org.postgresql.Driver"; - private static final String H2_DRIVER_CLASS = "org.hsqldb.jdbc.JDBCDriver"; - - - public static SqlDbType getSqlDbType(JdbcTemplate template){ - try { - String driverName = DriverManager.getDriver(template.getDataSource().getConnection().getMetaData().getURL()).getClass().getName(); - if (POSTGRES_DRIVER_CLASS.equals(driverName)) { - return SqlDbType.POSTGRES; - } else if (H2_DRIVER_CLASS.equals(driverName)) { - return SqlDbType.H2; - } - } catch (Exception e) { - e.printStackTrace(); - } - return null; - } -} diff --git a/dao/src/test/java/org/thingsboard/server/dao/util/SqlDbType.java b/dao/src/test/java/org/thingsboard/server/dao/util/SqlDbType.java deleted file mode 100644 index f655955807..0000000000 --- a/dao/src/test/java/org/thingsboard/server/dao/util/SqlDbType.java +++ /dev/null @@ -1,20 +0,0 @@ -/** - * Copyright © 2016-2022 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.dao.util; - -public enum SqlDbType { - POSTGRES, H2; -} diff --git a/dao/src/test/resources/nosql-test.properties b/dao/src/test/resources/nosql-test.properties index 8f661359a9..1c6d2442cf 100644 --- a/dao/src/test/resources/nosql-test.properties +++ b/dao/src/test/resources/nosql-test.properties @@ -11,7 +11,6 @@ spring.jpa.properties.hibernate.jdbc.log.warnings=false spring.jpa.show-sql=false spring.jpa.hibernate.ddl-auto=none -spring.jpa.database-platform=org.hibernate.dialect.PostgreSQL10Dialect spring.datasource.username=postgres spring.datasource.password=postgres spring.datasource.url=jdbc:tc:postgresql:12.8:///thingsboard?TC_DAEMON=true&TC_TMPFS=/testtmpfs:rw&?TC_INITFUNCTION=org.thingsboard.server.dao.PostgreSqlInitializer::initDb diff --git a/dao/src/test/resources/sql-test.properties b/dao/src/test/resources/sql-test.properties index d843b1dc09..b2ded716c4 100644 --- a/dao/src/test/resources/sql-test.properties +++ b/dao/src/test/resources/sql-test.properties @@ -12,7 +12,6 @@ spring.jpa.properties.hibernate.jdbc.log.warnings=false spring.jpa.show-sql=false spring.jpa.hibernate.ddl-auto=none -spring.jpa.database-platform=org.hibernate.dialect.PostgreSQL10Dialect spring.datasource.username=postgres spring.datasource.password=postgres spring.datasource.url=jdbc:tc:postgresql:12.8:///thingsboard?TC_DAEMON=true&TC_TMPFS=/testtmpfs:rw&?TC_INITFUNCTION=org.thingsboard.server.dao.PostgreSqlInitializer::initDb @@ -32,7 +31,6 @@ service.type=monolith #spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation=true #spring.jpa.show-sql=false #spring.jpa.hibernate.ddl-auto=none -#spring.jpa.database-platform=org.hibernate.dialect.PostgreSQL10Dialect # #spring.datasource.username=postgres #spring.datasource.password=postgres diff --git a/docker/tb-node.hybrid.env b/docker/tb-node.hybrid.env index 5f72c6e1a1..e03239bd96 100644 --- a/docker/tb-node.hybrid.env +++ b/docker/tb-node.hybrid.env @@ -2,7 +2,6 @@ DATABASE_TS_TYPE=cassandra CASSANDRA_URL=cassandra:9042 -SPRING_JPA_DATABASE_PLATFORM=org.hibernate.dialect.PostgreSQL10Dialect SPRING_DRIVER_CLASS_NAME=org.postgresql.Driver SPRING_DATASOURCE_URL=jdbc:postgresql://postgres:5432/thingsboard SPRING_DATASOURCE_USERNAME=postgres diff --git a/docker/tb-node.postgres.env b/docker/tb-node.postgres.env index f4e11cd70a..633b8b6fe9 100644 --- a/docker/tb-node.postgres.env +++ b/docker/tb-node.postgres.env @@ -1,7 +1,6 @@ # ThingsBoard server configuration for PostgreSQL database DATABASE_TS_TYPE=sql -SPRING_JPA_DATABASE_PLATFORM=org.hibernate.dialect.PostgreSQL10Dialect SPRING_DRIVER_CLASS_NAME=org.postgresql.Driver SPRING_DATASOURCE_URL=jdbc:postgresql://postgres:5432/thingsboard SPRING_DATASOURCE_USERNAME=postgres diff --git a/msa/tb/docker-cassandra/Dockerfile b/msa/tb/docker-cassandra/Dockerfile index 144c359cc0..5b22efd636 100644 --- a/msa/tb/docker-cassandra/Dockerfile +++ b/msa/tb/docker-cassandra/Dockerfile @@ -54,7 +54,6 @@ ENV DATABASE_TS_TYPE=cassandra ENV PGDATA=/data/db ENV CASSANDRA_DATA=/data/cassandra -ENV SPRING_JPA_DATABASE_PLATFORM=org.hibernate.dialect.PostgreSQL10Dialect ENV SPRING_DRIVER_CLASS_NAME=org.postgresql.Driver ENV SPRING_DATASOURCE_URL=jdbc:postgresql://localhost:5432/thingsboard ENV SPRING_DATASOURCE_USERNAME=${pkg.user} diff --git a/msa/tb/docker-postgres/Dockerfile b/msa/tb/docker-postgres/Dockerfile index 6b6dbc0011..720f770007 100644 --- a/msa/tb/docker-postgres/Dockerfile +++ b/msa/tb/docker-postgres/Dockerfile @@ -50,7 +50,6 @@ ENV DATABASE_TS_TYPE=sql ENV PGDATA=/data/db ENV PATH=$PATH:/usr/lib/postgresql/$PG_MAJOR/bin -ENV SPRING_JPA_DATABASE_PLATFORM=org.hibernate.dialect.PostgreSQL10Dialect ENV SPRING_DRIVER_CLASS_NAME=org.postgresql.Driver ENV SPRING_DATASOURCE_URL=jdbc:postgresql://localhost:5432/thingsboard ENV SPRING_DATASOURCE_USERNAME=${pkg.user} diff --git a/msa/tb/docker-tb/Dockerfile b/msa/tb/docker-tb/Dockerfile deleted file mode 100644 index 6deea6d274..0000000000 --- a/msa/tb/docker-tb/Dockerfile +++ /dev/null @@ -1,61 +0,0 @@ -# -# Copyright © 2016-2022 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. -# - -FROM thingsboard/openjdk11 - -COPY logback.xml ${pkg.name}.conf start-db.sh stop-db.sh start-tb.sh upgrade-tb.sh install-tb.sh ${pkg.name}.deb /tmp/ - -RUN chmod a+x /tmp/*.sh \ - && mv /tmp/start-tb.sh /usr/bin \ - && mv /tmp/upgrade-tb.sh /usr/bin \ - && mv /tmp/install-tb.sh /usr/bin \ - && mv /tmp/start-db.sh /usr/bin \ - && mv /tmp/stop-db.sh /usr/bin - -RUN dpkg -i /tmp/${pkg.name}.deb -RUN rm /tmp/${pkg.name}.deb - -RUN systemctl --no-reload disable --now ${pkg.name}.service > /dev/null 2>&1 || : - -RUN mv /tmp/logback.xml ${pkg.installFolder}/conf \ - && mv /tmp/${pkg.name}.conf ${pkg.installFolder}/conf - -ENV DATA_FOLDER=/data - -ENV HTTP_BIND_PORT=9090 -ENV DATABASE_TS_TYPE=sql - -ENV SPRING_JPA_DATABASE_PLATFORM=org.hibernate.dialect.HSQLDialect -ENV SPRING_DRIVER_CLASS_NAME=org.hsqldb.jdbc.JDBCDriver -ENV SPRING_DATASOURCE_URL=jdbc:hsqldb:file:/data/db/thingsboardDb;sql.enforce_size=false;hsqldb.log_size=5 -ENV SPRING_DATASOURCE_USERNAME=sa -ENV SPRING_DATASOURCE_PASSWORD= - -RUN mkdir -p /data -RUN chown -R ${pkg.user}:${pkg.user} /data - -RUN chmod 555 ${pkg.installFolder}/bin/${pkg.name}.jar - -USER ${pkg.user} - -EXPOSE 9090 -EXPOSE 1883 -EXPOSE 5683/udp -EXPOSE 5685/udp - -VOLUME ["/data"] - -CMD ["start-tb.sh"] diff --git a/msa/tb/docker-tb/start-db.sh b/msa/tb/docker-tb/start-db.sh deleted file mode 100644 index 6dfddf8a7a..0000000000 --- a/msa/tb/docker-tb/start-db.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/bash -# -# Copyright © 2016-2022 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. -# - -# Do nothing \ No newline at end of file diff --git a/msa/tb/docker-tb/stop-db.sh b/msa/tb/docker-tb/stop-db.sh deleted file mode 100644 index 6dfddf8a7a..0000000000 --- a/msa/tb/docker-tb/stop-db.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/bash -# -# Copyright © 2016-2022 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. -# - -# Do nothing \ No newline at end of file diff --git a/msa/tb/pom.xml b/msa/tb/pom.xml index a1aa1c0e16..13f6ef3226 100644 --- a/msa/tb/pom.xml +++ b/msa/tb/pom.xml @@ -35,7 +35,6 @@ UTF-8 ${basedir}/../.. thingsboard - tb tb-postgres tb-cassandra /usr/share/${pkg.name} @@ -59,25 +58,6 @@ org.apache.maven.plugins maven-dependency-plugin - - copy-tb-deb - package - - copy - - - - - org.thingsboard - application - deb - deb - ${pkg.name}.deb - ${project.build.directory}/docker-tb - - - - copy-tb-postgres-deb package @@ -122,27 +102,7 @@ org.apache.maven.plugins maven-resources-plugin - - copy-docker-tb-config - process-resources - - copy-resources - - - ${project.build.directory}/docker-tb - - - docker - true - - - docker-tb - true - - - - - + copy-docker-tb-postgres-config process-resources @@ -188,32 +148,6 @@ com.spotify dockerfile-maven-plugin - - build-docker-tb-image - pre-integration-test - - build - - - ${dockerfile.skip} - ${docker.repo}/${tb.docker.name} - true - false - ${project.build.directory}/docker-tb - - - - tag-docker-tb-image - pre-integration-test - - tag - - - ${dockerfile.skip} - ${docker.repo}/${tb.docker.name} - ${project.version} - - build-docker-tb-postgres-image pre-integration-test @@ -284,28 +218,6 @@ com.spotify dockerfile-maven-plugin - - push-latest-docker-tb-image - pre-integration-test - - push - - - latest - ${docker.repo}/${tb.docker.name} - - - - push-version-docker-tb-image - pre-integration-test - - push - - - ${project.version} - ${docker.repo}/${tb.docker.name} - - push-latest-docker-tb-postgres-image pre-integration-test @@ -368,48 +280,6 @@ org.codehaus.mojo exec-maven-plugin - - push-latest-docker-amd-arm-tb-images - pre-integration-test - - exec - - - docker - ${project.build.directory}/docker-tb - - buildx - build - -t - ${docker.repo}/${tb.docker.name}:latest - --platform=linux/amd64,linux/arm64 - -o - type=registry - . - - - - - push-version-docker-amd-arm-tb-images - pre-integration-test - - exec - - - docker - ${project.build.directory}/docker-tb - - buildx - build - -t - ${docker.repo}/${tb.docker.name}:${project.version} - --platform=linux/amd64,linux/arm64 - -o - type=registry - . - - - push-latest-docker-amd-arm-tb-postgres-images pre-integration-test diff --git a/pom.xml b/pom.xml index 78c4ae6fa2..9273e7bebf 100755 --- a/pom.xml +++ b/pom.xml @@ -125,7 +125,6 @@ 4.1.0 4.3.1.0 2.7.2 - 2.6.1 1.5.2 5.7.2 2.6.0 @@ -1659,11 +1658,6 @@ bcpkix-jdk15on ${bouncycastle.version} - - org.hsqldb - hsqldb - ${hsqldb.version} - org.testcontainers postgresql
-

Email verification code:

+

Your verification code:

- This code will expire in ${expiredTime} minutes. To get a new one, click Resend code on the Thingsboard platform. + This code will expire in ${expirationTimeSeconds} seconds.
- If you didn't request for this code, then you can just ignore this email; access won't provided. + If you didn't request this code, you can ignore this email.
-

${verificationCode}

+

${сode}

-

${сode}

+

${code}