Merge pull request #12159 from AndriiLandiak/feature/edge-limit
Edges maximum number configuration
This commit is contained in:
		
						commit
						03ac89bdde
					
				@ -416,6 +416,9 @@ public class HomePageApiTest extends AbstractControllerTest {
 | 
			
		||||
        Assert.assertEquals(DEFAULT_DASHBOARDS_COUNT, usageInfo.getDashboards());
 | 
			
		||||
        Assert.assertEquals(configuration.getMaxDashboards(), usageInfo.getMaxDashboards());
 | 
			
		||||
 | 
			
		||||
        Assert.assertEquals(0, usageInfo.getEdges());
 | 
			
		||||
        Assert.assertEquals(configuration.getMaxEdges(), usageInfo.getMaxEdges());
 | 
			
		||||
 | 
			
		||||
        Assert.assertEquals(0, usageInfo.getTransportMessages());
 | 
			
		||||
        Assert.assertEquals(configuration.getMaxTransportMessages(), usageInfo.getMaxTransportMessages());
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -50,6 +50,7 @@ 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.DeviceProfileAlarm;
 | 
			
		||||
import org.thingsboard.server.common.data.device.profile.SimpleAlarmConditionSpec;
 | 
			
		||||
import org.thingsboard.server.common.data.edge.Edge;
 | 
			
		||||
import org.thingsboard.server.common.data.id.AlarmId;
 | 
			
		||||
import org.thingsboard.server.common.data.id.DeviceId;
 | 
			
		||||
import org.thingsboard.server.common.data.id.TenantId;
 | 
			
		||||
@ -125,7 +126,8 @@ import static org.thingsboard.server.common.data.notification.rule.trigger.confi
 | 
			
		||||
@DaoSqlTest
 | 
			
		||||
@TestPropertySource(properties = {
 | 
			
		||||
        "transport.http.enabled=true",
 | 
			
		||||
        "notification_system.rules.deduplication_durations=RATE_LIMITS:10000"
 | 
			
		||||
        "notification_system.rules.deduplication_durations=RATE_LIMITS:10000",
 | 
			
		||||
        "edges.enabled=true"
 | 
			
		||||
})
 | 
			
		||||
public class NotificationRuleApiTest extends AbstractNotificationApiTest {
 | 
			
		||||
 | 
			
		||||
@ -372,6 +374,7 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest {
 | 
			
		||||
            profileConfiguration.setMaxUsers(limit);
 | 
			
		||||
            profileConfiguration.setMaxDashboards(limit);
 | 
			
		||||
            profileConfiguration.setMaxRuleChains(limit);
 | 
			
		||||
            profileConfiguration.setMaxEdges(limit);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        EntitiesLimitNotificationRuleTriggerConfig triggerConfig = EntitiesLimitNotificationRuleTriggerConfig.builder()
 | 
			
		||||
@ -419,6 +422,19 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest {
 | 
			
		||||
            assertThat(notification.getText()).isEqualTo("Rule chains usage: " + threshold + "/" + limit + " (80%)");
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        checkNotificationAfter(() -> {
 | 
			
		||||
            for (int i = 1; i <= threshold; i++) {
 | 
			
		||||
                Edge edge = new Edge();
 | 
			
		||||
                edge.setName(i + "");
 | 
			
		||||
                edge.setType("default");
 | 
			
		||||
                edge.setSecret("secret_" + i);
 | 
			
		||||
                edge.setRoutingKey("routingKey_" + i);
 | 
			
		||||
                doPost("/api/edge", edge);
 | 
			
		||||
            }
 | 
			
		||||
        }, notification -> {
 | 
			
		||||
            assertThat(notification.getText()).isEqualTo("Edges usage: " + threshold + "/" + limit + " (80%)");
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        triggerConfig.setThreshold(1.0f);
 | 
			
		||||
        rule.setTriggerConfig(triggerConfig);
 | 
			
		||||
        loginSysAdmin();
 | 
			
		||||
 | 
			
		||||
@ -19,6 +19,7 @@ import lombok.Data;
 | 
			
		||||
 | 
			
		||||
@Data
 | 
			
		||||
public class UsageInfo {
 | 
			
		||||
 | 
			
		||||
    private long devices;
 | 
			
		||||
    private long maxDevices;
 | 
			
		||||
    private long assets;
 | 
			
		||||
@ -29,6 +30,8 @@ public class UsageInfo {
 | 
			
		||||
    private long maxUsers;
 | 
			
		||||
    private long dashboards;
 | 
			
		||||
    private long maxDashboards;
 | 
			
		||||
    private long edges;
 | 
			
		||||
    private long maxEdges;
 | 
			
		||||
 | 
			
		||||
    private long transportMessages;
 | 
			
		||||
    private long maxTransportMessages;
 | 
			
		||||
@ -43,4 +46,5 @@ public class UsageInfo {
 | 
			
		||||
    private Boolean smsEnabled;
 | 
			
		||||
    private long alarms;
 | 
			
		||||
    private long maxAlarms;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -24,6 +24,8 @@ import org.thingsboard.server.common.data.ApiUsageRecordKey;
 | 
			
		||||
import org.thingsboard.server.common.data.EntityType;
 | 
			
		||||
import org.thingsboard.server.common.data.TenantProfileType;
 | 
			
		||||
 | 
			
		||||
import java.io.Serial;
 | 
			
		||||
 | 
			
		||||
@Schema
 | 
			
		||||
@AllArgsConstructor
 | 
			
		||||
@NoArgsConstructor
 | 
			
		||||
@ -31,6 +33,7 @@ import org.thingsboard.server.common.data.TenantProfileType;
 | 
			
		||||
@Data
 | 
			
		||||
public class DefaultTenantProfileConfiguration implements TenantProfileConfiguration {
 | 
			
		||||
 | 
			
		||||
    @Serial
 | 
			
		||||
    private static final long serialVersionUID = -7134932690332578595L;
 | 
			
		||||
 | 
			
		||||
    private long maxDevices;
 | 
			
		||||
@ -39,6 +42,7 @@ public class DefaultTenantProfileConfiguration implements TenantProfileConfigura
 | 
			
		||||
    private long maxUsers;
 | 
			
		||||
    private long maxDashboards;
 | 
			
		||||
    private long maxRuleChains;
 | 
			
		||||
    private long maxEdges;
 | 
			
		||||
    private long maxResourcesInBytes;
 | 
			
		||||
    private long maxOtaPackagesInBytes;
 | 
			
		||||
    private long maxResourceSize;
 | 
			
		||||
@ -131,27 +135,18 @@ public class DefaultTenantProfileConfiguration implements TenantProfileConfigura
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public long getProfileThreshold(ApiUsageRecordKey key) {
 | 
			
		||||
        switch (key) {
 | 
			
		||||
            case TRANSPORT_MSG_COUNT:
 | 
			
		||||
                return maxTransportMessages;
 | 
			
		||||
            case TRANSPORT_DP_COUNT:
 | 
			
		||||
                return maxTransportDataPoints;
 | 
			
		||||
            case JS_EXEC_COUNT:
 | 
			
		||||
                return maxJSExecutions;
 | 
			
		||||
            case TBEL_EXEC_COUNT:
 | 
			
		||||
                return maxTbelExecutions;
 | 
			
		||||
            case RE_EXEC_COUNT:
 | 
			
		||||
                return maxREExecutions;
 | 
			
		||||
            case STORAGE_DP_COUNT:
 | 
			
		||||
                return maxDPStorageDays;
 | 
			
		||||
            case EMAIL_EXEC_COUNT:
 | 
			
		||||
                return maxEmails;
 | 
			
		||||
            case SMS_EXEC_COUNT:
 | 
			
		||||
                return maxSms;
 | 
			
		||||
            case CREATED_ALARMS_COUNT:
 | 
			
		||||
                return maxCreatedAlarms;
 | 
			
		||||
        }
 | 
			
		||||
        return 0L;
 | 
			
		||||
        return switch (key) {
 | 
			
		||||
            case TRANSPORT_MSG_COUNT -> maxTransportMessages;
 | 
			
		||||
            case TRANSPORT_DP_COUNT -> maxTransportDataPoints;
 | 
			
		||||
            case JS_EXEC_COUNT -> maxJSExecutions;
 | 
			
		||||
            case TBEL_EXEC_COUNT -> maxTbelExecutions;
 | 
			
		||||
            case RE_EXEC_COUNT -> maxREExecutions;
 | 
			
		||||
            case STORAGE_DP_COUNT -> maxDPStorageDays;
 | 
			
		||||
            case EMAIL_EXEC_COUNT -> maxEmails;
 | 
			
		||||
            case SMS_EXEC_COUNT -> maxSms;
 | 
			
		||||
            case CREATED_ALARMS_COUNT -> maxCreatedAlarms;
 | 
			
		||||
            default -> 0L;
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
@ -170,22 +165,16 @@ public class DefaultTenantProfileConfiguration implements TenantProfileConfigura
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public long getEntitiesLimit(EntityType entityType) {
 | 
			
		||||
        switch (entityType) {
 | 
			
		||||
            case DEVICE:
 | 
			
		||||
                return maxDevices;
 | 
			
		||||
            case ASSET:
 | 
			
		||||
                return maxAssets;
 | 
			
		||||
            case CUSTOMER:
 | 
			
		||||
                return maxCustomers;
 | 
			
		||||
            case USER:
 | 
			
		||||
                return maxUsers;
 | 
			
		||||
            case DASHBOARD:
 | 
			
		||||
                return maxDashboards;
 | 
			
		||||
            case RULE_CHAIN:
 | 
			
		||||
                return maxRuleChains;
 | 
			
		||||
            default:
 | 
			
		||||
                return 0;
 | 
			
		||||
        }
 | 
			
		||||
        return switch (entityType) {
 | 
			
		||||
            case DEVICE -> maxDevices;
 | 
			
		||||
            case ASSET -> maxAssets;
 | 
			
		||||
            case CUSTOMER -> maxCustomers;
 | 
			
		||||
            case USER -> maxUsers;
 | 
			
		||||
            case DASHBOARD -> maxDashboards;
 | 
			
		||||
            case RULE_CHAIN -> maxRuleChains;
 | 
			
		||||
            case EDGE -> maxEdges;
 | 
			
		||||
            default -> 0;
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
@ -197,4 +186,5 @@ public class DefaultTenantProfileConfiguration implements TenantProfileConfigura
 | 
			
		||||
    public int getMaxRuleNodeExecsPerMessage() {
 | 
			
		||||
        return maxRuleNodeExecutionsPerMessage;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -25,6 +25,7 @@ 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.dao.Dao;
 | 
			
		||||
import org.thingsboard.server.dao.TenantEntityDao;
 | 
			
		||||
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.Optional;
 | 
			
		||||
@ -34,7 +35,7 @@ import java.util.UUID;
 | 
			
		||||
 * The Interface EdgeDao.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
public interface EdgeDao extends Dao<Edge> {
 | 
			
		||||
public interface EdgeDao extends Dao<Edge>, TenantEntityDao {
 | 
			
		||||
 | 
			
		||||
    Edge save(TenantId tenantId, Edge edge);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -61,6 +61,7 @@ import org.thingsboard.server.common.data.rule.RuleChain;
 | 
			
		||||
import org.thingsboard.server.common.data.rule.RuleNode;
 | 
			
		||||
import org.thingsboard.server.dao.attributes.AttributesService;
 | 
			
		||||
import org.thingsboard.server.dao.entity.AbstractCachedEntityService;
 | 
			
		||||
import org.thingsboard.server.dao.entity.EntityCountService;
 | 
			
		||||
import org.thingsboard.server.dao.eventsourcing.ActionEntityEvent;
 | 
			
		||||
import org.thingsboard.server.dao.eventsourcing.DeleteEntityEvent;
 | 
			
		||||
import org.thingsboard.server.dao.eventsourcing.SaveEntityEvent;
 | 
			
		||||
@ -127,6 +128,9 @@ public class EdgeServiceImpl extends AbstractCachedEntityService<EdgeCacheKey, E
 | 
			
		||||
    @Lazy
 | 
			
		||||
    private RelatedEdgesService relatedEdgesService;
 | 
			
		||||
 | 
			
		||||
    @Autowired
 | 
			
		||||
    private EntityCountService countService;
 | 
			
		||||
 | 
			
		||||
    @Value("${edges.enabled}")
 | 
			
		||||
    @Getter
 | 
			
		||||
    private boolean edgesEnabled;
 | 
			
		||||
@ -198,6 +202,9 @@ public class EdgeServiceImpl extends AbstractCachedEntityService<EdgeCacheKey, E
 | 
			
		||||
            publishEvictEvent(evictEvent);
 | 
			
		||||
            eventPublisher.publishEvent(SaveEntityEvent.builder().tenantId(savedEdge.getTenantId())
 | 
			
		||||
                    .entityId(savedEdge.getId()).entity(savedEdge).created(edge.getId() == null).build());
 | 
			
		||||
            if (edge.getId() == null) {
 | 
			
		||||
                countService.publishCountEntityEvictEvent(savedEdge.getTenantId(), EntityType.EDGE);
 | 
			
		||||
            }
 | 
			
		||||
            return savedEdge;
 | 
			
		||||
        } catch (Exception t) {
 | 
			
		||||
            handleEvictEvent(evictEvent);
 | 
			
		||||
@ -252,6 +259,7 @@ public class EdgeServiceImpl extends AbstractCachedEntityService<EdgeCacheKey, E
 | 
			
		||||
        edgeDao.removeById(tenantId, edgeId.getId());
 | 
			
		||||
 | 
			
		||||
        publishEvictEvent(new EdgeCacheEvictEvent(edge.getTenantId(), edge.getName(), null));
 | 
			
		||||
        countService.publishCountEntityEvictEvent(tenantId, EntityType.EDGE);
 | 
			
		||||
        eventPublisher.publishEvent(DeleteEntityEvent.builder().tenantId(tenantId).entityId(edgeId).build());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -604,6 +612,11 @@ public class EdgeServiceImpl extends AbstractCachedEntityService<EdgeCacheKey, E
 | 
			
		||||
        return result;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public long countByTenantId(TenantId tenantId) {
 | 
			
		||||
        return edgeDao.countByTenantId(tenantId);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public Optional<HasId<?>> findEntity(TenantId tenantId, EntityId entityId) {
 | 
			
		||||
        return Optional.ofNullable(findEdgeById(tenantId, new EdgeId(entityId.getId())));
 | 
			
		||||
 | 
			
		||||
@ -18,6 +18,7 @@ package org.thingsboard.server.dao.service.validator;
 | 
			
		||||
import lombok.AllArgsConstructor;
 | 
			
		||||
import org.springframework.stereotype.Component;
 | 
			
		||||
import org.thingsboard.server.common.data.Customer;
 | 
			
		||||
import org.thingsboard.server.common.data.EntityType;
 | 
			
		||||
import org.thingsboard.server.common.data.StringUtils;
 | 
			
		||||
import org.thingsboard.server.common.data.edge.Edge;
 | 
			
		||||
import org.thingsboard.server.common.data.id.CustomerId;
 | 
			
		||||
@ -40,6 +41,7 @@ public class EdgeDataValidator extends DataValidator<Edge> {
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void validateCreate(TenantId tenantId, Edge edge) {
 | 
			
		||||
        validateNumberOfEntitiesPerTenant(tenantId, EntityType.EDGE);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
@ -76,4 +78,5 @@ public class EdgeDataValidator extends DataValidator<Edge> {
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -143,6 +143,9 @@ public interface EdgeRepository extends JpaRepository<EdgeEntity, UUID> {
 | 
			
		||||
    @Query("SELECT DISTINCT d.type FROM EdgeEntity d WHERE d.tenantId = :tenantId")
 | 
			
		||||
    List<String> findTenantEdgeTypes(@Param("tenantId") UUID tenantId);
 | 
			
		||||
 | 
			
		||||
    @Query("SELECT count(*) FROM EdgeEntity e WHERE e.tenantId = :tenantId")
 | 
			
		||||
    Long countByTenantId(@Param("tenantId") UUID tenantId);
 | 
			
		||||
 | 
			
		||||
    EdgeEntity findByTenantIdAndName(UUID tenantId, String name);
 | 
			
		||||
 | 
			
		||||
    List<EdgeEntity> findEdgesByTenantIdAndCustomerIdAndIdIn(UUID tenantId, UUID customerId, List<UUID> edgeIds);
 | 
			
		||||
 | 
			
		||||
@ -214,6 +214,11 @@ public class JpaEdgeDao extends JpaAbstractDao<EdgeEntity, Edge> implements Edge
 | 
			
		||||
                        DaoUtil.toPageable(pageLink)));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public Long countByTenantId(TenantId tenantId) {
 | 
			
		||||
        return edgeRepository.countByTenantId(tenantId.getId());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public EntityType getEntityType() {
 | 
			
		||||
        return EntityType.EDGE;
 | 
			
		||||
 | 
			
		||||
@ -63,6 +63,8 @@ public class BasicUsageInfoService implements UsageInfoService {
 | 
			
		||||
        usageInfo.setMaxUsers(profileConfiguration.getMaxUsers());
 | 
			
		||||
        usageInfo.setDashboards(countService.countByTenantIdAndEntityType(tenantId, EntityType.DASHBOARD));
 | 
			
		||||
        usageInfo.setMaxDashboards(profileConfiguration.getMaxDashboards());
 | 
			
		||||
        usageInfo.setEdges(countService.countByTenantIdAndEntityType(tenantId, EntityType.EDGE));
 | 
			
		||||
        usageInfo.setMaxEdges(profileConfiguration.getMaxEdges());
 | 
			
		||||
 | 
			
		||||
        usageInfo.setMaxAlarms(profileConfiguration.getMaxCreatedAlarms());
 | 
			
		||||
        usageInfo.setMaxTransportMessages(profileConfiguration.getMaxTransportMessages());
 | 
			
		||||
 | 
			
		||||
@ -17,6 +17,7 @@ package org.thingsboard.server.dao.service;
 | 
			
		||||
 | 
			
		||||
import com.datastax.oss.driver.api.core.uuid.Uuids;
 | 
			
		||||
import com.fasterxml.jackson.databind.node.ObjectNode;
 | 
			
		||||
import org.junit.After;
 | 
			
		||||
import org.junit.Assert;
 | 
			
		||||
import org.junit.Test;
 | 
			
		||||
import org.junit.jupiter.api.Assertions;
 | 
			
		||||
@ -26,6 +27,7 @@ import org.thingsboard.server.common.data.Customer;
 | 
			
		||||
import org.thingsboard.server.common.data.EntitySubtype;
 | 
			
		||||
import org.thingsboard.server.common.data.StringUtils;
 | 
			
		||||
import org.thingsboard.server.common.data.Tenant;
 | 
			
		||||
import org.thingsboard.server.common.data.TenantProfile;
 | 
			
		||||
import org.thingsboard.server.common.data.edge.Edge;
 | 
			
		||||
import org.thingsboard.server.common.data.id.CustomerId;
 | 
			
		||||
import org.thingsboard.server.common.data.id.TenantId;
 | 
			
		||||
@ -35,10 +37,13 @@ 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.DefaultTenantProfileConfiguration;
 | 
			
		||||
import org.thingsboard.server.dao.customer.CustomerService;
 | 
			
		||||
import org.thingsboard.server.dao.edge.EdgeService;
 | 
			
		||||
import org.thingsboard.server.dao.exception.DataValidationException;
 | 
			
		||||
import org.thingsboard.server.dao.rule.RuleChainService;
 | 
			
		||||
import org.thingsboard.server.dao.tenant.TbTenantProfileCache;
 | 
			
		||||
import org.thingsboard.server.dao.tenant.TenantProfileService;
 | 
			
		||||
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.Arrays;
 | 
			
		||||
@ -51,6 +56,10 @@ import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID;
 | 
			
		||||
@DaoSqlTest
 | 
			
		||||
public class EdgeServiceTest extends AbstractServiceTest {
 | 
			
		||||
 | 
			
		||||
    @Autowired
 | 
			
		||||
    TbTenantProfileCache tbTenantProfileCache;
 | 
			
		||||
    @Autowired
 | 
			
		||||
    TenantProfileService tenantProfileService;
 | 
			
		||||
    @Autowired
 | 
			
		||||
    CustomerService customerService;
 | 
			
		||||
    @Autowired
 | 
			
		||||
@ -58,7 +67,13 @@ public class EdgeServiceTest extends AbstractServiceTest {
 | 
			
		||||
    @Autowired
 | 
			
		||||
    RuleChainService ruleChainService;
 | 
			
		||||
 | 
			
		||||
    private IdComparator<Edge> idComparator = new IdComparator<>();
 | 
			
		||||
    private final IdComparator<Edge> idComparator = new IdComparator<>();
 | 
			
		||||
 | 
			
		||||
    @After
 | 
			
		||||
    public void after() {
 | 
			
		||||
        tenantService.deleteTenant(tenantId);
 | 
			
		||||
        tenantProfileService.deleteTenantProfiles(tenantId);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    public void testSaveEdge() {
 | 
			
		||||
@ -113,6 +128,34 @@ public class EdgeServiceTest extends AbstractServiceTest {
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    public void testSaveEdgesWithInfiniteMaxEdgeLimit() {
 | 
			
		||||
        TenantProfile defaultTenantProfile = tenantProfileService.findDefaultTenantProfile(tenantId);
 | 
			
		||||
        defaultTenantProfile.getProfileData().setConfiguration(DefaultTenantProfileConfiguration.builder().maxEdges(Long.MAX_VALUE).build());
 | 
			
		||||
        tenantProfileService.saveTenantProfile(tenantId, defaultTenantProfile);
 | 
			
		||||
 | 
			
		||||
        Edge savedEdge = edgeService.saveEdge(constructEdge("My edge", "default"));
 | 
			
		||||
        edgeService.deleteEdge(tenantId, savedEdge.getId());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    public void testSaveEdgesWithMaxEdgeOutOfLimit() {
 | 
			
		||||
        TenantProfile defaultTenantProfile = tenantProfileService.findDefaultTenantProfile(tenantId);
 | 
			
		||||
        defaultTenantProfile.getProfileData().setConfiguration(DefaultTenantProfileConfiguration.builder().maxEdges(1).build());
 | 
			
		||||
        tenantProfileService.saveTenantProfile(tenantId, defaultTenantProfile);
 | 
			
		||||
        tbTenantProfileCache.evict(defaultTenantProfile.getId());
 | 
			
		||||
 | 
			
		||||
        Assert.assertEquals(0, edgeService.countByTenantId(tenantId));
 | 
			
		||||
 | 
			
		||||
        Edge savedEdge = edgeService.saveEdge(constructEdge("My first edge", "default"));
 | 
			
		||||
        Assert.assertEquals(1, edgeService.countByTenantId(tenantId));
 | 
			
		||||
 | 
			
		||||
        Assertions.assertThrows(DataValidationException.class, () -> {
 | 
			
		||||
            edgeService.saveEdge(constructEdge("My second edge that out of maxEdgeCount limit", "default"));
 | 
			
		||||
        });
 | 
			
		||||
        edgeService.deleteEdge(tenantId, savedEdge.getId());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    public void testAssignEdgeToNonExistentCustomer() {
 | 
			
		||||
        Edge edge = constructEdge("My edge", "default");
 | 
			
		||||
@ -668,5 +711,8 @@ public class EdgeServiceTest extends AbstractServiceTest {
 | 
			
		||||
        PageData<Edge> edgesPageData = edgeService.findEdgesByTenantProfileId(tenant2.getTenantProfileId(),
 | 
			
		||||
                new PageLink(1000));
 | 
			
		||||
        Assert.assertEquals(2, edgesPageData.getTotalElements());
 | 
			
		||||
        tenantService.deleteTenant(tenant1.getId());
 | 
			
		||||
        tenantService.deleteTenant(tenant2.getId());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -111,6 +111,22 @@
 | 
			
		||||
            <mat-hint></mat-hint>
 | 
			
		||||
          </mat-form-field>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="flex flex-1 flex-row xs:flex-col gt-xs:gap-4">
 | 
			
		||||
          <mat-form-field class="mat-block flex-1" appearance="fill">
 | 
			
		||||
            <mat-label translate>tenant-profile.maximum-edges</mat-label>
 | 
			
		||||
            <input matInput required min="0" step="1"
 | 
			
		||||
                   formControlName="maxEdges"
 | 
			
		||||
                   type="number">
 | 
			
		||||
            <mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('maxEdges').hasError('required')">
 | 
			
		||||
              {{ 'tenant-profile.maximum-edges-required' | translate }}
 | 
			
		||||
            </mat-error>
 | 
			
		||||
            <mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('maxEdges').hasError('min')">
 | 
			
		||||
              {{ 'tenant-profile.maximum-edges-range' | translate }}
 | 
			
		||||
            </mat-error>
 | 
			
		||||
            <mat-hint></mat-hint>
 | 
			
		||||
          </mat-form-field>
 | 
			
		||||
          <div class="flex-1"></div>
 | 
			
		||||
        </div>
 | 
			
		||||
      </ng-template>
 | 
			
		||||
    </mat-expansion-panel>
 | 
			
		||||
  </fieldset>
 | 
			
		||||
 | 
			
		||||
@ -65,6 +65,7 @@ export class DefaultTenantProfileConfigurationComponent implements ControlValueA
 | 
			
		||||
      maxUsers: [null, [Validators.required, Validators.min(0)]],
 | 
			
		||||
      maxDashboards: [null, [Validators.required, Validators.min(0)]],
 | 
			
		||||
      maxRuleChains: [null, [Validators.required, Validators.min(0)]],
 | 
			
		||||
      maxEdges: [null, [Validators.required, Validators.min(0)]],
 | 
			
		||||
      maxResourcesInBytes: [null, [Validators.required, Validators.min(0)]],
 | 
			
		||||
      maxOtaPackagesInBytes: [null, [Validators.required, Validators.min(0)]],
 | 
			
		||||
      maxResourceSize: [null, [Validators.required, Validators.min(0)]],
 | 
			
		||||
 | 
			
		||||
@ -5146,6 +5146,9 @@
 | 
			
		||||
        "maximum-dashboards": "الحد الأقصى لعدد لوحات التحكم",
 | 
			
		||||
        "maximum-dashboards-required": "الحد الأقصى لعدد لوحات التحكم مطلوب.",
 | 
			
		||||
        "maximum-dashboards-range": "لا يمكن أن يكون الحد الأقصى لعدد لوحات التحكم سالبًا",
 | 
			
		||||
        "maximum-edges": "الحد الأقصى لعدد الحواف",
 | 
			
		||||
        "maximum-edges-required": "يجب أن يكون الحد الأقصى لعدد الحواف.",
 | 
			
		||||
        "maximum-edges-range": "لا يمكن أن يكون الحد الأقصى لعدد الحواف سالبًا",
 | 
			
		||||
        "maximum-rule-chains": "الحد الأقصى لعدد سلاسل القواعد",
 | 
			
		||||
        "maximum-rule-chains-required": "الحد الأقصى لعدد سلاسل القواعد مطلوب.",
 | 
			
		||||
        "maximum-rule-chains-range": "لا يمكن أن يكون الحد الأقصى لعدد سلاسل القواعد سالبًا",
 | 
			
		||||
 | 
			
		||||
@ -4142,6 +4142,9 @@
 | 
			
		||||
        "maximum-dashboards": "Nº Màxim de panells (0 - sense límit)",
 | 
			
		||||
        "maximum-dashboards-required": "Cal Nº Màxim de panells.",
 | 
			
		||||
        "maximum-dashboards-range": "Nº Màxim de panells no pot ser negatiu",
 | 
			
		||||
        "maximum-edges": "Nº Màxim de d'arestes (0 - sense límit)",
 | 
			
		||||
        "maximum-edges-required": "Cal Nº Màxim de d'arestes.",
 | 
			
		||||
        "maximum-edges-range": "Nº Màxim de d'arestes no pot ser negatiu",
 | 
			
		||||
        "maximum-rule-chains": "Nº Màxim de cadenes de regles (0 - sense límit)",
 | 
			
		||||
        "maximum-rule-chains-required": "Cal Nº Màxim de cadenes de regles.",
 | 
			
		||||
        "maximum-rule-chains-range": "Nº Màxim de cadenes de regles no pot ser negatiu",
 | 
			
		||||
 | 
			
		||||
@ -2522,6 +2522,9 @@
 | 
			
		||||
        "maximum-dashboards": "Maximální počet dashboardů (0 - neomezeno)",
 | 
			
		||||
        "maximum-dashboards-required": "Maximální počet dashboardů je povinný.",
 | 
			
		||||
        "maximum-dashboards-range": "Maximální počet dashboardů nemůže být záporný",
 | 
			
		||||
        "maximum-edges": "Maximální počet hran (0 - neomezeno)",
 | 
			
		||||
        "maximum-edges-required": "Maximální počet hran je povinný.",
 | 
			
		||||
        "maximum-edges-range": "Maximální počet hran nemůže být záporný",
 | 
			
		||||
        "maximum-rule-chains": "Maximální počet řetězů pravidel (0 - neomezeno)",
 | 
			
		||||
        "maximum-rule-chains-required": "Maximální počet řetězů pravidel je povinný.",
 | 
			
		||||
        "maximum-rule-chains-range": "Maximální počet řetězů pravidel nemůže být záporný",
 | 
			
		||||
 | 
			
		||||
@ -3038,6 +3038,9 @@
 | 
			
		||||
    "maximum-dashboards": "Maks. antal dashboards (0 – ubegrænset)",
 | 
			
		||||
    "maximum-dashboards-required": "Maks. antal dashboards er påkrævet.",
 | 
			
		||||
    "maximum-dashboards-range": "Maks. antal dashboards kan ikke være negativt",
 | 
			
		||||
    "maximum-edges": "Maks. antal edges (0 – ubegrænset)",
 | 
			
		||||
    "maximum-edges-required": "Maks. antal edges er påkrævet.",
 | 
			
		||||
    "maximum-edges-range": "Maks. antal edges kan ikke være negativt",
 | 
			
		||||
    "maximum-rule-chains": "Maks. antal regelkæder (0 – ubegrænset)",
 | 
			
		||||
    "maximum-rule-chains-required": "Maks. antal regelkæder er påkrævet.",
 | 
			
		||||
    "maximum-rule-chains-range": "Maks. antal regelkæder kan ikke være negativt",
 | 
			
		||||
 | 
			
		||||
@ -4447,6 +4447,9 @@
 | 
			
		||||
        "maximum-dashboards": "Dashboards maximum number",
 | 
			
		||||
        "maximum-dashboards-required": "Dashboards maximum number is required.",
 | 
			
		||||
        "maximum-dashboards-range": "Dashboards maximum number can't be negative",
 | 
			
		||||
        "maximum-edges": "Edges maximum number",
 | 
			
		||||
        "maximum-edges-required": "Edges maximum number is required.",
 | 
			
		||||
        "maximum-edges-range": "Edges maximum number can't be negative",
 | 
			
		||||
        "maximum-rule-chains": "Rule chains maximum number",
 | 
			
		||||
        "maximum-rule-chains-required": "Rule chains maximum number is required.",
 | 
			
		||||
        "maximum-rule-chains-range": "Rule chains maximum number can't be negative",
 | 
			
		||||
 | 
			
		||||
@ -3739,6 +3739,9 @@
 | 
			
		||||
        "maximum-dashboards": "Nº Máximo de paneles (0 - sin límite)",
 | 
			
		||||
        "maximum-dashboards-required": "Nº Máximo de paneles requerido.",
 | 
			
		||||
        "maximum-dashboards-range": "Nº Máximo de paneles no puede ser negativo",
 | 
			
		||||
        "maximum-edges": "Nº Máximo de bordes (0 - sin límite)",
 | 
			
		||||
        "maximum-edges-required": "Nº Máximo de bordes requerido.",
 | 
			
		||||
        "maximum-edges-range": "Nº Máximo de bordes no puede ser negativo",
 | 
			
		||||
        "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",
 | 
			
		||||
 | 
			
		||||
@ -1967,6 +1967,9 @@
 | 
			
		||||
        "maximum-dashboards": "Maximum number of dashboards (0 - unlimited)",
 | 
			
		||||
        "maximum-dashboards-required": "Maximum number of dashboards is required.",
 | 
			
		||||
        "maximum-dashboards-range": "Maximum number of dashboards can't be negative",
 | 
			
		||||
        "maximum-edges": "Edges maximum number (0 - unlimited)",
 | 
			
		||||
        "maximum-edges-required": "Edges maximum number is required.",
 | 
			
		||||
        "maximum-edges-range": "Edges maximum number can't be negative",
 | 
			
		||||
        "maximum-rule-chains": "Maximum number of rule chains (0 - unlimited)",
 | 
			
		||||
        "maximum-rule-chains-required": "Maximum number of rule chains is required.",
 | 
			
		||||
        "maximum-rule-chains-range": "Maximum number of rule chains can't be negative",
 | 
			
		||||
 | 
			
		||||
@ -5042,6 +5042,9 @@
 | 
			
		||||
        "maximum-dashboards": "Dashboards maximum number",
 | 
			
		||||
        "maximum-dashboards-required": "Dashboards maximum number is required.",
 | 
			
		||||
        "maximum-dashboards-range": "Dashboards maximum number can't be negative",
 | 
			
		||||
        "maximum-edges": "Edges maximum number",
 | 
			
		||||
        "maximum-edges-required": "Edges maximum number is required.",
 | 
			
		||||
        "maximum-edges-range": "Edges maximum number can't be negative",
 | 
			
		||||
        "maximum-rule-chains": "Rule chains maximum number",
 | 
			
		||||
        "maximum-rule-chains-required": "Rule chains maximum number is required.",
 | 
			
		||||
        "maximum-rule-chains-range": "Rule chains maximum number can't be negative",
 | 
			
		||||
 | 
			
		||||
@ -4841,6 +4841,9 @@
 | 
			
		||||
        "maximum-dashboards": "Dashboards maximaal aantal",
 | 
			
		||||
        "maximum-dashboards-required": "Het maximale aantal dashboards is vereist.",
 | 
			
		||||
        "maximum-dashboards-range": "Het maximumaantal dashboards mag niet negatief zijn",
 | 
			
		||||
        "maximum-edges": "Randen maximaal aantal",
 | 
			
		||||
        "maximum-edges-required": "Het maximale aantal randen is vereist.",
 | 
			
		||||
        "maximum-edges-range": "Het maximumaantal randen mag niet negatief zijn",
 | 
			
		||||
        "maximum-rule-chains": "Maximaal aantal rule chains",
 | 
			
		||||
        "maximum-rule-chains-required": "Het maximale aantal rule chains is vereist.",
 | 
			
		||||
        "maximum-rule-chains-range": "Het maximale aantal rule chains mag niet negatief zijn",
 | 
			
		||||
 | 
			
		||||
@ -5059,6 +5059,9 @@
 | 
			
		||||
        "maximum-dashboards": "Maksymalna liczba paneli",
 | 
			
		||||
        "maximum-dashboards-required": "Maksymalna liczba paneli jest wymagana.",
 | 
			
		||||
        "maximum-dashboards-range": "Maksymalna liczba paneli nie może być ujemna",
 | 
			
		||||
        "maximum-edges": "Maksymalna liczba krawędzi",
 | 
			
		||||
        "maximum-edges-required": "Maksymalna liczba krawędzi jest wymagana.",
 | 
			
		||||
        "maximum-edges-range": "Maksymalna liczba krawędzi nie może być ujemna",
 | 
			
		||||
        "maximum-rule-chains": "Maksymalna liczba łańcuchów reguł",
 | 
			
		||||
        "maximum-rule-chains-required": "Maksymalna liczba łańcuchów reguł jest wymagana.",
 | 
			
		||||
        "maximum-rule-chains-range": "Maksymalna liczba łańcuchów reguł nie może być ujemna",
 | 
			
		||||
 | 
			
		||||
@ -1968,6 +1968,9 @@
 | 
			
		||||
    "maximum-dashboards": "Maximum number of dashboards (0 - unlimited)",
 | 
			
		||||
    "maximum-dashboards-required": "Maximum number of dashboards is required.",
 | 
			
		||||
    "maximum-dashboards-range": "Maximum number of dashboards can't be negative",
 | 
			
		||||
    "maximum-edges": "Edges maximum number (0 - unlimited)",
 | 
			
		||||
    "maximum-edges-required": "Edges maximum number is required.",
 | 
			
		||||
    "maximum-edges-range": "Edges maximum number can't be negative",
 | 
			
		||||
    "maximum-rule-chains": "Maximum number of rule chains (0 - unlimited)",
 | 
			
		||||
    "maximum-rule-chains-required": "Maximum number of rule chains is required.",
 | 
			
		||||
    "maximum-rule-chains-range": "Maximum number of rule chains can't be negative",
 | 
			
		||||
 | 
			
		||||
@ -2543,6 +2543,9 @@
 | 
			
		||||
    "maximum-dashboards": "Maksimum gösterge paneli sayısı (0 - sınırsız)",
 | 
			
		||||
    "maximum-dashboards-required": "Maksimum gösterge paneli sayısı gerekli.",
 | 
			
		||||
    "maximum-dashboards-range": "Maksimum gösterge paneli sayısı negatif olamaz",
 | 
			
		||||
    "maximum-edges": "Maksimum gösterge kenar sayısı (0 - sınırsız)",
 | 
			
		||||
    "maximum-edges-required": "Maksimum gösterge kenar sayısı gerekli.",
 | 
			
		||||
    "maximum-edges-range": "Maksimum gösterge kenar sayısı negatif olamaz",
 | 
			
		||||
    "maximum-rule-chains": "Maksimum kural zinciri sayısı (0 - sınırsız)",
 | 
			
		||||
    "maximum-rule-chains-required": "Maksimum kural zinciri sayısı gerekli.",
 | 
			
		||||
    "maximum-rule-chains-range": "Maksimum kural zinciri sayısı negatif olamaz",
 | 
			
		||||
 | 
			
		||||
@ -4234,6 +4234,9 @@
 | 
			
		||||
        "maximum-dashboards": "最大仪表板数",
 | 
			
		||||
        "maximum-dashboards-required": "最大仪表板数必填。",
 | 
			
		||||
        "maximum-dashboards-range": "最大仪表板数不能为负数",
 | 
			
		||||
        "maximum-edges": "最大边数",
 | 
			
		||||
        "maximum-edges-required": "需要最大边数。",
 | 
			
		||||
        "maximum-edges-range": "边的最大数量不能为负数",
 | 
			
		||||
        "maximum-rule-chains": "最大规则链数",
 | 
			
		||||
        "maximum-rule-chains-required": "最大规则链数必填。",
 | 
			
		||||
        "maximum-rule-chains-range": "最大规则链数不能为负数",
 | 
			
		||||
 | 
			
		||||
@ -2972,6 +2972,9 @@
 | 
			
		||||
        "maximum-dashboards": "儀表板最大數量",
 | 
			
		||||
        "maximum-dashboards-required": "儀表板最大數量必填",
 | 
			
		||||
        "maximum-dashboards-range": "儀表板最大數量不可為否",
 | 
			
		||||
        "maximum-edges": "最大邊數",
 | 
			
		||||
        "maximum-edges-required": "需要最大邊數。",
 | 
			
		||||
        "maximum-edges-range": "邊數最大不能為負數",
 | 
			
		||||
        "maximum-rule-chains": "規則鏈最大數量",
 | 
			
		||||
        "maximum-rule-chains-required": "規則鏈最大數量必填",
 | 
			
		||||
        "maximum-rule-chains-range": "規則鏈最大數量不可為否",
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user