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