Resolved review comments
This commit is contained in:
parent
ec8e7a0726
commit
61f77f301d
@ -40,7 +40,8 @@ public abstract class BaseApiUsageState {
|
|||||||
private final Map<ApiUsageRecordKey, Long> gaugesReportCycles = new HashMap<>();
|
private final Map<ApiUsageRecordKey, Long> gaugesReportCycles = new HashMap<>();
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
private final ApiUsageState apiUsageState;
|
@Setter
|
||||||
|
private ApiUsageState apiUsageState;
|
||||||
@Getter
|
@Getter
|
||||||
private volatile long currentCycleTs;
|
private volatile long currentCycleTs;
|
||||||
@Getter
|
@Getter
|
||||||
|
|||||||
@ -355,7 +355,8 @@ public class DefaultTbApiUsageStateService extends AbstractPartitionBasedService
|
|||||||
|
|
||||||
private void persistAndNotify(BaseApiUsageState state, Map<ApiFeature, ApiUsageStateValue> result) {
|
private void persistAndNotify(BaseApiUsageState state, Map<ApiFeature, ApiUsageStateValue> result) {
|
||||||
log.info("[{}] Detected update of the API state for {}: {}", state.getEntityId(), state.getEntityType(), result);
|
log.info("[{}] Detected update of the API state for {}: {}", state.getEntityId(), state.getEntityType(), result);
|
||||||
apiUsageStateService.update(state.getApiUsageState());
|
ApiUsageState updatedState = apiUsageStateService.update(state.getApiUsageState());
|
||||||
|
state.setApiUsageState(updatedState);
|
||||||
long ts = System.currentTimeMillis();
|
long ts = System.currentTimeMillis();
|
||||||
List<TsKvEntry> stateTelemetry = new ArrayList<>();
|
List<TsKvEntry> stateTelemetry = new ArrayList<>();
|
||||||
result.forEach((apiFeature, aState) -> stateTelemetry.add(new BasicTsKvEntry(ts, new StringDataEntry(apiFeature.getApiStateKey(), aState.name()))));
|
result.forEach((apiFeature, aState) -> stateTelemetry.add(new BasicTsKvEntry(ts, new StringDataEntry(apiFeature.getApiStateKey(), aState.name()))));
|
||||||
|
|||||||
@ -15,6 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
package org.thingsboard.server.service.apiusage;
|
package org.thingsboard.server.service.apiusage;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.extension.ExtendWith;
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
@ -23,14 +24,43 @@ import org.mockito.Mock;
|
|||||||
import org.mockito.Mockito;
|
import org.mockito.Mockito;
|
||||||
import org.mockito.Spy;
|
import org.mockito.Spy;
|
||||||
import org.mockito.junit.jupiter.MockitoExtension;
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
|
import org.springframework.test.util.ReflectionTestUtils;
|
||||||
|
import org.thingsboard.server.common.data.ApiFeature;
|
||||||
|
import org.thingsboard.server.common.data.ApiUsageRecordKey;
|
||||||
import org.thingsboard.server.common.data.ApiUsageState;
|
import org.thingsboard.server.common.data.ApiUsageState;
|
||||||
|
import org.thingsboard.server.common.data.ApiUsageStateValue;
|
||||||
|
import org.thingsboard.server.common.data.EntityType;
|
||||||
|
import org.thingsboard.server.common.data.Tenant;
|
||||||
import org.thingsboard.server.common.data.id.TenantId;
|
import org.thingsboard.server.common.data.id.TenantId;
|
||||||
|
import org.thingsboard.server.common.data.notification.rule.trigger.ApiUsageLimitTrigger;
|
||||||
|
import org.thingsboard.server.common.msg.notification.NotificationRuleProcessor;
|
||||||
|
import org.thingsboard.server.common.msg.queue.TbCallback;
|
||||||
|
import org.thingsboard.server.dao.tenant.TenantService;
|
||||||
|
import org.thingsboard.server.dao.usagerecord.ApiUsageStateService;
|
||||||
|
import org.thingsboard.server.gen.transport.TransportProtos;
|
||||||
|
import org.thingsboard.server.queue.common.TbProtoQueueMsg;
|
||||||
|
import org.thingsboard.server.service.mail.MailExecutorService;
|
||||||
|
import org.thingsboard.server.service.telemetry.InternalTelemetryService;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
import static org.hamcrest.CoreMatchers.is;
|
import static org.hamcrest.CoreMatchers.is;
|
||||||
import static org.hamcrest.MatcherAssert.assertThat;
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.ArgumentMatchers.anyLong;
|
||||||
|
import static org.mockito.ArgumentMatchers.anyString;
|
||||||
|
import static org.mockito.ArgumentMatchers.argThat;
|
||||||
|
import static org.mockito.Mockito.atLeastOnce;
|
||||||
|
import static org.mockito.Mockito.doNothing;
|
||||||
|
import static org.mockito.Mockito.doReturn;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.never;
|
import static org.mockito.Mockito.never;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
@ExtendWith(MockitoExtension.class)
|
@ExtendWith(MockitoExtension.class)
|
||||||
public class DefaultTbApiUsageStateServiceTest {
|
public class DefaultTbApiUsageStateServiceTest {
|
||||||
@ -38,6 +68,21 @@ public class DefaultTbApiUsageStateServiceTest {
|
|||||||
@Mock
|
@Mock
|
||||||
TenantApiUsageState tenantUsageStateMock;
|
TenantApiUsageState tenantUsageStateMock;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private NotificationRuleProcessor notificationRuleProcessor;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private ApiUsageStateService apiUsageStateService;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private TenantService tenantService;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private InternalTelemetryService tsWsService;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private MailExecutorService mailExecutor;
|
||||||
|
|
||||||
TenantId tenantId = TenantId.fromUUID(UUID.fromString("00797a3b-7aeb-4b5b-b57a-c2a810d0f112"));
|
TenantId tenantId = TenantId.fromUUID(UUID.fromString("00797a3b-7aeb-4b5b-b57a-c2a810d0f112"));
|
||||||
|
|
||||||
@Spy
|
@Spy
|
||||||
@ -46,6 +91,7 @@ public class DefaultTbApiUsageStateServiceTest {
|
|||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
|
ReflectionTestUtils.setField(service, "tsWsService", tsWsService);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -56,4 +102,100 @@ public class DefaultTbApiUsageStateServiceTest {
|
|||||||
Mockito.verify(service, never()).getOrFetchState(tenantId, tenantId);
|
Mockito.verify(service, never()).getOrFetchState(tenantId, tenantId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAllApiFeaturesDisabledWhenLimitReached() {
|
||||||
|
doReturn(null).when(tsWsService).saveTimeseriesInternal(any());
|
||||||
|
|
||||||
|
TenantApiUsageState tenantUsageStateMock = mock(TenantApiUsageState.class);
|
||||||
|
ApiUsageState apiUsageState = getApiUsageState();
|
||||||
|
when(tenantUsageStateMock.getApiUsageState()).thenReturn(apiUsageState);
|
||||||
|
|
||||||
|
doReturn(BaseApiUsageState.StatsCalculationResult.builder()
|
||||||
|
.newValue(200L)
|
||||||
|
.valueChanged(true)
|
||||||
|
.newHourlyValue(200L)
|
||||||
|
.hourlyValueChanged(true)
|
||||||
|
.build())
|
||||||
|
.when(tenantUsageStateMock).calculate(any(ApiUsageRecordKey.class), anyLong(), anyString());
|
||||||
|
|
||||||
|
doReturn(200L).when(tenantUsageStateMock).getProfileThreshold(any(ApiUsageRecordKey.class));
|
||||||
|
doReturn(50L).when(tenantUsageStateMock).getProfileWarnThreshold(any(ApiUsageRecordKey.class));
|
||||||
|
doReturn(300L).when(tenantUsageStateMock).get(any(ApiUsageRecordKey.class));
|
||||||
|
|
||||||
|
when(tenantUsageStateMock.getEntityType()).thenReturn(EntityType.TENANT);
|
||||||
|
when(tenantUsageStateMock.getEntityId()).thenReturn(tenantId);
|
||||||
|
|
||||||
|
Map<ApiFeature, ApiUsageStateValue> expectedResult = getExpectedResult();
|
||||||
|
when(tenantUsageStateMock.checkStateUpdatedDueToThreshold(any())).thenReturn(expectedResult);
|
||||||
|
service.myUsageStates.put(tenantId, tenantUsageStateMock);
|
||||||
|
|
||||||
|
when(apiUsageStateService.update(apiUsageState)).thenReturn(apiUsageState);
|
||||||
|
|
||||||
|
Tenant dummyTenant = new Tenant();
|
||||||
|
dummyTenant.setEmail("test@example.com");
|
||||||
|
when(tenantService.findTenantById(any())).thenReturn(dummyTenant);
|
||||||
|
|
||||||
|
TransportProtos.ToUsageStatsServiceMsg.Builder msgBuilder = TransportProtos.ToUsageStatsServiceMsg.newBuilder();
|
||||||
|
UUID uuid = tenantId.getId();
|
||||||
|
msgBuilder.setTenantIdMSB(uuid.getMostSignificantBits())
|
||||||
|
.setTenantIdLSB(uuid.getLeastSignificantBits());
|
||||||
|
msgBuilder.setCustomerIdMSB(0)
|
||||||
|
.setCustomerIdLSB(0);
|
||||||
|
msgBuilder.setServiceId("TEST_SERVICE");
|
||||||
|
|
||||||
|
List<TransportProtos.UsageStatsKVProto> usageStats = new ArrayList<>();
|
||||||
|
for (ApiUsageRecordKey recordKey : ApiUsageRecordKey.values()) {
|
||||||
|
if (recordKey.getApiFeature() != null) {
|
||||||
|
TransportProtos.UsageStatsKVProto stat = TransportProtos.UsageStatsKVProto.newBuilder()
|
||||||
|
.setKey(recordKey.name())
|
||||||
|
.setValue(1000L)
|
||||||
|
.build();
|
||||||
|
usageStats.add(stat);
|
||||||
|
msgBuilder.addValues(stat);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TransportProtos.ToUsageStatsServiceMsg statsMsg = msgBuilder.build();
|
||||||
|
TbProtoQueueMsg<TransportProtos.ToUsageStatsServiceMsg> queueMsg = new TbProtoQueueMsg<>(UUID.randomUUID(), statsMsg);
|
||||||
|
TbCallback callback = mock(TbCallback.class);
|
||||||
|
|
||||||
|
service.process(queueMsg, callback);
|
||||||
|
verify(callback).onSuccess();
|
||||||
|
|
||||||
|
for (ApiFeature feature : expectedResult.keySet()) {
|
||||||
|
verify(notificationRuleProcessor, atLeastOnce()).process(argThat(trigger ->
|
||||||
|
trigger instanceof ApiUsageLimitTrigger &&
|
||||||
|
((ApiUsageLimitTrigger) trigger).getStatus() == ApiUsageStateValue.DISABLED &&
|
||||||
|
((ApiUsageLimitTrigger) trigger).getState().getApiFeature().getApiStateKey().equals(feature.getApiStateKey())
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private ApiUsageState getApiUsageState() {
|
||||||
|
ApiUsageState apiUsageState = new ApiUsageState();
|
||||||
|
|
||||||
|
apiUsageState.setTenantId(tenantId);
|
||||||
|
apiUsageState.setTransportState(ApiUsageStateValue.ENABLED);
|
||||||
|
apiUsageState.setDbStorageState(ApiUsageStateValue.ENABLED);
|
||||||
|
apiUsageState.setReExecState(ApiUsageStateValue.ENABLED);
|
||||||
|
apiUsageState.setJsExecState(ApiUsageStateValue.ENABLED);
|
||||||
|
apiUsageState.setTbelExecState(ApiUsageStateValue.ENABLED);
|
||||||
|
apiUsageState.setEmailExecState(ApiUsageStateValue.ENABLED);
|
||||||
|
apiUsageState.setSmsExecState(ApiUsageStateValue.ENABLED);
|
||||||
|
apiUsageState.setAlarmExecState(ApiUsageStateValue.ENABLED);
|
||||||
|
return apiUsageState;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<ApiFeature, ApiUsageStateValue> getExpectedResult() {
|
||||||
|
Map<ApiFeature, ApiUsageStateValue> expectedResult = new HashMap<>();
|
||||||
|
for (ApiUsageRecordKey recordKey : ApiUsageRecordKey.values()) {
|
||||||
|
if (recordKey.getApiFeature() != null) {
|
||||||
|
expectedResult.put(recordKey.getApiFeature(), ApiUsageStateValue.DISABLED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return expectedResult;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -34,4 +34,5 @@ public interface ApiUsageStateService extends EntityDaoService {
|
|||||||
void deleteApiUsageStateByEntityId(EntityId entityId);
|
void deleteApiUsageStateByEntityId(EntityId entityId);
|
||||||
|
|
||||||
ApiUsageState findApiUsageStateById(TenantId tenantId, ApiUsageStateId id);
|
ApiUsageState findApiUsageStateById(TenantId tenantId, ApiUsageStateId id);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -86,11 +86,11 @@ public class ApiUsageState extends BaseData<ApiUsageStateId> implements HasTenan
|
|||||||
return !ApiUsageStateValue.DISABLED.equals(tbelExecState);
|
return !ApiUsageStateValue.DISABLED.equals(tbelExecState);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isEmailSendEnabled(){
|
public boolean isEmailSendEnabled() {
|
||||||
return !ApiUsageStateValue.DISABLED.equals(emailExecState);
|
return !ApiUsageStateValue.DISABLED.equals(emailExecState);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isSmsSendEnabled(){
|
public boolean isSmsSendEnabled() {
|
||||||
return !ApiUsageStateValue.DISABLED.equals(smsExecState);
|
return !ApiUsageStateValue.DISABLED.equals(smsExecState);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1019,7 +1019,9 @@ public class ProtoUtils {
|
|||||||
.setTbelExecState(apiUsageState.getTbelExecState().name())
|
.setTbelExecState(apiUsageState.getTbelExecState().name())
|
||||||
.setEmailExecState(apiUsageState.getEmailExecState().name())
|
.setEmailExecState(apiUsageState.getEmailExecState().name())
|
||||||
.setSmsExecState(apiUsageState.getSmsExecState().name())
|
.setSmsExecState(apiUsageState.getSmsExecState().name())
|
||||||
.setAlarmExecState(apiUsageState.getAlarmExecState().name()).build();
|
.setAlarmExecState(apiUsageState.getAlarmExecState().name())
|
||||||
|
.setVersion(apiUsageState.getVersion())
|
||||||
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ApiUsageState fromProto(TransportProtos.ApiUsageStateProto proto) {
|
public static ApiUsageState fromProto(TransportProtos.ApiUsageStateProto proto) {
|
||||||
@ -1035,6 +1037,7 @@ public class ProtoUtils {
|
|||||||
apiUsageState.setEmailExecState(ApiUsageStateValue.valueOf(proto.getEmailExecState()));
|
apiUsageState.setEmailExecState(ApiUsageStateValue.valueOf(proto.getEmailExecState()));
|
||||||
apiUsageState.setSmsExecState(ApiUsageStateValue.valueOf(proto.getSmsExecState()));
|
apiUsageState.setSmsExecState(ApiUsageStateValue.valueOf(proto.getSmsExecState()));
|
||||||
apiUsageState.setAlarmExecState(ApiUsageStateValue.valueOf(proto.getAlarmExecState()));
|
apiUsageState.setAlarmExecState(ApiUsageStateValue.valueOf(proto.getAlarmExecState()));
|
||||||
|
apiUsageState.setVersion(proto.getVersion());
|
||||||
return apiUsageState;
|
return apiUsageState;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -325,6 +325,7 @@ message ApiUsageStateProto {
|
|||||||
string emailExecState = 14;
|
string emailExecState = 14;
|
||||||
string smsExecState = 15;
|
string smsExecState = 15;
|
||||||
string alarmExecState = 16;
|
string alarmExecState = 16;
|
||||||
|
int64 version = 17;
|
||||||
}
|
}
|
||||||
|
|
||||||
message RepositorySettingsProto {
|
message RepositorySettingsProto {
|
||||||
|
|||||||
@ -154,6 +154,7 @@ public class ApiUsageStateServiceImpl extends AbstractEntityService implements A
|
|||||||
log.trace("Executing save [{}]", apiUsageState.getTenantId());
|
log.trace("Executing save [{}]", apiUsageState.getTenantId());
|
||||||
validateId(apiUsageState.getTenantId(), id -> INCORRECT_TENANT_ID + id);
|
validateId(apiUsageState.getTenantId(), id -> INCORRECT_TENANT_ID + id);
|
||||||
validateId(apiUsageState.getId(), "Can't save new usage state. Only update is allowed!");
|
validateId(apiUsageState.getId(), "Can't save new usage state. Only update is allowed!");
|
||||||
|
apiUsageState.setVersion(null);
|
||||||
ApiUsageState savedState = apiUsageStateDao.save(apiUsageState.getTenantId(), apiUsageState);
|
ApiUsageState savedState = apiUsageStateDao.save(apiUsageState.getTenantId(), apiUsageState);
|
||||||
eventPublisher.publishEvent(SaveEntityEvent.builder().tenantId(savedState.getTenantId()).entityId(savedState.getId())
|
eventPublisher.publishEvent(SaveEntityEvent.builder().tenantId(savedState.getTenantId()).entityId(savedState.getId())
|
||||||
.entity(savedState).build());
|
.entity(savedState).build());
|
||||||
|
|||||||
@ -20,6 +20,7 @@ import org.junit.Test;
|
|||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.thingsboard.server.common.data.ApiUsageState;
|
import org.thingsboard.server.common.data.ApiUsageState;
|
||||||
import org.thingsboard.server.common.data.ApiUsageStateValue;
|
import org.thingsboard.server.common.data.ApiUsageStateValue;
|
||||||
|
import org.thingsboard.server.dao.exception.IncorrectParameterException;
|
||||||
import org.thingsboard.server.dao.usagerecord.ApiUsageStateService;
|
import org.thingsboard.server.dao.usagerecord.ApiUsageStateService;
|
||||||
|
|
||||||
|
|
||||||
@ -30,22 +31,42 @@ public class ApiUsageStateServiceTest extends AbstractServiceTest {
|
|||||||
ApiUsageStateService apiUsageStateService;
|
ApiUsageStateService apiUsageStateService;
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFindApiUsageStateByTenantId() {
|
public void testFindTenantApiUsageState() {
|
||||||
ApiUsageState apiUsageState = apiUsageStateService.findTenantApiUsageState(tenantId);
|
ApiUsageState state = apiUsageStateService.findTenantApiUsageState(tenantId);
|
||||||
Assert.assertNotNull(apiUsageState);
|
Assert.assertNotNull(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testUpdateApiUsageState(){
|
public void testUpdate() {
|
||||||
ApiUsageState apiUsageState = apiUsageStateService.findTenantApiUsageState(tenantId);
|
ApiUsageState state = apiUsageStateService.findTenantApiUsageState(tenantId);
|
||||||
Assert.assertNotNull(apiUsageState);
|
|
||||||
Assert.assertTrue(apiUsageState.isTransportEnabled());
|
state.setTransportState(ApiUsageStateValue.DISABLED);
|
||||||
apiUsageState.setTransportState(ApiUsageStateValue.DISABLED);
|
ApiUsageState updated = apiUsageStateService.update(state);
|
||||||
apiUsageState = apiUsageStateService.update(apiUsageState);
|
Assert.assertEquals(ApiUsageStateValue.DISABLED, updated.getTransportState());
|
||||||
Assert.assertNotNull(apiUsageState);
|
}
|
||||||
apiUsageState = apiUsageStateService.findTenantApiUsageState(tenantId);
|
|
||||||
Assert.assertNotNull(apiUsageState);
|
@Test
|
||||||
Assert.assertFalse(apiUsageState.isTransportEnabled());
|
public void testUpdateWithNullId() {
|
||||||
|
ApiUsageState newState = new ApiUsageState();
|
||||||
|
newState.setTenantId(tenantId);
|
||||||
|
newState.setTransportState(ApiUsageStateValue.ENABLED);
|
||||||
|
Assert.assertThrows(IncorrectParameterException.class, () -> apiUsageStateService.update(newState));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFindApiUsageStateByEntityId() {
|
||||||
|
ApiUsageState state = apiUsageStateService.findApiUsageStateByEntityId(tenantId);
|
||||||
|
Assert.assertNotNull(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDeleteByTenantId() {
|
||||||
|
ApiUsageState state = apiUsageStateService.findTenantApiUsageState(tenantId);
|
||||||
|
Assert.assertNotNull(state);
|
||||||
|
|
||||||
|
apiUsageStateService.deleteByTenantId(tenantId);
|
||||||
|
state = apiUsageStateService.findTenantApiUsageState(tenantId);
|
||||||
|
Assert.assertNull(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user