Browse Source

Merge pull request #12648 from artem-barysh-dev/api-usage-version

Added versioning for ApiUsageState entity
pull/12751/head
Viacheslav Klimov 1 year ago
committed by GitHub
parent
commit
fbb6700a6c
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 2
      application/src/main/data/upgrade/basic/schema_update.sql
  2. 3
      application/src/main/java/org/thingsboard/server/service/apiusage/BaseApiUsageState.java
  3. 3
      application/src/main/java/org/thingsboard/server/service/apiusage/DefaultTbApiUsageStateService.java
  4. 144
      application/src/test/java/org/thingsboard/server/service/apiusage/ApiUsageTest.java
  5. 123
      application/src/test/java/org/thingsboard/server/service/apiusage/DefaultTbApiUsageStateServiceTest.java
  6. 1
      common/dao-api/src/main/java/org/thingsboard/server/dao/usagerecord/ApiUsageStateService.java
  7. 9
      common/data/src/main/java/org/thingsboard/server/common/data/ApiUsageState.java
  8. 5
      common/proto/src/main/java/org/thingsboard/server/common/util/ProtoUtils.java
  9. 1
      common/proto/src/main/proto/queue.proto
  10. 10
      dao/src/main/java/org/thingsboard/server/dao/model/sql/ApiUsageStateEntity.java
  11. 1
      dao/src/main/java/org/thingsboard/server/dao/usagerecord/ApiUsageStateServiceImpl.java
  12. 1
      dao/src/main/resources/sql/schema-entities.sql
  13. 47
      dao/src/test/java/org/thingsboard/server/dao/service/ApiUsageStateServiceTest.java

2
application/src/main/data/upgrade/basic/schema_update.sql

@ -61,3 +61,5 @@ DO $$
$$; $$;
-- UPDATE SAVE TIME SERIES NODES END -- UPDATE SAVE TIME SERIES NODES END
ALTER TABLE api_usage_state ADD COLUMN IF NOT EXISTS version BIGINT DEFAULT 1;

3
application/src/main/java/org/thingsboard/server/service/apiusage/BaseApiUsageState.java

@ -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

3
application/src/main/java/org/thingsboard/server/service/apiusage/DefaultTbApiUsageStateService.java

@ -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()))));

144
application/src/test/java/org/thingsboard/server/service/apiusage/ApiUsageTest.java

@ -0,0 +1,144 @@
/**
* Copyright © 2016-2025 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.service.apiusage;
import org.junit.Before;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.TestPropertySource;
import org.thingsboard.server.common.data.ApiUsageStateValue;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.SaveDeviceWithCredentialsRequest;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.TenantProfile;
import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.security.Authority;
import org.thingsboard.server.common.data.security.DeviceCredentials;
import org.thingsboard.server.common.data.security.DeviceCredentialsType;
import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration;
import org.thingsboard.server.common.data.tenant.profile.TenantProfileData;
import org.thingsboard.server.controller.AbstractControllerTest;
import org.thingsboard.server.controller.TbUrlConstants;
import org.thingsboard.server.dao.service.DaoSqlTest;
import org.thingsboard.server.dao.usagerecord.ApiUsageStateService;
import java.util.concurrent.TimeUnit;
import static org.awaitility.Awaitility.await;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@DaoSqlTest
@TestPropertySource(properties = {
"usage.stats.report.enabled=true",
"usage.stats.report.interval=2",
"usage.stats.gauge_report_interval=1",
})
public class ApiUsageTest extends AbstractControllerTest {
private Tenant savedTenant;
private User tenantAdmin;
private static final int MAX_DP_ENABLE_VALUE = 12;
private static final double WARN_THRESHOLD_VALUE = 0.5;
@Autowired
private ApiUsageStateService apiUsageStateService;
@Before
public void beforeTest() throws Exception {
loginSysAdmin();
TenantProfile tenantProfile = createTenantProfile();
TenantProfile savedTenantProfile = doPost("/api/tenantProfile", tenantProfile, TenantProfile.class);
assertNotNull(savedTenantProfile);
Tenant tenant = new Tenant();
tenant.setTitle("My tenant");
tenant.setTenantProfileId(savedTenantProfile.getId());
savedTenant = saveTenant(tenant);
tenantId = savedTenant.getId();
assertNotNull(savedTenant);
tenantAdmin = new User();
tenantAdmin.setAuthority(Authority.TENANT_ADMIN);
tenantAdmin.setTenantId(savedTenant.getId());
tenantAdmin.setEmail("tenant2@thingsboard.org");
tenantAdmin = createUserAndLogin(tenantAdmin, "testPassword1");
}
@Test
public void testTelemetryApiCall() throws Exception {
Device device = createDevice();
assertNotNull(device);
String telemetryPayload = "{\"temperature\":25, \"humidity\":60}";
String url = TbUrlConstants.TELEMETRY_URL_PREFIX + "/DEVICE/" + device.getId() + "/timeseries/ANY";
long VALUE_WARNING = (long) (MAX_DP_ENABLE_VALUE * WARN_THRESHOLD_VALUE) / 2;
for (int i = 0; i < VALUE_WARNING; i++) {
doPostAsync(url, telemetryPayload, String.class, status().isOk());
}
await().atMost(TIMEOUT, TimeUnit.SECONDS).untilAsserted(() -> assertEquals(ApiUsageStateValue.WARNING, apiUsageStateService.findTenantApiUsageState(tenantId).getDbStorageState()));
long VALUE_DISABLE = (long) (MAX_DP_ENABLE_VALUE - (MAX_DP_ENABLE_VALUE * WARN_THRESHOLD_VALUE)) / 2;
for (int i = 0; i < VALUE_DISABLE; i++) {
doPostAsync(url, telemetryPayload, String.class, status().isOk());
}
await().atMost(TIMEOUT, TimeUnit.SECONDS)
.untilAsserted(() -> {
assertEquals(ApiUsageStateValue.DISABLED, apiUsageStateService.findTenantApiUsageState(tenantId).getDbStorageState());
});
}
private TenantProfile createTenantProfile() {
TenantProfile tenantProfile = new TenantProfile();
tenantProfile.setName("Tenant Profile");
tenantProfile.setDescription("Tenant Profile" + " Test");
TenantProfileData tenantProfileData = new TenantProfileData();
DefaultTenantProfileConfiguration config = DefaultTenantProfileConfiguration.builder()
.maxDPStorageDays(MAX_DP_ENABLE_VALUE)
.warnThreshold(WARN_THRESHOLD_VALUE)
.build();
tenantProfileData.setConfiguration(config);
tenantProfile.setProfileData(tenantProfileData);
return tenantProfile;
}
private Device createDevice() throws Exception {
String testToken = "TEST_TOKEN";
Device device = new Device();
device.setName("My device");
device.setType("default");
device.setTenantId(tenantId);
DeviceCredentials deviceCredentials = new DeviceCredentials();
deviceCredentials.setCredentialsType(DeviceCredentialsType.ACCESS_TOKEN);
deviceCredentials.setCredentialsId(testToken);
SaveDeviceWithCredentialsRequest saveRequest = new SaveDeviceWithCredentialsRequest(device, deviceCredentials);
return readResponse(doPost("/api/device-with-credentials", saveRequest).andExpect(status().isOk()), Device.class);
}
}

123
application/src/test/java/org/thingsboard/server/service/apiusage/DefaultTbApiUsageStateServiceTest.java

@ -15,45 +15,114 @@
*/ */
package org.thingsboard.server.service.apiusage; package org.thingsboard.server.service.apiusage;
import org.junit.jupiter.api.BeforeEach; import org.junit.Assert;
import org.junit.jupiter.api.Test; import org.junit.Before;
import org.junit.jupiter.api.extension.ExtendWith; import org.junit.Test;
import org.mockito.InjectMocks; import org.springframework.beans.factory.annotation.Autowired;
import org.mockito.Mock; import org.thingsboard.server.common.data.ApiUsageRecordKey;
import org.mockito.Mockito; import org.thingsboard.server.common.data.ApiUsageStateValue;
import org.mockito.Spy; import org.thingsboard.server.common.data.Tenant;
import org.mockito.junit.jupiter.MockitoExtension; import org.thingsboard.server.common.data.TenantProfile;
import org.thingsboard.server.common.data.ApiUsageState;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration;
import org.thingsboard.server.common.data.tenant.profile.TenantProfileData;
import org.thingsboard.server.common.msg.queue.TbCallback;
import org.thingsboard.server.controller.AbstractControllerTest;
import org.thingsboard.server.dao.service.DaoSqlTest;
import org.thingsboard.server.dao.usagerecord.ApiUsageStateService;
import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.queue.common.TbProtoQueueMsg;
import java.util.UUID; import java.util.UUID;
import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.assertEquals;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.mockito.Mockito.never;
@ExtendWith(MockitoExtension.class) @DaoSqlTest
public class DefaultTbApiUsageStateServiceTest { public class DefaultTbApiUsageStateServiceTest extends AbstractControllerTest {
@Mock @Autowired
TenantApiUsageState tenantUsageStateMock; DefaultTbApiUsageStateService service;
TenantId tenantId = TenantId.fromUUID(UUID.fromString("00797a3b-7aeb-4b5b-b57a-c2a810d0f112")); @Autowired
private ApiUsageStateService apiUsageStateService;
@Spy private TenantId tenantId;
@InjectMocks private Tenant savedTenant;
DefaultTbApiUsageStateService service;
private static final int MAX_ENABLE_VALUE = 5000;
private static final long VALUE_WARNING = 4500L;
private static final long VALUE_DISABLE = 5500L;
private static final double WARN_THRESHOLD_VALUE = 0.8;
@Before
public void init() throws Exception {
loginSysAdmin();
@BeforeEach TenantProfile tenantProfile = createTenantProfile();
public void setUp() { TenantProfile savedTenantProfile = doPost("/api/tenantProfile", tenantProfile, TenantProfile.class);
Assert.assertNotNull(savedTenantProfile);
Tenant tenant = new Tenant();
tenant.setTitle("My tenant");
tenant.setTenantProfileId(savedTenantProfile.getId());
savedTenant = saveTenant(tenant);
tenantId = savedTenant.getId();
Assert.assertNotNull(savedTenant);
} }
@Test @Test
public void givenTenantIdFromEntityStatesMap_whenGetApiUsageState() { public void testProcess_transitionFromWarningToDisabled() {
service.myUsageStates.put(tenantId, tenantUsageStateMock); TransportProtos.ToUsageStatsServiceMsg.Builder warningMsgBuilder = TransportProtos.ToUsageStatsServiceMsg.newBuilder()
ApiUsageState tenantUsageState = service.getApiUsageState(tenantId); .setTenantIdMSB(tenantId.getId().getMostSignificantBits())
assertThat(tenantUsageState, is(tenantUsageStateMock.getApiUsageState())); .setTenantIdLSB(tenantId.getId().getLeastSignificantBits())
Mockito.verify(service, never()).getOrFetchState(tenantId, tenantId); .setCustomerIdMSB(0)
.setCustomerIdLSB(0)
.setServiceId("testService");
warningMsgBuilder.addValues(TransportProtos.UsageStatsKVProto.newBuilder()
.setKey(ApiUsageRecordKey.STORAGE_DP_COUNT.name())
.setValue(VALUE_WARNING)
.build());
TransportProtos.ToUsageStatsServiceMsg warningStatsMsg = warningMsgBuilder.build();
TbProtoQueueMsg<TransportProtos.ToUsageStatsServiceMsg> warningMsg = new TbProtoQueueMsg<>(UUID.randomUUID(), warningStatsMsg);
service.process(warningMsg, TbCallback.EMPTY);
assertEquals(ApiUsageStateValue.WARNING, apiUsageStateService.findTenantApiUsageState(tenantId).getDbStorageState());
TransportProtos.ToUsageStatsServiceMsg.Builder disableMsgBuilder = TransportProtos.ToUsageStatsServiceMsg.newBuilder()
.setTenantIdMSB(tenantId.getId().getMostSignificantBits())
.setTenantIdLSB(tenantId.getId().getLeastSignificantBits())
.setCustomerIdMSB(0)
.setCustomerIdLSB(0)
.setServiceId("testService");
disableMsgBuilder.addValues(TransportProtos.UsageStatsKVProto.newBuilder()
.setKey(ApiUsageRecordKey.STORAGE_DP_COUNT.name())
.setValue(VALUE_DISABLE)
.build());
TransportProtos.ToUsageStatsServiceMsg disableStatsMsg = disableMsgBuilder.build();
TbProtoQueueMsg<TransportProtos.ToUsageStatsServiceMsg> disableMsg = new TbProtoQueueMsg<>(UUID.randomUUID(), disableStatsMsg);
service.process(disableMsg, TbCallback.EMPTY);
assertEquals(ApiUsageStateValue.DISABLED, apiUsageStateService.findTenantApiUsageState(tenantId).getDbStorageState());
}
private TenantProfile createTenantProfile() {
TenantProfile tenantProfile = new TenantProfile();
tenantProfile.setName("Tenant Profile");
tenantProfile.setDescription("Tenant Profile" + " Test");
TenantProfileData tenantProfileData = new TenantProfileData();
DefaultTenantProfileConfiguration config = DefaultTenantProfileConfiguration.builder()
.maxDPStorageDays(MAX_ENABLE_VALUE)
.warnThreshold(WARN_THRESHOLD_VALUE)
.build();
tenantProfileData.setConfiguration(config);
tenantProfile.setProfileData(tenantProfileData);
return tenantProfile;
} }
} }

1
common/dao-api/src/main/java/org/thingsboard/server/dao/usagerecord/ApiUsageStateService.java

@ -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);
} }

9
common/data/src/main/java/org/thingsboard/server/common/data/ApiUsageState.java

@ -27,7 +27,7 @@ import org.thingsboard.server.common.data.id.TenantId;
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
@Getter @Getter
@Setter @Setter
public class ApiUsageState extends BaseData<ApiUsageStateId> implements HasTenantId { public class ApiUsageState extends BaseData<ApiUsageStateId> implements HasTenantId, HasVersion {
private static final long serialVersionUID = 8250339805336035966L; private static final long serialVersionUID = 8250339805336035966L;
@ -41,6 +41,7 @@ public class ApiUsageState extends BaseData<ApiUsageStateId> implements HasTenan
private ApiUsageStateValue emailExecState; private ApiUsageStateValue emailExecState;
private ApiUsageStateValue smsExecState; private ApiUsageStateValue smsExecState;
private ApiUsageStateValue alarmExecState; private ApiUsageStateValue alarmExecState;
private Long version;
public ApiUsageState() { public ApiUsageState() {
super(); super();
@ -62,6 +63,7 @@ public class ApiUsageState extends BaseData<ApiUsageStateId> implements HasTenan
this.emailExecState = ur.getEmailExecState(); this.emailExecState = ur.getEmailExecState();
this.smsExecState = ur.getSmsExecState(); this.smsExecState = ur.getSmsExecState();
this.alarmExecState = ur.getAlarmExecState(); this.alarmExecState = ur.getAlarmExecState();
this.version = ur.getVersion();
} }
public boolean isTransportEnabled() { public boolean isTransportEnabled() {
@ -84,15 +86,16 @@ 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);
} }
public boolean isAlarmCreationEnabled() { public boolean isAlarmCreationEnabled() {
return alarmExecState != ApiUsageStateValue.DISABLED; return alarmExecState != ApiUsageStateValue.DISABLED;
} }
} }

5
common/proto/src/main/java/org/thingsboard/server/common/util/ProtoUtils.java

@ -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;
} }

1
common/proto/src/main/proto/queue.proto

@ -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 {

10
dao/src/main/java/org/thingsboard/server/dao/model/sql/ApiUsageStateEntity.java

@ -28,7 +28,7 @@ import org.thingsboard.server.common.data.id.ApiUsageStateId;
import org.thingsboard.server.common.data.id.EntityIdFactory; import org.thingsboard.server.common.data.id.EntityIdFactory;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.dao.model.BaseEntity; import org.thingsboard.server.dao.model.BaseEntity;
import org.thingsboard.server.dao.model.BaseSqlEntity; import org.thingsboard.server.dao.model.BaseVersionedEntity;
import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.dao.model.ModelConstants;
import java.util.UUID; import java.util.UUID;
@ -40,7 +40,7 @@ import java.util.UUID;
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
@Entity @Entity
@Table(name = ModelConstants.API_USAGE_STATE_TABLE_NAME) @Table(name = ModelConstants.API_USAGE_STATE_TABLE_NAME)
public class ApiUsageStateEntity extends BaseSqlEntity<ApiUsageState> implements BaseEntity<ApiUsageState> { public class ApiUsageStateEntity extends BaseVersionedEntity<ApiUsageState> implements BaseEntity<ApiUsageState> {
@Column(name = ModelConstants.API_USAGE_STATE_TENANT_ID_COLUMN) @Column(name = ModelConstants.API_USAGE_STATE_TENANT_ID_COLUMN)
private UUID tenantId; private UUID tenantId;
@ -77,10 +77,7 @@ public class ApiUsageStateEntity extends BaseSqlEntity<ApiUsageState> implements
} }
public ApiUsageStateEntity(ApiUsageState ur) { public ApiUsageStateEntity(ApiUsageState ur) {
if (ur.getId() != null) { super(ur);
this.setUuid(ur.getId().getId());
}
this.setCreatedTime(ur.getCreatedTime());
if (ur.getTenantId() != null) { if (ur.getTenantId() != null) {
this.tenantId = ur.getTenantId().getId(); this.tenantId = ur.getTenantId().getId();
} }
@ -116,6 +113,7 @@ public class ApiUsageStateEntity extends BaseSqlEntity<ApiUsageState> implements
ur.setEmailExecState(emailExecState); ur.setEmailExecState(emailExecState);
ur.setSmsExecState(smsExecState); ur.setSmsExecState(smsExecState);
ur.setAlarmExecState(alarmExecState); ur.setAlarmExecState(alarmExecState);
ur.setVersion(version);
return ur; return ur;
} }

1
dao/src/main/java/org/thingsboard/server/dao/usagerecord/ApiUsageStateServiceImpl.java

@ -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());

1
dao/src/main/resources/sql/schema-entities.sql

@ -702,6 +702,7 @@ CREATE TABLE IF NOT EXISTS api_usage_state (
email_exec varchar(32), email_exec varchar(32),
sms_exec varchar(32), sms_exec varchar(32),
alarm_exec varchar(32), alarm_exec varchar(32),
version BIGINT DEFAULT 1,
CONSTRAINT api_usage_state_unq_key UNIQUE (tenant_id, entity_id) CONSTRAINT api_usage_state_unq_key UNIQUE (tenant_id, entity_id)
); );

47
dao/src/test/java/org/thingsboard/server/dao/service/ApiUsageStateServiceTest.java

@ -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…
Cancel
Save