Browse Source

Rate limits moved to tenant profile

pull/4650/head
AndrewVolosytnykhThingsboard 5 years ago
parent
commit
2b0af5bdb4
  1. 55
      application/src/main/java/org/thingsboard/server/config/RateLimitProcessingFilter.java
  2. 67
      application/src/main/java/org/thingsboard/server/controller/plugin/TbWebSocketHandler.java
  3. 78
      application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetryWebSocketService.java
  4. 7
      common/data/src/main/java/org/thingsboard/server/common/data/TenantProfile.java
  5. 17
      common/data/src/main/java/org/thingsboard/server/common/data/tenant/profile/DefaultTenantProfileConfiguration.java
  6. 34
      dao/src/main/java/org/thingsboard/server/dao/nosql/CassandraBufferedRateExecutor.java
  7. 26
      dao/src/main/java/org/thingsboard/server/dao/util/AbstractBufferedRateExecutor.java
  8. 30
      ui-ngx/src/app/modules/home/components/profile/tenant-profile-data.component.html
  9. 584
      ui-ngx/src/app/modules/home/components/profile/tenant/default-tenant-profile-configuration.component.html
  10. 31
      ui-ngx/src/app/modules/home/components/profile/tenant/default-tenant-profile-configuration.component.ts
  11. 38
      ui-ngx/src/app/shared/models/tenant.model.ts
  12. 18
      ui-ngx/src/assets/locale/locale.constant-en_US.json

55
application/src/main/java/org/thingsboard/server/config/RateLimitProcessingFilter.java

@ -16,16 +16,17 @@
package org.thingsboard.server.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.GenericFilterBean;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.msg.tools.TbRateLimits;
import org.thingsboard.server.common.msg.tools.TbRateLimitsException;
import org.thingsboard.server.dao.tenant.TbTenantProfileCache;
import org.thingsboard.server.exception.ThingsboardErrorResponseHandler;
import org.thingsboard.server.service.security.model.SecurityUser;
@ -41,18 +42,21 @@ import java.util.concurrent.ConcurrentMap;
@Component
public class RateLimitProcessingFilter extends GenericFilterBean {
@Value("${server.rest.limits.tenant.enabled:false}")
private boolean perTenantLimitsEnabled;
@Value("${server.rest.limits.tenant.configuration:}")
private String perTenantLimitsConfiguration;
@Value("${server.rest.limits.customer.enabled:false}")
private boolean perCustomerLimitsEnabled;
@Value("${server.rest.limits.customer.configuration:}")
private String perCustomerLimitsConfiguration;
// @Value("${server.rest.limits.tenant.enabled:false}")
// private boolean perTenantLimitsEnabled;
// @Value("${server.rest.limits.tenant.configuration:}")
// private String perTenantLimitsConfiguration;
// @Value("${server.rest.limits.customer.enabled:false}")
// private boolean perCustomerLimitsEnabled;
// @Value("${server.rest.limits.customer.configuration:}")
// private String perCustomerLimitsConfiguration;
@Autowired
private ThingsboardErrorResponseHandler errorResponseHandler;
@Autowired
private TbTenantProfileCache tenantProfileCache;
private ConcurrentMap<TenantId, TbRateLimits> perTenantLimits = new ConcurrentHashMap<>();
private ConcurrentMap<CustomerId, TbRateLimits> perCustomerLimits = new ConcurrentHashMap<>();
@ -60,18 +64,29 @@ public class RateLimitProcessingFilter extends GenericFilterBean {
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
SecurityUser user = getCurrentUser();
if (user != null && !user.isSystemAdmin()) {
if (perTenantLimitsEnabled) {
TbRateLimits rateLimits = perTenantLimits.computeIfAbsent(user.getTenantId(), id -> new TbRateLimits(perTenantLimitsConfiguration));
if (!rateLimits.tryConsume()) {
errorResponseHandler.handle(new TbRateLimitsException(EntityType.TENANT), (HttpServletResponse) response);
return;
var profile= tenantProfileCache.get(user.getTenantId()).getDefaultTenantConfiguration();
if(profile != null) {
if (StringUtils.isNotEmpty(profile.getRateLimitsTenantConfiguration())) {
TbRateLimits rateLimits = perTenantLimits.computeIfAbsent(
user.getTenantId(), id -> new TbRateLimits(profile.getRateLimitsTenantConfiguration())
);
if (!rateLimits.tryConsume()) {
errorResponseHandler.handle(new TbRateLimitsException(EntityType.TENANT), (HttpServletResponse) response);
return;
}
}
}
if (perCustomerLimitsEnabled && user.isCustomerUser()) {
TbRateLimits rateLimits = perCustomerLimits.computeIfAbsent(user.getCustomerId(), id -> new TbRateLimits(perCustomerLimitsConfiguration));
if (!rateLimits.tryConsume()) {
errorResponseHandler.handle(new TbRateLimitsException(EntityType.CUSTOMER), (HttpServletResponse) response);
return;
if (StringUtils.isNotEmpty(profile.getRateLimitsCustomerConfiguration())) {
TbRateLimits rateLimits = perCustomerLimits.computeIfAbsent(
user.getCustomerId(), id -> new TbRateLimits(profile.getRateLimitsCustomerConfiguration())
);
if (!rateLimits.tryConsume()) {
errorResponseHandler.handle(new TbRateLimitsException(EntityType.CUSTOMER), (HttpServletResponse) response);
return;
}
}
}
}

67
application/src/main/java/org/thingsboard/server/controller/plugin/TbWebSocketHandler.java

@ -33,6 +33,7 @@ import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.UserId;
import org.thingsboard.server.common.msg.tools.TbRateLimits;
import org.thingsboard.server.config.WebSocketConfiguration;
import org.thingsboard.server.dao.tenant.TbTenantProfileCache;
import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.security.model.SecurityUser;
import org.thingsboard.server.service.security.model.UserPrincipal;
@ -68,22 +69,11 @@ public class TbWebSocketHandler extends TextWebSocketHandler implements Telemetr
@Autowired
private TelemetryWebSocketService webSocketService;
@Autowired
private TbTenantProfileCache tenantProfileCache;
@Value("${server.ws.send_timeout:5000}")
private long sendTimeout;
@Value("${server.ws.limits.max_sessions_per_tenant:0}")
private int maxSessionsPerTenant;
@Value("${server.ws.limits.max_sessions_per_customer:0}")
private int maxSessionsPerCustomer;
@Value("${server.ws.limits.max_sessions_per_regular_user:0}")
private int maxSessionsPerRegularUser;
@Value("${server.ws.limits.max_sessions_per_public_user:0}")
private int maxSessionsPerPublicUser;
@Value("${server.ws.limits.max_queue_per_ws_session:1000}")
private int maxMsgQueuePerSession;
@Value("${server.ws.limits.max_updates_per_session:}")
private String perSessionUpdatesConfiguration;
@Value("${server.ws.ping_timeout:30000}")
private long pingTimeout;
@ -124,10 +114,15 @@ public class TbWebSocketHandler extends TextWebSocketHandler implements Telemetr
String internalSessionId = session.getId();
TelemetryWebSocketSessionRef sessionRef = toRef(session);
String externalSessionId = sessionRef.getSessionId();
if (!checkLimits(session, sessionRef)) {
return;
}
internalSessionMap.put(internalSessionId, new SessionMetaData(session, sessionRef, maxMsgQueuePerSession));
var tenantProfileConfiguration = tenantProfileCache.get(sessionRef.getSecurityCtx().getTenantId()).getDefaultTenantConfiguration();
if(tenantProfileConfiguration.getWsLimitQueuePerWsSession() <= 0) {
tenantProfileConfiguration.setWsLimitQueuePerWsSession(500);
}
internalSessionMap.put(internalSessionId, new SessionMetaData(session, sessionRef, tenantProfileConfiguration.getWsLimitQueuePerWsSession()));
externalSessionMap.put(externalSessionId, internalSessionId);
processInWebSocketService(sessionRef, SessionEvent.onEstablished());
@ -287,11 +282,14 @@ public class TbWebSocketHandler extends TextWebSocketHandler implements Telemetr
String externalId = sessionRef.getSessionId();
log.debug("[{}] Processing {}", externalId, msg);
String internalId = externalSessionMap.get(externalId);
var tenantProfileConfiguration = tenantProfileCache.get(sessionRef.getSecurityCtx().getTenantId()).getDefaultTenantConfiguration();
if (internalId != null) {
SessionMetaData sessionMd = internalSessionMap.get(internalId);
if (sessionMd != null) {
if (!StringUtils.isEmpty(perSessionUpdatesConfiguration)) {
TbRateLimits rateLimits = perSessionUpdateLimits.computeIfAbsent(sessionRef.getSessionId(), sid -> new TbRateLimits(perSessionUpdatesConfiguration));
if (!StringUtils.isEmpty(tenantProfileConfiguration.getWsLimitUpdatesPerSession())) {
TbRateLimits rateLimits = perSessionUpdateLimits.computeIfAbsent(sessionRef.getSessionId(), sid -> new TbRateLimits(tenantProfileConfiguration.getWsLimitUpdatesPerSession()));
if (!rateLimits.tryConsume()) {
if (blacklistedSessions.putIfAbsent(externalId, sessionRef) == null) {
log.info("[{}][{}][{}] Failed to process session update. Max session updates limit reached"
@ -347,11 +345,18 @@ public class TbWebSocketHandler extends TextWebSocketHandler implements Telemetr
}
private boolean checkLimits(WebSocketSession session, TelemetryWebSocketSessionRef sessionRef) throws Exception {
var tenantProfileConfiguration =
tenantProfileCache.get(sessionRef.getSecurityCtx().getTenantId()).getDefaultTenantConfiguration();
if(tenantProfileConfiguration == null) {
return true;
}
String sessionId = session.getId();
if (maxSessionsPerTenant > 0) {
if (tenantProfileConfiguration.getWsLimitMaxSessionsPerTenant() > 0) {
Set<String> tenantSessions = tenantSessionsMap.computeIfAbsent(sessionRef.getSecurityCtx().getTenantId(), id -> ConcurrentHashMap.newKeySet());
synchronized (tenantSessions) {
if (tenantSessions.size() < maxSessionsPerTenant) {
if (tenantSessions.size() < tenantProfileConfiguration.getWsLimitMaxSessionsPerTenant()) {
tenantSessions.add(sessionId);
} else {
log.info("[{}][{}][{}] Failed to start session. Max tenant sessions limit reached"
@ -363,10 +368,10 @@ public class TbWebSocketHandler extends TextWebSocketHandler implements Telemetr
}
if (sessionRef.getSecurityCtx().isCustomerUser()) {
if (maxSessionsPerCustomer > 0) {
if (tenantProfileConfiguration.getWsLimitMaxSessionsPerCustomer() > 0) {
Set<String> customerSessions = customerSessionsMap.computeIfAbsent(sessionRef.getSecurityCtx().getCustomerId(), id -> ConcurrentHashMap.newKeySet());
synchronized (customerSessions) {
if (customerSessions.size() < maxSessionsPerCustomer) {
if (customerSessions.size() < tenantProfileConfiguration.getWsLimitMaxSessionsPerCustomer()) {
customerSessions.add(sessionId);
} else {
log.info("[{}][{}][{}] Failed to start session. Max customer sessions limit reached"
@ -376,10 +381,11 @@ public class TbWebSocketHandler extends TextWebSocketHandler implements Telemetr
}
}
}
if (maxSessionsPerRegularUser > 0 && UserPrincipal.Type.USER_NAME.equals(sessionRef.getSecurityCtx().getUserPrincipal().getType())) {
if (tenantProfileConfiguration.getWsLimitMaxSessionsPerRegularUser() > 0
&& UserPrincipal.Type.USER_NAME.equals(sessionRef.getSecurityCtx().getUserPrincipal().getType())) {
Set<String> regularUserSessions = regularUserSessionsMap.computeIfAbsent(sessionRef.getSecurityCtx().getId(), id -> ConcurrentHashMap.newKeySet());
synchronized (regularUserSessions) {
if (regularUserSessions.size() < maxSessionsPerRegularUser) {
if (regularUserSessions.size() < tenantProfileConfiguration.getWsLimitMaxSessionsPerRegularUser()) {
regularUserSessions.add(sessionId);
} else {
log.info("[{}][{}][{}] Failed to start session. Max regular user sessions limit reached"
@ -389,10 +395,11 @@ public class TbWebSocketHandler extends TextWebSocketHandler implements Telemetr
}
}
}
if (maxSessionsPerPublicUser > 0 && UserPrincipal.Type.PUBLIC_ID.equals(sessionRef.getSecurityCtx().getUserPrincipal().getType())) {
if (tenantProfileConfiguration.getWsLimitMaxSessionsPerPublicUser() > 0
&& UserPrincipal.Type.PUBLIC_ID.equals(sessionRef.getSecurityCtx().getUserPrincipal().getType())) {
Set<String> publicUserSessions = publicUserSessionsMap.computeIfAbsent(sessionRef.getSecurityCtx().getId(), id -> ConcurrentHashMap.newKeySet());
synchronized (publicUserSessions) {
if (publicUserSessions.size() < maxSessionsPerPublicUser) {
if (publicUserSessions.size() < tenantProfileConfiguration.getWsLimitMaxSessionsPerPublicUser()) {
publicUserSessions.add(sessionId);
} else {
log.info("[{}][{}][{}] Failed to start session. Max public user sessions limit reached"
@ -407,29 +414,31 @@ public class TbWebSocketHandler extends TextWebSocketHandler implements Telemetr
}
private void cleanupLimits(WebSocketSession session, TelemetryWebSocketSessionRef sessionRef) {
var tenantProfileConfiguration = tenantProfileCache.get(sessionRef.getSecurityCtx().getTenantId()).getDefaultTenantConfiguration();
String sessionId = session.getId();
perSessionUpdateLimits.remove(sessionRef.getSessionId());
blacklistedSessions.remove(sessionRef.getSessionId());
if (maxSessionsPerTenant > 0) {
if (tenantProfileConfiguration.getWsLimitMaxSessionsPerTenant() > 0) {
Set<String> tenantSessions = tenantSessionsMap.computeIfAbsent(sessionRef.getSecurityCtx().getTenantId(), id -> ConcurrentHashMap.newKeySet());
synchronized (tenantSessions) {
tenantSessions.remove(sessionId);
}
}
if (sessionRef.getSecurityCtx().isCustomerUser()) {
if (maxSessionsPerCustomer > 0) {
if (tenantProfileConfiguration.getWsLimitMaxSessionsPerCustomer() > 0) {
Set<String> customerSessions = customerSessionsMap.computeIfAbsent(sessionRef.getSecurityCtx().getCustomerId(), id -> ConcurrentHashMap.newKeySet());
synchronized (customerSessions) {
customerSessions.remove(sessionId);
}
}
if (maxSessionsPerRegularUser > 0 && UserPrincipal.Type.USER_NAME.equals(sessionRef.getSecurityCtx().getUserPrincipal().getType())) {
if (tenantProfileConfiguration.getWsLimitMaxSessionsPerRegularUser() > 0 && UserPrincipal.Type.USER_NAME.equals(sessionRef.getSecurityCtx().getUserPrincipal().getType())) {
Set<String> regularUserSessions = regularUserSessionsMap.computeIfAbsent(sessionRef.getSecurityCtx().getId(), id -> ConcurrentHashMap.newKeySet());
synchronized (regularUserSessions) {
regularUserSessions.remove(sessionId);
}
}
if (maxSessionsPerPublicUser > 0 && UserPrincipal.Type.PUBLIC_ID.equals(sessionRef.getSecurityCtx().getUserPrincipal().getType())) {
if (tenantProfileConfiguration.getWsLimitMaxSessionsPerPublicUser() > 0 && UserPrincipal.Type.PUBLIC_ID.equals(sessionRef.getSecurityCtx().getUserPrincipal().getType())) {
Set<String> publicUserSessions = publicUserSessionsMap.computeIfAbsent(sessionRef.getSecurityCtx().getId(), id -> ConcurrentHashMap.newKeySet());
synchronized (publicUserSessions) {
publicUserSessions.remove(sessionId);

78
application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetryWebSocketService.java

@ -24,7 +24,6 @@ import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.MoreExecutors;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.socket.CloseStatus;
@ -42,7 +41,9 @@ import org.thingsboard.server.common.data.kv.BaseReadTsKvQuery;
import org.thingsboard.server.common.data.kv.BasicTsKvEntry;
import org.thingsboard.server.common.data.kv.ReadTsKvQuery;
import org.thingsboard.server.common.data.kv.TsKvEntry;
import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration;
import org.thingsboard.server.dao.attributes.AttributesService;
import org.thingsboard.server.dao.tenant.TbTenantProfileCache;
import org.thingsboard.server.dao.timeseries.TimeseriesService;
import org.thingsboard.server.dao.util.TenantRateLimitException;
import org.thingsboard.server.queue.discovery.TbServiceInfoProvider;
@ -136,15 +137,8 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi
@Autowired
private TbServiceInfoProvider serviceInfoProvider;
@Value("${server.ws.limits.max_subscriptions_per_tenant:0}")
private int maxSubscriptionsPerTenant;
@Value("${server.ws.limits.max_subscriptions_per_customer:0}")
private int maxSubscriptionsPerCustomer;
@Value("${server.ws.limits.max_subscriptions_per_regular_user:0}")
private int maxSubscriptionsPerRegularUser;
@Value("${server.ws.limits.max_subscriptions_per_public_user:0}")
private int maxSubscriptionsPerPublicUser;
@Autowired
private TbTenantProfileCache tenantProfileCache;
private ConcurrentMap<TenantId, Set<String>> tenantSubscriptionsMap = new ConcurrentHashMap<>();
private ConcurrentMap<CustomerId, Set<String>> customerSubscriptionsMap = new ConcurrentHashMap<>();
@ -304,44 +298,50 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi
}
private void processSessionClose(TelemetryWebSocketSessionRef sessionRef) {
String sessionId = "[" + sessionRef.getSessionId() + "]";
if (maxSubscriptionsPerTenant > 0) {
Set<String> tenantSubscriptions = tenantSubscriptionsMap.computeIfAbsent(sessionRef.getSecurityCtx().getTenantId(), id -> ConcurrentHashMap.newKeySet());
synchronized (tenantSubscriptions) {
tenantSubscriptions.removeIf(subId -> subId.startsWith(sessionId));
}
}
if (sessionRef.getSecurityCtx().isCustomerUser()) {
if (maxSubscriptionsPerCustomer > 0) {
Set<String> customerSessions = customerSubscriptionsMap.computeIfAbsent(sessionRef.getSecurityCtx().getCustomerId(), id -> ConcurrentHashMap.newKeySet());
synchronized (customerSessions) {
customerSessions.removeIf(subId -> subId.startsWith(sessionId));
var tenantProfileConfiguration = (DefaultTenantProfileConfiguration) tenantProfileCache.get(sessionRef.getSecurityCtx().getTenantId()).getDefaultTenantConfiguration();
if(tenantProfileConfiguration != null) {
String sessionId = "[" + sessionRef.getSessionId() + "]";
if (tenantProfileConfiguration.getWsLimitMaxSubscriptionsPerTenant() > 0) {
Set<String> tenantSubscriptions = tenantSubscriptionsMap.computeIfAbsent(sessionRef.getSecurityCtx().getTenantId(), id -> ConcurrentHashMap.newKeySet());
synchronized (tenantSubscriptions) {
tenantSubscriptions.removeIf(subId -> subId.startsWith(sessionId));
}
}
if (maxSubscriptionsPerRegularUser > 0 && UserPrincipal.Type.USER_NAME.equals(sessionRef.getSecurityCtx().getUserPrincipal().getType())) {
Set<String> regularUserSessions = regularUserSubscriptionsMap.computeIfAbsent(sessionRef.getSecurityCtx().getId(), id -> ConcurrentHashMap.newKeySet());
synchronized (regularUserSessions) {
regularUserSessions.removeIf(subId -> subId.startsWith(sessionId));
if (sessionRef.getSecurityCtx().isCustomerUser()) {
if (tenantProfileConfiguration.getWsLimitMaxSubscriptionsPerCustomer() > 0) {
Set<String> customerSessions = customerSubscriptionsMap.computeIfAbsent(sessionRef.getSecurityCtx().getCustomerId(), id -> ConcurrentHashMap.newKeySet());
synchronized (customerSessions) {
customerSessions.removeIf(subId -> subId.startsWith(sessionId));
}
}
}
if (maxSubscriptionsPerPublicUser > 0 && UserPrincipal.Type.PUBLIC_ID.equals(sessionRef.getSecurityCtx().getUserPrincipal().getType())) {
Set<String> publicUserSessions = publicUserSubscriptionsMap.computeIfAbsent(sessionRef.getSecurityCtx().getId(), id -> ConcurrentHashMap.newKeySet());
synchronized (publicUserSessions) {
publicUserSessions.removeIf(subId -> subId.startsWith(sessionId));
if (tenantProfileConfiguration.getWsLimitMaxSubscriptionsPerRegularUser() > 0 && UserPrincipal.Type.USER_NAME.equals(sessionRef.getSecurityCtx().getUserPrincipal().getType())) {
Set<String> regularUserSessions = regularUserSubscriptionsMap.computeIfAbsent(sessionRef.getSecurityCtx().getId(), id -> ConcurrentHashMap.newKeySet());
synchronized (regularUserSessions) {
regularUserSessions.removeIf(subId -> subId.startsWith(sessionId));
}
}
if (tenantProfileConfiguration.getWsLimitMaxSubscriptionsPerPublicUser() > 0 && UserPrincipal.Type.PUBLIC_ID.equals(sessionRef.getSecurityCtx().getUserPrincipal().getType())) {
Set<String> publicUserSessions = publicUserSubscriptionsMap.computeIfAbsent(sessionRef.getSecurityCtx().getId(), id -> ConcurrentHashMap.newKeySet());
synchronized (publicUserSessions) {
publicUserSessions.removeIf(subId -> subId.startsWith(sessionId));
}
}
}
}
}
private boolean processSubscription(TelemetryWebSocketSessionRef sessionRef, SubscriptionCmd cmd) {
var tenantProfileConfiguration = (DefaultTenantProfileConfiguration) tenantProfileCache.get(sessionRef.getSecurityCtx().getTenantId()).getDefaultTenantConfiguration();
String subId = "[" + sessionRef.getSessionId() + "]:[" + cmd.getCmdId() + "]";
try {
if (maxSubscriptionsPerTenant > 0) {
if (tenantProfileConfiguration.getWsLimitMaxSubscriptionsPerTenant() > 0) {
Set<String> tenantSubscriptions = tenantSubscriptionsMap.computeIfAbsent(sessionRef.getSecurityCtx().getTenantId(), id -> ConcurrentHashMap.newKeySet());
synchronized (tenantSubscriptions) {
if (cmd.isUnsubscribe()) {
tenantSubscriptions.remove(subId);
} else if (tenantSubscriptions.size() < maxSubscriptionsPerTenant) {
} else if (tenantSubscriptions.size() < tenantProfileConfiguration.getWsLimitMaxSubscriptionsPerTenant()) {
tenantSubscriptions.add(subId);
} else {
log.info("[{}][{}][{}] Failed to start subscription. Max tenant subscriptions limit reached"
@ -353,12 +353,12 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi
}
if (sessionRef.getSecurityCtx().isCustomerUser()) {
if (maxSubscriptionsPerCustomer > 0) {
if (tenantProfileConfiguration.getWsLimitMaxSubscriptionsPerCustomer() > 0) {
Set<String> customerSessions = customerSubscriptionsMap.computeIfAbsent(sessionRef.getSecurityCtx().getCustomerId(), id -> ConcurrentHashMap.newKeySet());
synchronized (customerSessions) {
if (cmd.isUnsubscribe()) {
customerSessions.remove(subId);
} else if (customerSessions.size() < maxSubscriptionsPerCustomer) {
} else if (customerSessions.size() < tenantProfileConfiguration.getWsLimitMaxSubscriptionsPerCustomer()) {
customerSessions.add(subId);
} else {
log.info("[{}][{}][{}] Failed to start subscription. Max customer subscriptions limit reached"
@ -368,10 +368,10 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi
}
}
}
if (maxSubscriptionsPerRegularUser > 0 && UserPrincipal.Type.USER_NAME.equals(sessionRef.getSecurityCtx().getUserPrincipal().getType())) {
if (tenantProfileConfiguration.getWsLimitMaxSubscriptionsPerRegularUser() > 0 && UserPrincipal.Type.USER_NAME.equals(sessionRef.getSecurityCtx().getUserPrincipal().getType())) {
Set<String> regularUserSessions = regularUserSubscriptionsMap.computeIfAbsent(sessionRef.getSecurityCtx().getId(), id -> ConcurrentHashMap.newKeySet());
synchronized (regularUserSessions) {
if (regularUserSessions.size() < maxSubscriptionsPerRegularUser) {
if (regularUserSessions.size() < tenantProfileConfiguration.getWsLimitMaxSubscriptionsPerRegularUser()) {
regularUserSessions.add(subId);
} else {
log.info("[{}][{}][{}] Failed to start subscription. Max regular user subscriptions limit reached"
@ -381,10 +381,10 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi
}
}
}
if (maxSubscriptionsPerPublicUser > 0 && UserPrincipal.Type.PUBLIC_ID.equals(sessionRef.getSecurityCtx().getUserPrincipal().getType())) {
if (tenantProfileConfiguration.getWsLimitMaxSubscriptionsPerPublicUser() > 0 && UserPrincipal.Type.PUBLIC_ID.equals(sessionRef.getSecurityCtx().getUserPrincipal().getType())) {
Set<String> publicUserSessions = publicUserSubscriptionsMap.computeIfAbsent(sessionRef.getSecurityCtx().getId(), id -> ConcurrentHashMap.newKeySet());
synchronized (publicUserSessions) {
if (publicUserSessions.size() < maxSubscriptionsPerPublicUser) {
if (publicUserSessions.size() < tenantProfileConfiguration.getWsLimitMaxSubscriptionsPerPublicUser()) {
publicUserSessions.add(subId);
} else {
log.info("[{}][{}][{}] Failed to start subscription. Max public user subscriptions limit reached"

7
common/data/src/main/java/org/thingsboard/server/common/data/TenantProfile.java

@ -107,4 +107,11 @@ public class TenantProfile extends SearchTextBased<TenantProfileId> implements H
}
}
public DefaultTenantProfileConfiguration getDefaultTenantConfiguration() {
if(this.profileData != null && this.profileData.getConfiguration().getType().equals(TenantProfileType.DEFAULT)) {
return (DefaultTenantProfileConfiguration) this.profileData.getConfiguration();
}
return null;
}
}

17
common/data/src/main/java/org/thingsboard/server/common/data/tenant/profile/DefaultTenantProfileConfiguration.java

@ -42,6 +42,23 @@ public class DefaultTenantProfileConfiguration implements TenantProfileConfigura
private String transportDeviceTelemetryMsgRateLimit;
private String transportDeviceTelemetryDataPointsRateLimit;
private String rateLimitsTenantConfiguration;
private String rateLimitsCustomerConfiguration;
private int wsLimitMaxSessionsPerTenant;
private int wsLimitMaxSessionsPerCustomer;
private int wsLimitMaxSessionsPerRegularUser;
private int wsLimitMaxSessionsPerPublicUser;
private int wsLimitQueuePerWsSession;
private long wsLimitMaxSubscriptionsPerTenant;
private long wsLimitMaxSubscriptionsPerCustomer;
private long wsLimitMaxSubscriptionsPerRegularUser;
private long wsLimitMaxSubscriptionsPerPublicUser;
private String wsLimitUpdatesPerSession;
private String cassandraTenantLimitsConfiguration;
private boolean printTenantNames;
private long maxTransportMessages;
private long maxTransportDataPoints;
private long maxREExecutions;

34
dao/src/main/java/org/thingsboard/server/dao/nosql/CassandraBufferedRateExecutor.java

@ -22,14 +22,18 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.msg.tools.TbRateLimits;
import org.thingsboard.server.common.stats.DefaultCounter;
import org.thingsboard.server.common.stats.StatsCounter;
import org.thingsboard.server.common.stats.StatsFactory;
import org.thingsboard.server.dao.entity.EntityService;
import org.thingsboard.server.dao.tenant.TbTenantProfileCache;
import org.thingsboard.server.dao.util.AbstractBufferedRateExecutor;
import org.thingsboard.server.dao.util.AsyncTaskContext;
import org.thingsboard.server.dao.util.NoSqlAnyDao;
import org.thingsboard.server.dao.util.TenantRateLimitException;
import javax.annotation.PreDestroy;
import java.util.HashMap;
@ -45,6 +49,9 @@ public class CassandraBufferedRateExecutor extends AbstractBufferedRateExecutor<
@Autowired
private EntityService entityService;
@Autowired
private TbTenantProfileCache tenantProfileCache;
private Map<TenantId, String> tenantNamesCache = new HashMap<>();
private boolean printTenantNames;
@ -56,12 +63,10 @@ public class CassandraBufferedRateExecutor extends AbstractBufferedRateExecutor<
@Value("${cassandra.query.dispatcher_threads:2}") int dispatcherThreads,
@Value("${cassandra.query.callback_threads:4}") int callbackThreads,
@Value("${cassandra.query.poll_ms:50}") long pollMs,
@Value("${cassandra.query.tenant_rate_limits.enabled}") boolean tenantRateLimitsEnabled,
@Value("${cassandra.query.tenant_rate_limits.configuration}") String tenantRateLimitsConfiguration,
@Value("${cassandra.query.tenant_rate_limits.print_tenant_names}") boolean printTenantNames,
@Value("${cassandra.query.print_queries_freq:0}") int printQueriesFreq,
@Autowired StatsFactory statsFactory) {
super(queueLimit, concurrencyLimit, maxWaitTime, dispatcherThreads, callbackThreads, pollMs, tenantRateLimitsEnabled, tenantRateLimitsConfiguration, printQueriesFreq, statsFactory);
super(queueLimit, concurrencyLimit, maxWaitTime, dispatcherThreads, callbackThreads, pollMs, printQueriesFreq, statsFactory);
this.printTenantNames = printTenantNames;
}
@ -97,7 +102,8 @@ public class CassandraBufferedRateExecutor extends AbstractBufferedRateExecutor<
DefaultCounter counter = entry.getValue();
int rateLimitedRequests = counter.get();
counter.clear();
if (printTenantNames) {
var profile = tenantProfileCache.get(tenantId).getDefaultTenantConfiguration();
if (profile != null && profile.isPrintTenantNames()) {
String name = tenantNamesCache.computeIfAbsent(tenantId, tId -> {
try {
return entityService.fetchEntityNameAsync(TenantId.SYS_TENANT_ID, tenantId).get();
@ -137,4 +143,24 @@ public class CassandraBufferedRateExecutor extends AbstractBufferedRateExecutor<
);
}
@Override
protected boolean checkRateLimits(CassandraStatementTask task, SettableFuture<TbResultSet> future) {
var tenantProfileConfiguration = tenantProfileCache.get(task.getTenantId()).getDefaultTenantConfiguration();
if (StringUtils.isNotEmpty(tenantProfileConfiguration.getCassandraTenantLimitsConfiguration())) {
if (task.getTenantId() == null) {
log.info("Invalid task received: {}", task);
} else if (!task.getTenantId().isNullUid()) {
TbRateLimits rateLimits = perTenantLimits.computeIfAbsent(
task.getTenantId(), id -> new TbRateLimits(tenantProfileConfiguration.getCassandraTenantLimitsConfiguration())
);
if (!rateLimits.tryConsume()) {
stats.incrementRateLimitedTenant(task.getTenantId());
stats.getTotalRateLimited().increment();
future.setException(new TenantRateLimitException());
return true;
}
}
}
return false;
}
}

26
dao/src/main/java/org/thingsboard/server/dao/util/AbstractBufferedRateExecutor.java

@ -66,9 +66,7 @@ public abstract class AbstractBufferedRateExecutor<T extends AsyncTask, F extend
private final ScheduledExecutorService timeoutExecutor;
private final int concurrencyLimit;
private final int printQueriesFreq;
private final boolean perTenantLimitsEnabled;
private final String perTenantLimitsConfiguration;
private final ConcurrentMap<TenantId, TbRateLimits> perTenantLimits = new ConcurrentHashMap<>();
protected final ConcurrentMap<TenantId, TbRateLimits> perTenantLimits = new ConcurrentHashMap<>();
private final AtomicInteger printQueriesIdx = new AtomicInteger(0);
@ -76,7 +74,7 @@ public abstract class AbstractBufferedRateExecutor<T extends AsyncTask, F extend
protected final BufferedRateExecutorStats stats;
public AbstractBufferedRateExecutor(int queueLimit, int concurrencyLimit, long maxWaitTime, int dispatcherThreads, int callbackThreads, long pollMs,
boolean perTenantLimitsEnabled, String perTenantLimitsConfiguration, int printQueriesFreq, StatsFactory statsFactory) {
int printQueriesFreq, StatsFactory statsFactory) {
this.maxWaitTime = maxWaitTime;
this.pollMs = pollMs;
this.concurrencyLimit = concurrencyLimit;
@ -85,8 +83,6 @@ public abstract class AbstractBufferedRateExecutor<T extends AsyncTask, F extend
this.dispatcherExecutor = Executors.newFixedThreadPool(dispatcherThreads, ThingsBoardThreadFactory.forName("nosql-dispatcher"));
this.callbackExecutor = ThingsBoardExecutors.newWorkStealingPool(callbackThreads, getClass());
this.timeoutExecutor = Executors.newSingleThreadScheduledExecutor(ThingsBoardThreadFactory.forName("nosql-timeout"));
this.perTenantLimitsEnabled = perTenantLimitsEnabled;
this.perTenantLimitsConfiguration = perTenantLimitsConfiguration;
this.stats = new BufferedRateExecutorStats(statsFactory);
String concurrencyLevelKey = StatsType.RATE_EXECUTOR.getName() + "." + CONCURRENCY_LEVEL;
this.concurrencyLevel = statsFactory.createGauge(concurrencyLevelKey, new AtomicInteger(0));
@ -96,24 +92,14 @@ public abstract class AbstractBufferedRateExecutor<T extends AsyncTask, F extend
}
}
protected abstract boolean checkRateLimits(T task, SettableFuture<V> future);
@Override
public F submit(T task) {
SettableFuture<V> settableFuture = create();
F result = wrap(task, settableFuture);
boolean perTenantLimitReached = false;
if (perTenantLimitsEnabled) {
if (task.getTenantId() == null) {
log.info("Invalid task received: {}", task);
} else if (!task.getTenantId().isNullUid()) {
TbRateLimits rateLimits = perTenantLimits.computeIfAbsent(task.getTenantId(), id -> new TbRateLimits(perTenantLimitsConfiguration));
if (!rateLimits.tryConsume()) {
stats.incrementRateLimitedTenant(task.getTenantId());
stats.getTotalRateLimited().increment();
settableFuture.setException(new TenantRateLimitException());
perTenantLimitReached = true;
}
}
}
boolean perTenantLimitReached = checkRateLimits(task, settableFuture);
if (!perTenantLimitReached) {
try {
stats.getTotalAdded().increment();

30
ui-ngx/src/app/modules/home/components/profile/tenant-profile-data.component.html

@ -16,17 +16,21 @@
-->
<form [formGroup]="tenantProfileDataFormGroup" style="padding-bottom: 16px;">
<mat-expansion-panel [expanded]="true">
<mat-expansion-panel-header>
<mat-panel-title>
<div translate>tenant-profile.profile-configuration</div>
</mat-panel-title>
</mat-expansion-panel-header>
<ng-template matExpansionPanelContent>
<tb-tenant-profile-configuration
formControlName="configuration"
required>
</tb-tenant-profile-configuration>
</ng-template>
</mat-expansion-panel>
<!-- <mat-expansion-panel [expanded]="true">-->
<!-- <mat-expansion-panel-header>-->
<!-- <mat-panel-title>-->
<!-- <div translate>tenant-profile.profile-configuration</div>-->
<!-- </mat-panel-title>-->
<!-- </mat-expansion-panel-header>-->
<!-- <ng-template matExpansionPanelContent>-->
<!-- <tb-tenant-profile-configuration-->
<!-- formControlName="configuration"-->
<!-- required>-->
<!-- </tb-tenant-profile-configuration>-->
<!-- </ng-template>-->
<!-- </mat-expansion-panel>-->
<tb-tenant-profile-configuration
formControlName="configuration"
required>
</tb-tenant-profile-configuration>
</form>

584
ui-ngx/src/app/modules/home/components/profile/tenant/default-tenant-profile-configuration.component.html

@ -15,221 +15,371 @@
limitations under the License.
-->
<section [formGroup]="defaultTenantProfileConfigurationFormGroup" fxLayout="column">
<mat-form-field class="mat-block">
<mat-label translate>tenant-profile.maximum-devices</mat-label>
<input matInput required min="0" step="1"
formControlName="maxDevices"
type="number">
<mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('maxDevices').hasError('required')">
{{ 'tenant-profile.maximum-devices-required' | translate}}
</mat-error>
<mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('maxDevices').hasError('min')">
{{ 'tenant-profile.maximum-devices-range' | translate}}
</mat-error>
</mat-form-field>
<mat-form-field class="mat-block">
<mat-label translate>tenant-profile.maximum-assets</mat-label>
<input matInput required min="0" step="1"
formControlName="maxAssets"
type="number">
<mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('maxAssets').hasError('required')">
{{ 'tenant-profile.maximum-assets-required' | translate}}
</mat-error>
<mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('maxAssets').hasError('min')">
{{ 'tenant-profile.maximum-assets-range' | translate}}
</mat-error>
</mat-form-field>
<mat-form-field class="mat-block">
<mat-label translate>tenant-profile.maximum-customers</mat-label>
<input matInput required min="0" step="1"
formControlName="maxCustomers"
type="number">
<mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('maxCustomers').hasError('required')">
{{ 'tenant-profile.maximum-customers-required' | translate}}
</mat-error>
<mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('maxCustomers').hasError('min')">
{{ 'tenant-profile.maximum-customers-range' | translate}}
</mat-error>
</mat-form-field>
<mat-form-field class="mat-block">
<mat-label translate>tenant-profile.maximum-users</mat-label>
<input matInput required min="0" step="1"
formControlName="maxUsers"
type="number">
<mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('maxUsers').hasError('required')">
{{ 'tenant-profile.maximum-users-required' | translate}}
</mat-error>
<mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('maxUsers').hasError('min')">
{{ 'tenant-profile.maximum-users-range' | translate}}
</mat-error>
</mat-form-field>
<mat-form-field class="mat-block">
<mat-label translate>tenant-profile.maximum-dashboards</mat-label>
<input matInput required min="0" step="1"
formControlName="maxDashboards"
type="number">
<mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('maxDashboards').hasError('required')">
{{ 'tenant-profile.maximum-dashboards-required' | translate}}
</mat-error>
<mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('maxDashboards').hasError('min')">
{{ 'tenant-profile.maximum-dashboards-range' | translate}}
</mat-error>
</mat-form-field>
<mat-form-field class="mat-block">
<mat-label translate>tenant-profile.maximum-rule-chains</mat-label>
<input matInput required min="0" step="1"
formControlName="maxRuleChains"
type="number">
<mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('maxRuleChains').hasError('required')">
{{ 'tenant-profile.maximum-rule-chains-required' | translate}}
</mat-error>
<mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('maxRuleChains').hasError('min')">
{{ 'tenant-profile.maximum-rule-chains-range' | translate}}
</mat-error>
</mat-form-field>
<mat-form-field class="mat-block">
<mat-label translate>tenant-profile.max-transport-messages</mat-label>
<input matInput required min="0" step="1"
formControlName="maxTransportMessages"
type="number">
<mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('maxTransportMessages').hasError('required')">
{{ 'tenant-profile.max-transport-messages-range' | translate}}
</mat-error>
<mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('maxTransportMessages').hasError('min')">
{{ 'tenant-profile.max-transport-messages-required' | translate}}
</mat-error>
</mat-form-field>
<mat-form-field class="mat-block">
<mat-label translate>tenant-profile.max-transport-data-points</mat-label>
<input matInput required min="0" step="1"
formControlName="maxTransportDataPoints"
type="number">
<mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('maxTransportDataPoints').hasError('required')">
{{ 'tenant-profile.max-transport-data-points-required' | translate}}
</mat-error>
<mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('maxTransportDataPoints').hasError('min')">
{{ 'tenant-profile.max-transport-data-points-range' | translate}}
</mat-error>
</mat-form-field>
<mat-form-field class="mat-block">
<mat-label translate>tenant-profile.max-r-e-executions</mat-label>
<input matInput required min="0" step="1"
formControlName="maxREExecutions"
type="number">
<mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('maxREExecutions').hasError('required')">
{{ 'tenant-profile.max-r-e-executions-required' | translate}}
</mat-error>
<mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('maxREExecutions').hasError('min')">
{{ 'tenant-profile.max-r-e-executions-range' | translate}}
</mat-error>
</mat-form-field>
<mat-form-field class="mat-block">
<mat-label translate>tenant-profile.max-j-s-executions</mat-label>
<input matInput required min="0" step="1"
formControlName="maxJSExecutions"
type="number">
<mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('maxJSExecutions').hasError('required')">
{{ 'tenant-profile.max-j-s-executions-required' | translate}}
</mat-error>
<mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('maxJSExecutions').hasError('min')">
{{ 'tenant-profile.max-j-s-executions-range' | translate}}
</mat-error>
</mat-form-field>
<mat-form-field class="mat-block">
<mat-label translate>tenant-profile.max-d-p-storage-days</mat-label>
<input matInput required min="0" step="1"
formControlName="maxDPStorageDays"
type="number">
<mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('maxDPStorageDays').hasError('required')">
{{ 'tenant-profile.max-d-p-storage-days-required' | translate}}
</mat-error>
<mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('maxDPStorageDays').hasError('min')">
{{ 'tenant-profile.max-d-p-storage-days-range' | translate}}
</mat-error>
</mat-form-field>
<mat-form-field class="mat-block">
<mat-label translate>tenant-profile.default-storage-ttl-days</mat-label>
<input matInput required min="0" step="1"
formControlName="defaultStorageTtlDays"
type="number">
<mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('defaultStorageTtlDays').hasError('required')">
{{ 'tenant-profile.default-storage-ttl-days-required' | translate}}
</mat-error>
<mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('defaultStorageTtlDays').hasError('min')">
{{ 'tenant-profile.default-storage-ttl-days-range' | translate}}
</mat-error>
</mat-form-field>
<mat-form-field class="mat-block">
<mat-label translate>tenant-profile.max-rule-node-executions-per-message</mat-label>
<input matInput required min="0" step="1"
formControlName="maxRuleNodeExecutionsPerMessage"
type="number">
<mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('maxRuleNodeExecutionsPerMessage').hasError('required')">
{{ 'tenant-profile.max-rule-node-executions-per-message-required' | translate}}
</mat-error>
<mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('maxRuleNodeExecutionsPerMessage').hasError('min')">
{{ 'tenant-profile.max-rule-node-executions-per-message-range' | translate}}
</mat-error>
</mat-form-field>
<mat-form-field class="mat-block">
<mat-label translate>tenant-profile.max-emails</mat-label>
<input matInput required min="0" step="1"
formControlName="maxEmails"
type="number">
<mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('maxEmails').hasError('required')">
{{ 'tenant-profile.max-emails-required' | translate}}
</mat-error>
<mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('maxEmails').hasError('min')">
{{ 'tenant-profile.max-emails-range' | translate}}
</mat-error>
</mat-form-field>
<mat-form-field class="mat-block">
<mat-label translate>tenant-profile.max-sms</mat-label>
<input matInput required min="0" step="1"
formControlName="maxSms"
type="number">
<mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('maxSms').hasError('required')">
{{ 'tenant-profile.max-sms-required' | translate}}
</mat-error>
<mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('maxSms').hasError('min')">
{{ 'tenant-profile.max-sms-range' | translate}}
</mat-error>
</mat-form-field>
<mat-form-field class="mat-block">
<mat-label translate>tenant-profile.max-created-alarms</mat-label>
<input matInput required min="0" step="1"
formControlName="maxCreatedAlarms"
type="number">
<mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('maxCreatedAlarms').hasError('required')">
{{ 'tenant-profile.max-created-alarms-required' | translate}}
</mat-error>
<mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('maxCreatedAlarms').hasError('min')">
{{ 'tenant-profile.max-created-alarms-range' | translate}}
</mat-error>
</mat-form-field>
<mat-form-field class="mat-block">
<mat-label translate>tenant-profile.transport-tenant-msg-rate-limit</mat-label>
<input matInput formControlName="transportTenantMsgRateLimit">
</mat-form-field>
<mat-form-field class="mat-block">
<mat-label translate>tenant-profile.transport-tenant-telemetry-msg-rate-limit</mat-label>
<input matInput formControlName="transportTenantTelemetryMsgRateLimit">
</mat-form-field>
<mat-form-field class="mat-block">
<mat-label translate>tenant-profile.transport-tenant-telemetry-data-points-rate-limit</mat-label>
<input matInput formControlName="transportTenantTelemetryDataPointsRateLimit">
</mat-form-field>
<mat-form-field class="mat-block">
<mat-label translate>tenant-profile.transport-device-msg-rate-limit</mat-label>
<input matInput formControlName="transportDeviceMsgRateLimit">
</mat-form-field>
<mat-form-field class="mat-block">
<mat-label translate>tenant-profile.transport-device-telemetry-msg-rate-limit</mat-label>
<input matInput formControlName="transportDeviceTelemetryMsgRateLimit">
</mat-form-field>
<mat-form-field class="mat-block">
<mat-label translate>tenant-profile.transport-device-telemetry-data-points-rate-limit</mat-label>
<input matInput formControlName="transportDeviceTelemetryDataPointsRateLimit">
</mat-form-field>
<section [formGroup]="defaultTenantProfileConfigurationFormGroup" fxLayout="column" fxLayoutGap="20px">
<mat-accordion [multi]="true">
<mat-expansion-panel>
<mat-expansion-panel-header>
Thingsboard rate limits
</mat-expansion-panel-header>
<ng-template matExpansionPanelContent>
<mat-form-field class="mat-block">
<mat-label translate>tenant-profile.maximum-devices</mat-label>
<input matInput required min="0" step="1"
formControlName="maxDevices"
type="number">
<mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('maxDevices').hasError('required')">
{{ 'tenant-profile.maximum-devices-required' | translate}}
</mat-error>
<mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('maxDevices').hasError('min')">
{{ 'tenant-profile.maximum-devices-range' | translate}}
</mat-error>
</mat-form-field>
<mat-form-field class="mat-block">
<mat-label translate>tenant-profile.maximum-assets</mat-label>
<input matInput required min="0" step="1"
formControlName="maxAssets"
type="number">
<mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('maxAssets').hasError('required')">
{{ 'tenant-profile.maximum-assets-required' | translate}}
</mat-error>
<mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('maxAssets').hasError('min')">
{{ 'tenant-profile.maximum-assets-range' | translate}}
</mat-error>
</mat-form-field>
<mat-form-field class="mat-block">
<mat-label translate>tenant-profile.maximum-customers</mat-label>
<input matInput required min="0" step="1"
formControlName="maxCustomers"
type="number">
<mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('maxCustomers').hasError('required')">
{{ 'tenant-profile.maximum-customers-required' | translate}}
</mat-error>
<mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('maxCustomers').hasError('min')">
{{ 'tenant-profile.maximum-customers-range' | translate}}
</mat-error>
</mat-form-field>
<mat-form-field class="mat-block">
<mat-label translate>tenant-profile.maximum-users</mat-label>
<input matInput required min="0" step="1"
formControlName="maxUsers"
type="number">
<mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('maxUsers').hasError('required')">
{{ 'tenant-profile.maximum-users-required' | translate}}
</mat-error>
<mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('maxUsers').hasError('min')">
{{ 'tenant-profile.maximum-users-range' | translate}}
</mat-error>
</mat-form-field>
<mat-form-field class="mat-block">
<mat-label translate>tenant-profile.maximum-dashboards</mat-label>
<input matInput required min="0" step="1"
formControlName="maxDashboards"
type="number">
<mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('maxDashboards').hasError('required')">
{{ 'tenant-profile.maximum-dashboards-required' | translate}}
</mat-error>
<mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('maxDashboards').hasError('min')">
{{ 'tenant-profile.maximum-dashboards-range' | translate}}
</mat-error>
</mat-form-field>
<mat-form-field class="mat-block">
<mat-label translate>tenant-profile.maximum-rule-chains</mat-label>
<input matInput required min="0" step="1"
formControlName="maxRuleChains"
type="number">
<mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('maxRuleChains').hasError('required')">
{{ 'tenant-profile.maximum-rule-chains-required' | translate}}
</mat-error>
<mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('maxRuleChains').hasError('min')">
{{ 'tenant-profile.maximum-rule-chains-range' | translate}}
</mat-error>
</mat-form-field>
<mat-form-field class="mat-block">
<mat-label translate>tenant-profile.max-transport-messages</mat-label>
<input matInput required min="0" step="1"
formControlName="maxTransportMessages"
type="number">
<mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('maxTransportMessages').hasError('required')">
{{ 'tenant-profile.max-transport-messages-range' | translate}}
</mat-error>
<mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('maxTransportMessages').hasError('min')">
{{ 'tenant-profile.max-transport-messages-required' | translate}}
</mat-error>
</mat-form-field>
<mat-form-field class="mat-block">
<mat-label translate>tenant-profile.max-transport-data-points</mat-label>
<input matInput required min="0" step="1"
formControlName="maxTransportDataPoints"
type="number">
<mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('maxTransportDataPoints').hasError('required')">
{{ 'tenant-profile.max-transport-data-points-required' | translate}}
</mat-error>
<mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('maxTransportDataPoints').hasError('min')">
{{ 'tenant-profile.max-transport-data-points-range' | translate}}
</mat-error>
</mat-form-field>
<mat-form-field class="mat-block">
<mat-label translate>tenant-profile.max-r-e-executions</mat-label>
<input matInput required min="0" step="1"
formControlName="maxREExecutions"
type="number">
<mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('maxREExecutions').hasError('required')">
{{ 'tenant-profile.max-r-e-executions-required' | translate}}
</mat-error>
<mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('maxREExecutions').hasError('min')">
{{ 'tenant-profile.max-r-e-executions-range' | translate}}
</mat-error>
</mat-form-field>
<mat-form-field class="mat-block">
<mat-label translate>tenant-profile.max-j-s-executions</mat-label>
<input matInput required min="0" step="1"
formControlName="maxJSExecutions"
type="number">
<mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('maxJSExecutions').hasError('required')">
{{ 'tenant-profile.max-j-s-executions-required' | translate}}
</mat-error>
<mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('maxJSExecutions').hasError('min')">
{{ 'tenant-profile.max-j-s-executions-range' | translate}}
</mat-error>
</mat-form-field>
<mat-form-field class="mat-block">
<mat-label translate>tenant-profile.max-d-p-storage-days</mat-label>
<input matInput required min="0" step="1"
formControlName="maxDPStorageDays"
type="number">
<mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('maxDPStorageDays').hasError('required')">
{{ 'tenant-profile.max-d-p-storage-days-required' | translate}}
</mat-error>
<mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('maxDPStorageDays').hasError('min')">
{{ 'tenant-profile.max-d-p-storage-days-range' | translate}}
</mat-error>
</mat-form-field>
<mat-form-field class="mat-block">
<mat-label translate>tenant-profile.default-storage-ttl-days</mat-label>
<input matInput required min="0" step="1"
formControlName="defaultStorageTtlDays"
type="number">
<mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('defaultStorageTtlDays').hasError('required')">
{{ 'tenant-profile.default-storage-ttl-days-required' | translate}}
</mat-error>
<mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('defaultStorageTtlDays').hasError('min')">
{{ 'tenant-profile.default-storage-ttl-days-range' | translate}}
</mat-error>
</mat-form-field>
<mat-form-field class="mat-block">
<mat-label translate>tenant-profile.max-rule-node-executions-per-message</mat-label>
<input matInput required min="0" step="1"
formControlName="maxRuleNodeExecutionsPerMessage"
type="number">
<mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('maxRuleNodeExecutionsPerMessage').hasError('required')">
{{ 'tenant-profile.max-rule-node-executions-per-message-required' | translate}}
</mat-error>
<mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('maxRuleNodeExecutionsPerMessage').hasError('min')">
{{ 'tenant-profile.max-rule-node-executions-per-message-range' | translate}}
</mat-error>
</mat-form-field>
<mat-form-field class="mat-block">
<mat-label translate>tenant-profile.max-emails</mat-label>
<input matInput required min="0" step="1"
formControlName="maxEmails"
type="number">
<mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('maxEmails').hasError('required')">
{{ 'tenant-profile.max-emails-required' | translate}}
</mat-error>
<mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('maxEmails').hasError('min')">
{{ 'tenant-profile.max-emails-range' | translate}}
</mat-error>
</mat-form-field>
<mat-form-field class="mat-block">
<mat-label translate>tenant-profile.max-sms</mat-label>
<input matInput required min="0" step="1"
formControlName="maxSms"
type="number">
<mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('maxSms').hasError('required')">
{{ 'tenant-profile.max-sms-required' | translate}}
</mat-error>
<mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('maxSms').hasError('min')">
{{ 'tenant-profile.max-sms-range' | translate}}
</mat-error>
</mat-form-field>
<mat-form-field class="mat-block">
<mat-label translate>tenant-profile.max-created-alarms</mat-label>
<input matInput required min="0" step="1"
formControlName="maxCreatedAlarms"
type="number">
<mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('maxCreatedAlarms').hasError('required')">
{{ 'tenant-profile.max-created-alarms-required' | translate}}
</mat-error>
<mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('maxCreatedAlarms').hasError('min')">
{{ 'tenant-profile.max-created-alarms-range' | translate}}
</mat-error>
</mat-form-field>
</ng-template>
</mat-expansion-panel>
<mat-expansion-panel>
<mat-expansion-panel-header>
Server rate limits
</mat-expansion-panel-header>
<ng-template matExpansionPanelContent>
<mat-form-field class="mat-block">
<mat-label translate>tenant-profile.tenant-rest-limits</mat-label>
<input matInput
formControlName="rateLimitsTenantConfiguration"
type="text">
<mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('rateLimitsTenantConfiguration').hasError('pattern')">
{{ 'tenant-profile.incorrect-pattern-for-rate-limits' | translate}}
</mat-error>
</mat-form-field>
<mat-form-field class="mat-block">
<mat-label translate>tenant-profile.customer-rest-limits</mat-label>
<input matInput
formControlName="rateLimitsCustomerConfiguration"
type="text">
<mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('rateLimitsCustomerConfiguration').hasError('pattern')">
{{ 'tenant-profile.incorrect-pattern-for-rate-limits' | translate}}
</mat-error>
</mat-form-field>
</ng-template>
</mat-expansion-panel>
<mat-expansion-panel>
<mat-expansion-panel-header>
Transport rate limits
</mat-expansion-panel-header>
<ng-template matExpansionPanelContent>
<mat-form-field class="mat-block">
<mat-label translate>tenant-profile.transport-tenant-msg-rate-limit</mat-label>
<input matInput formControlName="transportTenantMsgRateLimit">
<mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('transportTenantMsgRateLimit').hasError('pattern')">
{{ 'tenant-profile.incorrect-pattern-for-rate-limits' | translate}}
</mat-error>
</mat-form-field>
<mat-form-field class="mat-block">
<mat-label translate>tenant-profile.transport-tenant-telemetry-msg-rate-limit</mat-label>
<input matInput formControlName="transportTenantTelemetryMsgRateLimit">
<mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('transportTenantTelemetryMsgRateLimit').hasError('pattern')">
{{ 'tenant-profile.incorrect-pattern-for-rate-limits' | translate}}
</mat-error>
</mat-form-field>
<mat-form-field class="mat-block">
<mat-label translate>tenant-profile.transport-tenant-telemetry-data-points-rate-limit</mat-label>
<input matInput formControlName="transportTenantTelemetryDataPointsRateLimit">
<mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('transportTenantTelemetryDataPointsRateLimit').hasError('pattern')">
{{ 'tenant-profile.incorrect-pattern-for-rate-limits' | translate}}
</mat-error>
</mat-form-field>
<mat-form-field class="mat-block">
<mat-label translate>tenant-profile.transport-device-msg-rate-limit</mat-label>
<input matInput formControlName="transportDeviceMsgRateLimit">
<mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('transportDeviceMsgRateLimit').hasError('pattern')">
{{ 'tenant-profile.incorrect-pattern-for-rate-limits' | translate}}
</mat-error>
</mat-form-field>
<mat-form-field class="mat-block">
<mat-label translate>tenant-profile.transport-device-telemetry-msg-rate-limit</mat-label>
<input matInput formControlName="transportDeviceTelemetryMsgRateLimit">
<mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('transportDeviceTelemetryMsgRateLimit').hasError('pattern')">
{{ 'tenant-profile.incorrect-pattern-for-rate-limits' | translate}}
</mat-error>
</mat-form-field>
<mat-form-field class="mat-block">
<mat-label translate>tenant-profile.transport-device-telemetry-data-points-rate-limit</mat-label>
<input matInput formControlName="transportDeviceTelemetryDataPointsRateLimit">
<mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('transportDeviceTelemetryDataPointsRateLimit').hasError('pattern')">
{{ 'tenant-profile.incorrect-pattern-for-rate-limits' | translate}}
</mat-error>
</mat-form-field>
</ng-template>
</mat-expansion-panel>
<mat-expansion-panel>
<mat-expansion-panel-header>
Web Socket rate limits
</mat-expansion-panel-header>
<ng-template matExpansionPanelContent>
<mat-form-field class="mat-block">
<mat-label translate>tenant-profile.ws-limit-max-sessions-per-tenant</mat-label>
<input matInput type="number" formControlName="wsLimitMaxSessionsPerTenant">
<mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('wsLimitMaxSessionsPerTenant').hasError('min')">
{{ 'tenant-profile.too-small-value-zero' | translate}}
</mat-error>
</mat-form-field>
<mat-form-field class="mat-block">
<mat-label translate>tenant-profile.ws-limit-max-sessions-per-customer</mat-label>
<input matInput type="number" formControlName="wsLimitMaxSessionsPerCustomer">
<mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('wsLimitMaxSessionsPerCustomer').hasError('min')">
{{ 'tenant-profile.too-small-value-zero' | translate}}
</mat-error>
</mat-form-field>
<mat-form-field class="mat-block">
<mat-label translate>tenant-profile.ws-limit-max-sessions-per-public-user</mat-label>
<input matInput type="number" formControlName="wsLimitMaxSessionsPerPublicUser">
<mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('wsLimitMaxSessionsPerPublicUser').hasError('min')">
{{ 'tenant-profile.too-small-value-zero' | translate}}
</mat-error>
</mat-form-field>
<mat-form-field class="mat-block">
<mat-label translate>tenant-profile.ws-limit-queue-per-session</mat-label>
<input matInput type="number" formControlName="wsLimitQueuePerWsSession">
<mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('wsLimitQueuePerWsSession').hasError('min')">
{{ 'tenant-profile.too-small-value-one' | translate}}
</mat-error>
</mat-form-field>
<mat-form-field class="mat-block">
<mat-label translate>tenant-profile.ws-limit-max-subscriptions-per-tenant</mat-label>
<input matInput type="number" formControlName="wsLimitMaxSubscriptionsPerTenant">
<mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('wsLimitMaxSubscriptionsPerTenant').hasError('min')">
{{ 'tenant-profile.too-small-value-zero' | translate}}
</mat-error>
</mat-form-field>
<mat-form-field class="mat-block">
<mat-label translate>tenant-profile.ws-limit-max-subscriptions-per-customer</mat-label>
<input matInput type="number" formControlName="wsLimitMaxSubscriptionsPerCustomer">
<mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('wsLimitMaxSubscriptionsPerCustomer').hasError('min')">
{{ 'tenant-profile.too-small-value-zero' | translate}}
</mat-error>
</mat-form-field>
<mat-form-field class="mat-block">
<mat-label translate>tenant-profile.ws-limit-max-subscriptions-per-regular-user</mat-label>
<input matInput type="number" formControlName="wsLimitMaxSubscriptionsPerRegularUser">
<mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('wsLimitMaxSubscriptionsPerRegularUser').hasError('min')">
{{ 'tenant-profile.too-small-value-zero' | translate}}
</mat-error>
</mat-form-field>
<mat-form-field class="mat-block">
<mat-label translate>tenant-profile.ws-limit-max-subscriptions-per-public-user</mat-label>
<input matInput type="number" formControlName="wsLimitMaxSubscriptionsPerPublicUser">
<mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('wsLimitMaxSubscriptionsPerPublicUser').hasError('min')">
{{ 'tenant-profile.too-small-value-zero' | translate}}
</mat-error>
</mat-form-field>
<mat-form-field class="mat-block">
<mat-label translate>tenant-profile.ws-limit-updates-per-session</mat-label>
<input matInput type="text" formControlName="wsLimitUpdatesPerSession">
<mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('wsLimitUpdatesPerSession').hasError('pattern')">
{{ 'tenant-profile.incorrect-pattern-for-rate-limits' | translate}}
</mat-error>
</mat-form-field>
</ng-template>
</mat-expansion-panel>
<mat-expansion-panel>
<mat-expansion-panel-header>
Cassandra rate limits
</mat-expansion-panel-header>
<ng-template matExpansionPanelContent>
<mat-form-field class="mat-block">
<mat-label translate>tenant-profile.cassandra-tenant-limits-configuration</mat-label>
<input matInput type="text" formControlName="cassandraTenantLimitsConfiguration">
<mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('cassandraTenantLimitsConfiguration').hasError('pattern')">
{{ 'tenant-profile.incorrect-pattern-for-rate-limits' | translate}}
</mat-error>
</mat-form-field>
<mat-checkbox formControlName="printTenantNames">
{{ 'tenant-profile.cassandra-print-tenant-names' | translate }}
</mat-checkbox>
</ng-template>
</mat-expansion-panel>
</mat-accordion>
</section>

31
ui-ngx/src/app/modules/home/components/profile/tenant/default-tenant-profile-configuration.component.ts

@ -35,6 +35,7 @@ import { isDefinedAndNotNull } from '@core/utils';
export class DefaultTenantProfileConfigurationComponent implements ControlValueAccessor, OnInit {
defaultTenantProfileConfigurationFormGroup: FormGroup;
rateLimitsPattern = '^(((\\d+):(\\d+)),)*((\\d+):(\\d+))$';
private requiredValue: boolean;
get required(): boolean {
@ -59,12 +60,12 @@ 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)]],
transportTenantMsgRateLimit: [null, []],
transportTenantTelemetryMsgRateLimit: [null, []],
transportTenantTelemetryDataPointsRateLimit: [null, []],
transportDeviceMsgRateLimit: [null, []],
transportDeviceTelemetryMsgRateLimit: [null, []],
transportDeviceTelemetryDataPointsRateLimit: [null, []],
transportTenantMsgRateLimit: [null, [Validators.pattern(this.rateLimitsPattern)]],
transportTenantTelemetryMsgRateLimit: [null, [Validators.pattern(this.rateLimitsPattern)]],
transportTenantTelemetryDataPointsRateLimit: [null, [Validators.pattern(this.rateLimitsPattern)]],
transportDeviceMsgRateLimit: [null, [Validators.pattern(this.rateLimitsPattern)]],
transportDeviceTelemetryMsgRateLimit: [null, [Validators.pattern(this.rateLimitsPattern)]],
transportDeviceTelemetryDataPointsRateLimit: [null, [Validators.pattern(this.rateLimitsPattern)]],
maxTransportMessages: [null, [Validators.required, Validators.min(0)]],
maxTransportDataPoints: [null, [Validators.required, Validators.min(0)]],
maxREExecutions: [null, [Validators.required, Validators.min(0)]],
@ -74,7 +75,23 @@ export class DefaultTenantProfileConfigurationComponent implements ControlValueA
maxEmails: [null, [Validators.required, Validators.min(0)]],
maxSms: [null, [Validators.required, Validators.min(0)]],
maxCreatedAlarms: [null, [Validators.required, Validators.min(0)]],
defaultStorageTtlDays: [null, [Validators.required, Validators.min(0)]]
defaultStorageTtlDays: [null, [Validators.required, Validators.min(0)]],
rateLimitsTenantConfiguration: [null, [Validators.pattern(this.rateLimitsPattern)]],
rateLimitsCustomerConfiguration: [null, [Validators.pattern(this.rateLimitsPattern)]],
wsLimitMaxSessionsPerTenant: [null, [Validators.min(0)]],
wsLimitMaxSessionsPerCustomer: [null, [Validators.min(0)]],
wsLimitMaxSessionsPerPublicUser: [null, [Validators.min(0)]],
wsLimitQueuePerWsSession: [null, [Validators.min(0)]],
wsLimitMaxSubscriptionsPerTenant: [null, [Validators.min(0)]],
wsLimitMaxSubscriptionsPerCustomer: [null, [Validators.min(0)]],
wsLimitMaxSubscriptionsPerRegularUser: [null, [Validators.min(0)]],
wsLimitMaxSubscriptionsPerPublicUser: [null, [Validators.min(0)]],
wsLimitUpdatesPerSession: [null, [Validators.pattern(this.rateLimitsPattern)]],
cassandraTenantLimitsConfiguration: [null, [Validators.pattern(this.rateLimitsPattern)]],
printTenantNames: [null, []]
});
this.defaultTenantProfileConfigurationFormGroup.valueChanges.subscribe(() => {
this.updateModel();

38
ui-ngx/src/app/shared/models/tenant.model.ts

@ -46,6 +46,24 @@ export interface DefaultTenantProfileConfiguration {
maxRuleNodeExecutionsPerMessage: number;
maxEmails: number;
maxSms: number;
maxCreatedAlarms: number;
rateLimitsTenantConfiguration: string;
rateLimitsCustomerConfiguration: string;
wsLimitMaxSessionsPerTenant: number;
wsLimitMaxSessionsPerCustomer: number;
wsLimitMaxSessionsPerRegularUser: number;
wsLimitMaxSessionsPerPublicUser: number;
wsLimitQueuePerWsSession: number;
wsLimitMaxSubscriptionsPerTenant: number;
wsLimitMaxSubscriptionsPerCustomer: number;
wsLimitMaxSubscriptionsPerRegularUser: number;
wsLimitMaxSubscriptionsPerPublicUser: number;
wsLimitUpdatesPerSession: string;
cassandraTenantLimitsConfiguration: string;
printTenantNames: boolean;
defaultStorageTtlDays: number;
}
@ -76,7 +94,25 @@ export function createTenantProfileConfiguration(type: TenantProfileType): Tenan
maxRuleNodeExecutionsPerMessage: 0,
maxEmails: 0,
maxSms: 0,
defaultStorageTtlDays: 0
defaultStorageTtlDays: 0,
maxCreatedAlarms: 0,
rateLimitsTenantConfiguration: '',
rateLimitsCustomerConfiguration: '',
wsLimitMaxSessionsPerTenant: 0,
wsLimitMaxSessionsPerCustomer: 0,
wsLimitMaxSessionsPerRegularUser: 0,
wsLimitMaxSessionsPerPublicUser: 0,
wsLimitQueuePerWsSession: 500,
wsLimitMaxSubscriptionsPerTenant: 0,
wsLimitMaxSubscriptionsPerCustomer: 0,
wsLimitMaxSubscriptionsPerRegularUser: 0,
wsLimitMaxSubscriptionsPerPublicUser: 0,
wsLimitUpdatesPerSession: '' ,
cassandraTenantLimitsConfiguration: '',
printTenantNames: false
};
configuration = {...defaultConfiguration, type: TenantProfileType.DEFAULT};
break;

18
ui-ngx/src/assets/locale/locale.constant-en_US.json

@ -2533,7 +2533,23 @@
"max-sms-range": "Maximum number of SMS sent can't be negative",
"max-created-alarms": "Maximum number of alarms created (0 - unlimited)",
"max-created-alarms-required": "Maximum number of alarms created is required.",
"max-created-alarms-range": "Maximum number of alarms created can't be negative"
"max-created-alarms-range": "Maximum number of alarms created can't be negative",
"tenant-rest-limits": "Server limits of request for Tenant",
"customer-rest-limits": "Server limits of request for Customer",
"incorrect-pattern-for-rate-limits": "Here should be conformity of a number of requests per seconds divided by colon, comma separated. For example: 100:1,2000:60",
"too-small-value-zero": "Too small value specified, it should be more than 0",
"too-small-value-one": "Too small value specified, it should be more than 1",
"cassandra-tenant-limits-configuration": "Tenant limits configuration",
"ws-limit-max-sessions-per-tenant": "Max sessions per Tenant",
"ws-limit-max-sessions-per-customer": "Max sessions per Customer",
"ws-limit-max-sessions-per-public-user": "Max sessions per Public User",
"ws-limit-queue-per-session": "Limit queue per session",
"ws-limit-max-subscriptions-per-tenant": "Max subscriptions per Tenant",
"ws-limit-max-subscriptions-per-customer": "Max subscriptions per Customer",
"ws-limit-max-subscriptions-per-regular-user": "Max subscriptions per regular User",
"ws-limit-max-subscriptions-per-public-user": "Max subscriptions per public User",
"ws-limit-updates-per-session": "Constraint of updates per session",
"cassandra-print-tenant-names": "Print Tenant names to log"
},
"timeinterval": {
"seconds-interval": "{ seconds, plural, 1 {1 second} other {# seconds} }",

Loading…
Cancel
Save