committed by
GitHub
16 changed files with 330 additions and 28 deletions
@ -0,0 +1,171 @@ |
|||
/** |
|||
* Copyright © 2016-2024 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.transport.mqtt; |
|||
|
|||
import com.fasterxml.jackson.databind.node.ObjectNode; |
|||
import org.junit.Assert; |
|||
import org.junit.Before; |
|||
import org.junit.Test; |
|||
import org.mockito.Mockito; |
|||
import org.springframework.boot.test.mock.mockito.SpyBean; |
|||
import org.springframework.test.context.TestPropertySource; |
|||
import org.thingsboard.common.util.JacksonUtil; |
|||
import org.thingsboard.server.common.data.Device; |
|||
import org.thingsboard.server.common.data.TenantProfile; |
|||
import org.thingsboard.server.common.data.id.DeviceId; |
|||
import org.thingsboard.server.common.data.notification.rule.trigger.RateLimitsTrigger; |
|||
import org.thingsboard.server.common.data.security.DeviceCredentials; |
|||
import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; |
|||
import org.thingsboard.server.common.msg.notification.NotificationRuleProcessor; |
|||
import org.thingsboard.server.controller.AbstractControllerTest; |
|||
import org.thingsboard.server.dao.service.DaoSqlTest; |
|||
import org.thingsboard.server.transport.mqtt.mqttv3.MqttTestClient; |
|||
|
|||
import java.util.function.Consumer; |
|||
|
|||
import static org.junit.Assert.assertEquals; |
|||
import static org.junit.Assert.assertNotNull; |
|||
import static org.mockito.ArgumentMatchers.eq; |
|||
import static org.thingsboard.server.common.data.limit.LimitedApi.TRANSPORT_MESSAGES_PER_GATEWAY; |
|||
|
|||
@DaoSqlTest |
|||
@TestPropertySource(properties = { |
|||
"service.integrations.supported=ALL", |
|||
"transport.mqtt.enabled=true", |
|||
}) |
|||
public class MqttGatewayRateLimitsTest extends AbstractControllerTest { |
|||
|
|||
private static final String TOPIC = "v1/gateway/telemetry"; |
|||
private static final String DEVICE_A = "DeviceA"; |
|||
private static final String DEVICE_B = "DeviceB"; |
|||
|
|||
private DeviceId gatewayId; |
|||
private String gatewayAccessToken; |
|||
|
|||
@SpyBean |
|||
private NotificationRuleProcessor notificationRuleProcessor; |
|||
|
|||
@Before |
|||
public void beforeTest() throws Exception { |
|||
loginSysAdmin(); |
|||
|
|||
TenantProfile tenantProfile = doGet("/api/tenantProfile/" + tenantProfileId, TenantProfile.class); |
|||
Assert.assertNotNull(tenantProfile); |
|||
|
|||
DefaultTenantProfileConfiguration profileConfiguration = (DefaultTenantProfileConfiguration) tenantProfile.getProfileData().getConfiguration(); |
|||
|
|||
profileConfiguration.setTransportGatewayMsgRateLimit(null); |
|||
profileConfiguration.setTransportGatewayTelemetryMsgRateLimit(null); |
|||
profileConfiguration.setTransportGatewayTelemetryDataPointsRateLimit(null); |
|||
|
|||
doPost("/api/tenantProfile", tenantProfile); |
|||
|
|||
loginTenantAdmin(); |
|||
createGateway(); |
|||
|
|||
Mockito.reset(notificationRuleProcessor); |
|||
} |
|||
|
|||
@Test |
|||
public void transportGatewayMsgRateLimitTest() throws Exception { |
|||
transportGatewayRateLimitTest(profileConfiguration -> profileConfiguration.setTransportGatewayMsgRateLimit("1:600")); |
|||
} |
|||
|
|||
@Test |
|||
public void transportGatewayTelemetryMsgRateLimitTest() throws Exception { |
|||
transportGatewayRateLimitTest(profileConfiguration -> profileConfiguration.setTransportGatewayTelemetryMsgRateLimit("1:600")); |
|||
} |
|||
|
|||
@Test |
|||
public void transportGatewayTelemetryDataPointsRateLimitTest() throws Exception { |
|||
transportGatewayRateLimitTest(profileConfiguration -> profileConfiguration.setTransportGatewayTelemetryDataPointsRateLimit("1:600")); |
|||
} |
|||
|
|||
private void transportGatewayRateLimitTest(Consumer<DefaultTenantProfileConfiguration> profileConfiguration) throws Exception { |
|||
loginSysAdmin(); |
|||
|
|||
TenantProfile tenantProfile = doGet("/api/tenantProfile/" + tenantProfileId, TenantProfile.class); |
|||
Assert.assertNotNull(tenantProfile); |
|||
|
|||
profileConfiguration.accept((DefaultTenantProfileConfiguration) tenantProfile.getProfileData().getConfiguration()); |
|||
|
|||
doPost("/api/tenantProfile", tenantProfile); |
|||
|
|||
MqttTestClient client = new MqttTestClient(); |
|||
client.connectAndWait(gatewayAccessToken); |
|||
client.publishAndWait(TOPIC, getGatewayPayload(DEVICE_A)); |
|||
|
|||
loginTenantAdmin(); |
|||
|
|||
Device deviceA = getDeviceByName(DEVICE_A); |
|||
|
|||
var deviceATrigger = createRateLimitsTrigger(deviceA); |
|||
|
|||
Mockito.verify(notificationRuleProcessor, Mockito.never()).process(eq(deviceATrigger)); |
|||
|
|||
try { |
|||
client.publishAndWait(TOPIC, getGatewayPayload(DEVICE_B)); |
|||
} catch (Exception t) { |
|||
} |
|||
|
|||
Device deviceB = getDeviceByName(DEVICE_B); |
|||
|
|||
var deviceBTrigger = createRateLimitsTrigger(deviceB); |
|||
|
|||
Mockito.verify(notificationRuleProcessor, Mockito.times(1)).process(deviceBTrigger); |
|||
|
|||
if (client.isConnected()) { |
|||
client.disconnect(); |
|||
} |
|||
} |
|||
|
|||
private void createGateway() throws Exception { |
|||
Device device = new Device(); |
|||
device.setName("gateway"); |
|||
ObjectNode additionalInfo = JacksonUtil.newObjectNode(); |
|||
additionalInfo.put("gateway", true); |
|||
device.setAdditionalInfo(additionalInfo); |
|||
device = doPost("/api/device", device, Device.class); |
|||
assertNotNull(device); |
|||
gatewayId = device.getId(); |
|||
assertNotNull(gatewayId); |
|||
|
|||
DeviceCredentials deviceCredentials = doGet("/api/device/" + gatewayId + "/credentials", DeviceCredentials.class); |
|||
assertNotNull(deviceCredentials); |
|||
assertEquals(gatewayId, deviceCredentials.getDeviceId()); |
|||
gatewayAccessToken = deviceCredentials.getCredentialsId(); |
|||
assertNotNull(gatewayAccessToken); |
|||
} |
|||
|
|||
private Device getDeviceByName(String deviceName) throws Exception { |
|||
Device device = doGet("/api/tenant/devices?deviceName=" + deviceName, Device.class); |
|||
assertNotNull(device); |
|||
return device; |
|||
} |
|||
|
|||
private byte[] getGatewayPayload(String deviceName) { |
|||
return String.format("{\"%s\": [{\"values\": {\"temperature\": 42}}]}", deviceName).getBytes(); |
|||
} |
|||
|
|||
private RateLimitsTrigger createRateLimitsTrigger(Device device) { |
|||
return RateLimitsTrigger.builder() |
|||
.tenantId(tenantId) |
|||
.api(TRANSPORT_MESSAGES_PER_GATEWAY) |
|||
.limitLevel(device.getId()) |
|||
.limitLevelEntityName(device.getName()) |
|||
.build(); |
|||
} |
|||
} |
|||
@ -0,0 +1,20 @@ |
|||
/** |
|||
* Copyright © 2016-2024 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.common.transport.limits; |
|||
|
|||
public enum TransportLimitsType { |
|||
TENANT_LIMITS, DEVICE_LIMITS, GATEWAY_LIMITS |
|||
} |
|||
Loading…
Reference in new issue