diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/usagerecord/UsageRecordService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/usagerecord/UsageRecordService.java new file mode 100644 index 0000000000..55adc257ad --- /dev/null +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/usagerecord/UsageRecordService.java @@ -0,0 +1,28 @@ +/** + * Copyright © 2016-2020 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.dao.usagerecord; + +import org.thingsboard.server.common.data.UsageRecord; +import org.thingsboard.server.common.data.id.TenantId; + +public interface UsageRecordService { + + UsageRecord findTenantUsageRecord(TenantId tenantId); + + void deleteUsageRecordsByTenantId(TenantId tenantId); + + void createDefaultUsageRecord(TenantId id); +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/EntityType.java b/common/data/src/main/java/org/thingsboard/server/common/data/EntityType.java index cfe12a14cb..11bf45be76 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/EntityType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/EntityType.java @@ -19,5 +19,5 @@ package org.thingsboard.server.common.data; * @author Andrew Shvayka */ public enum EntityType { - TENANT, CUSTOMER, USER, DASHBOARD, ASSET, DEVICE, ALARM, RULE_CHAIN, RULE_NODE, ENTITY_VIEW, WIDGETS_BUNDLE, WIDGET_TYPE, TENANT_PROFILE, DEVICE_PROFILE + TENANT, CUSTOMER, USER, DASHBOARD, ASSET, DEVICE, ALARM, RULE_CHAIN, RULE_NODE, ENTITY_VIEW, WIDGETS_BUNDLE, WIDGET_TYPE, TENANT_PROFILE, DEVICE_PROFILE, USAGE_RECORD; } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/UsageRecord.java b/common/data/src/main/java/org/thingsboard/server/common/data/UsageRecord.java new file mode 100644 index 0000000000..e96cf2e452 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/UsageRecord.java @@ -0,0 +1,65 @@ +/** + * Copyright © 2016-2020 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.data; + +import lombok.EqualsAndHashCode; +import lombok.ToString; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.id.UsageRecordId; +import org.thingsboard.server.common.data.id.UserId; + +@ToString +@EqualsAndHashCode(callSuper = true) +public class UsageRecord extends BaseData implements HasTenantId { + + private static final long serialVersionUID = 8250339805336035966L; + + private TenantId tenantId; + private EntityId entityId; + + public UsageRecord() { + super(); + } + + public UsageRecord(UsageRecordId id) { + super(id); + } + + public UsageRecord(UsageRecord ur) { + super(ur); + this.tenantId = ur.getTenantId(); + this.entityId = ur.getEntityId(); + } + + @Override + public TenantId getTenantId() { + return tenantId; + } + + public void setTenantId(TenantId tenantId) { + this.tenantId = tenantId; + } + + public EntityId getEntityId() { + return entityId; + } + + public void setEntityId(EntityId entityId) { + this.entityId = entityId; + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/UsageRecordKey.java b/common/data/src/main/java/org/thingsboard/server/common/data/UsageRecordKey.java new file mode 100644 index 0000000000..74a54cf713 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/UsageRecordKey.java @@ -0,0 +1,27 @@ +/** + * Copyright © 2016-2020 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.data; + +public enum UsageRecordKey { + + MSG_COUNT, + MSG_BYTES_COUNT, + DP_TRANSPORT_COUNT, + DP_STORAGE_COUNT, + RE_EXEC_COUNT, + JS_EXEC_COUNT + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/id/UsageRecordId.java b/common/data/src/main/java/org/thingsboard/server/common/data/id/UsageRecordId.java new file mode 100644 index 0000000000..76cfaaedf6 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/id/UsageRecordId.java @@ -0,0 +1,42 @@ +/** + * Copyright © 2016-2020 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.data.id; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; +import org.thingsboard.server.common.data.EntityType; + +import java.util.UUID; + +public class UsageRecordId extends UUIDBased implements EntityId { + + @JsonCreator + public UsageRecordId(@JsonProperty("id") UUID id) { + super(id); + } + + public static UsageRecordId fromString(String userId) { + return new UsageRecordId(UUID.fromString(userId)); + } + + @JsonIgnore + @Override + public EntityType getEntityType() { + return EntityType.USAGE_RECORD; + } + +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java b/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java index 14ab9ed9e0..3ee942b0be 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java @@ -438,6 +438,14 @@ public class ModelConstants { public static final String OAUTH2_TEMPLATE_LOGIN_BUTTON_LABEL_PROPERTY = OAUTH2_LOGIN_BUTTON_LABEL_PROPERTY; public static final String OAUTH2_TEMPLATE_HELP_LINK_PROPERTY = "help_link"; + /** + * Usage Record constants. + */ + public static final String UR_TABLE_NAME = "usage_record"; + public static final String UR_TENANT_ID_COLUMN = TENANT_ID_PROPERTY; + public static final String UR_ENTITY_TYPE_COLUMN = ENTITY_TYPE_COLUMN; + public static final String UR_ENTITY_ID_COLUMN = ENTITY_ID_COLUMN; + /** * Cassandra attributes and timeseries constants. */ diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/UsageRecordEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/UsageRecordEntity.java new file mode 100644 index 0000000000..be5cc40621 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/UsageRecordEntity.java @@ -0,0 +1,93 @@ +/** + * Copyright © 2016-2020 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.dao.model.sql; + +import com.fasterxml.jackson.databind.JsonNode; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.hibernate.annotations.Type; +import org.hibernate.annotations.TypeDef; +import org.thingsboard.server.common.data.UsageRecord; +import org.thingsboard.server.common.data.User; +import org.thingsboard.server.common.data.id.CustomerId; +import org.thingsboard.server.common.data.id.EntityIdFactory; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.id.UsageRecordId; +import org.thingsboard.server.common.data.id.UserId; +import org.thingsboard.server.common.data.security.Authority; +import org.thingsboard.server.dao.model.BaseEntity; +import org.thingsboard.server.dao.model.BaseSqlEntity; +import org.thingsboard.server.dao.model.ModelConstants; +import org.thingsboard.server.dao.model.SearchTextEntity; +import org.thingsboard.server.dao.util.mapping.JsonStringType; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.EnumType; +import javax.persistence.Enumerated; +import javax.persistence.Table; +import java.util.UUID; + +/** + * Created by Valerii Sosliuk on 4/21/2017. + */ +@Data +@EqualsAndHashCode(callSuper = true) +@Entity +@TypeDef(name = "json", typeClass = JsonStringType.class) +@Table(name = ModelConstants.UR_TABLE_NAME) +public class UsageRecordEntity extends BaseSqlEntity implements BaseEntity { + + @Column(name = ModelConstants.UR_TENANT_ID_COLUMN) + private UUID tenantId; + + @Column(name = ModelConstants.UR_ENTITY_TYPE_COLUMN) + private String entityType; + + @Column(name = ModelConstants.UR_ENTITY_ID_COLUMN) + private UUID entityId; + + public UsageRecordEntity() { + } + + public UsageRecordEntity(UsageRecord ur) { + if (ur.getId() != null) { + this.setUuid(ur.getId().getId()); + } + this.setCreatedTime(ur.getCreatedTime()); + if (ur.getTenantId() != null) { + this.tenantId = ur.getTenantId().getId(); + } + if (ur.getEntityId() != null) { + this.entityType = ur.getEntityId().getEntityType().name(); + this.entityId = ur.getEntityId().getId(); + } + } + + @Override + public UsageRecord toData() { + UsageRecord ur = new UsageRecord(new UsageRecordId(this.getUuid())); + ur.setCreatedTime(createdTime); + if (tenantId != null) { + ur.setTenantId(new TenantId(tenantId)); + } + if (entityId != null) { + ur.setEntityId(EntityIdFactory.getByTypeAndUuid(entityType, entityId)); + } + return ur; + } + +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/usagerecord/JpaUsageRecordDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/usagerecord/JpaUsageRecordDao.java new file mode 100644 index 0000000000..8d54e6b53b --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/usagerecord/JpaUsageRecordDao.java @@ -0,0 +1,60 @@ +/** + * Copyright © 2016-2020 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.dao.sql.usagerecord; + +import org.springframework.data.repository.CrudRepository; +import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.UsageRecord; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.dao.DaoUtil; +import org.thingsboard.server.dao.model.sql.UsageRecordEntity; +import org.thingsboard.server.dao.sql.JpaAbstractDao; +import org.thingsboard.server.dao.usagerecord.UsageRecordDao; + +import java.util.UUID; + +/** + * @author Andrii Shvaika + */ +@Component +public class JpaUsageRecordDao extends JpaAbstractDao implements UsageRecordDao { + + private final UsageRecordRepository usageRecordRepository; + + public JpaUsageRecordDao(UsageRecordRepository usageRecordRepository) { + this.usageRecordRepository = usageRecordRepository; + } + + @Override + protected Class getEntityClass() { + return UsageRecordEntity.class; + } + + @Override + protected CrudRepository getCrudRepository() { + return usageRecordRepository; + } + + @Override + public UsageRecord findTenantUsageRecord(UUID tenantId) { + return DaoUtil.getData(usageRecordRepository.findByTenantId(tenantId)); + } + + @Override + public void deleteUsageRecordsByTenantId(TenantId tenantId) { + usageRecordRepository.deleteUsageRecordsByTenantId(tenantId.getId()); + } +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/usagerecord/UsageRecordRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/usagerecord/UsageRecordRepository.java new file mode 100644 index 0000000000..dc2b0d9ad7 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/usagerecord/UsageRecordRepository.java @@ -0,0 +1,43 @@ +/** + * Copyright © 2016-2020 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.dao.sql.usagerecord; + +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.CrudRepository; +import org.springframework.data.repository.query.Param; +import org.springframework.transaction.annotation.Transactional; +import org.thingsboard.server.common.data.UsageRecord; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.dao.model.sql.UsageRecordEntity; +import org.thingsboard.server.dao.model.sql.UserEntity; + +import java.util.UUID; + +/** + * @author Valerii Sosliuk + */ +public interface UsageRecordRepository extends CrudRepository { + + @Query("SELECT ur FROM UsageRecordEntity ur WHERE ur.tenantId = :tenantId " + + "AND ur.entityId = :tenantId AND ur.entityType = 'TENANT' ") + UsageRecordEntity findByTenantId(@Param("tenantId") UUID tenantId); + + @Transactional + @Modifying + @Query("DELETE FROM UsageRecordEntity ur WHERE ur.tenantId = :tenantId") + void deleteUsageRecordsByTenantId(@Param("tenantId") UUID tenantId); +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java index 52761184fd..bd9ee321e0 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java @@ -39,6 +39,7 @@ import org.thingsboard.server.dao.rule.RuleChainService; import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.service.PaginatedRemover; import org.thingsboard.server.dao.service.Validator; +import org.thingsboard.server.dao.usagerecord.UsageRecordService; import org.thingsboard.server.dao.user.UserService; import org.thingsboard.server.dao.widget.WidgetsBundleService; @@ -72,6 +73,9 @@ public class TenantServiceImpl extends AbstractEntityService implements TenantSe @Autowired private DeviceProfileService deviceProfileService; + @Autowired + private UsageRecordService usageRecordService; + @Autowired private EntityViewService entityViewService; @@ -117,6 +121,7 @@ public class TenantServiceImpl extends AbstractEntityService implements TenantSe Tenant savedTenant = tenantDao.save(tenant.getId(), tenant); if (tenant.getId() == null) { deviceProfileService.createDefaultDeviceProfile(savedTenant.getId()); + usageRecordService.createDefaultUsageRecord(savedTenant.getId()); } return savedTenant; } @@ -134,6 +139,7 @@ public class TenantServiceImpl extends AbstractEntityService implements TenantSe deviceProfileService.deleteDeviceProfilesByTenantId(tenantId); userService.deleteTenantAdmins(tenantId); ruleChainService.deleteRuleChainsByTenantId(tenantId); + usageRecordService.deleteUsageRecordsByTenantId(tenantId); tenantDao.removeById(tenantId, tenantId.getId()); deleteEntityRelations(tenantId, tenantId); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/usagerecord/UsageRecordDao.java b/dao/src/main/java/org/thingsboard/server/dao/usagerecord/UsageRecordDao.java new file mode 100644 index 0000000000..3b71e4ce20 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/usagerecord/UsageRecordDao.java @@ -0,0 +1,51 @@ +/** + * Copyright © 2016-2020 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.dao.usagerecord; + +import org.thingsboard.server.common.data.UsageRecord; +import org.thingsboard.server.common.data.User; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.page.PageLink; +import org.thingsboard.server.dao.Dao; + +import java.util.UUID; + +public interface UsageRecordDao extends Dao { + + /** + * Save or update usage record object + * + * @param usageRecord the usage record + * @return saved usage record entity + */ + UsageRecord save(TenantId tenantId, UsageRecord usageRecord); + + /** + * Find usage record by tenantId. + * + * @param tenantId the tenantId + * @return the corresponding usage record + */ + UsageRecord findTenantUsageRecord(UUID tenantId); + + /** + * Delete usage record by tenantId. + * + * @param tenantId the tenantId + */ + void deleteUsageRecordsByTenantId(TenantId tenantId); +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/usagerecord/UsageRecordServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/usagerecord/UsageRecordServiceImpl.java new file mode 100644 index 0000000000..fdaa38a1a2 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/usagerecord/UsageRecordServiceImpl.java @@ -0,0 +1,112 @@ +/** + * Copyright © 2016-2020 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.dao.usagerecord; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.google.common.util.concurrent.ListenableFuture; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.RandomStringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.util.StringUtils; +import org.thingsboard.server.common.data.Customer; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.Tenant; +import org.thingsboard.server.common.data.UsageRecord; +import org.thingsboard.server.common.data.User; +import org.thingsboard.server.common.data.id.CustomerId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.id.UserCredentialsId; +import org.thingsboard.server.common.data.id.UserId; +import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.page.PageLink; +import org.thingsboard.server.common.data.security.UserCredentials; +import org.thingsboard.server.dao.entity.AbstractEntityService; +import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.dao.exception.IncorrectParameterException; +import org.thingsboard.server.dao.model.ModelConstants; +import org.thingsboard.server.dao.service.DataValidator; +import org.thingsboard.server.dao.tenant.TenantDao; + +import java.util.HashMap; +import java.util.Map; + +import static org.thingsboard.server.dao.service.Validator.validateId; +import static org.thingsboard.server.dao.service.Validator.validatePageLink; +import static org.thingsboard.server.dao.service.Validator.validateString; + +@Service +@Slf4j +public class UsageRecordServiceImpl extends AbstractEntityService implements UsageRecordService { + public static final String INCORRECT_TENANT_ID = "Incorrect tenantId "; + + private final UsageRecordDao usageRecordDao; + private final TenantDao tenantDao; + + public UsageRecordServiceImpl(TenantDao tenantDao, UsageRecordDao usageRecordDao) { + this.tenantDao = tenantDao; + this.usageRecordDao = usageRecordDao; + } + + @Override + public void deleteUsageRecordsByTenantId(TenantId tenantId) { + log.trace("Executing deleteUsageRecordsByTenantId [{}]", tenantId); + validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + usageRecordDao.deleteUsageRecordsByTenantId(tenantId); + } + + @Override + public void createDefaultUsageRecord(TenantId tenantId) { + log.trace("Executing createDefaultUsageRecord [{}]", tenantId); + validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + UsageRecord usageRecord = new UsageRecord(); + usageRecord.setTenantId(tenantId); + usageRecord.setEntityId(tenantId); + usageRecordValidator.validate(usageRecord, UsageRecord::getTenantId); + usageRecordDao.save(usageRecord.getTenantId(), usageRecord); + } + + @Override + public UsageRecord findTenantUsageRecord(TenantId tenantId) { + log.trace("Executing findTenantUsageRecord, tenantId [{}]", tenantId); + validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + return usageRecordDao.findTenantUsageRecord(tenantId.getId()); + } + + private DataValidator usageRecordValidator = + new DataValidator() { + @Override + protected void validateDataImpl(TenantId requestTenantId, UsageRecord usageRecord) { + if (usageRecord.getTenantId() == null) { + throw new DataValidationException("UsageRecord should be assigned to tenant!"); + } else { + Tenant tenant = tenantDao.findById(requestTenantId, usageRecord.getTenantId().getId()); + if (tenant == null) { + throw new DataValidationException("Asset is referencing to non-existent tenant!"); + } + } + if (usageRecord.getEntityId() == null) { + throw new DataValidationException("UsageRecord should be assigned to entity!"); + } else if (!EntityType.TENANT.equals(usageRecord.getEntityId().getEntityType())) { + throw new DataValidationException("Only Tenant Usage Records are supported!"); + } else if (!usageRecord.getTenantId().getId().equals(usageRecord.getEntityId().getId())) { + throw new DataValidationException("Can't assign one Usage Record to multiple tenants!"); + } + } + }; + +} diff --git a/dao/src/main/resources/sql/schema-entities-hsql.sql b/dao/src/main/resources/sql/schema-entities-hsql.sql index 41d9e3ac81..e3cbbda351 100644 --- a/dao/src/main/resources/sql/schema-entities-hsql.sql +++ b/dao/src/main/resources/sql/schema-entities-hsql.sql @@ -404,3 +404,12 @@ CREATE TABLE IF NOT EXISTS oauth2_client_registration_template ( help_link varchar(255), CONSTRAINT oauth2_template_provider_id_unq_key UNIQUE (provider_id) ); + +CREATE TABLE IF NOT EXISTS usage_record ( + id uuid NOT NULL CONSTRAINT usage_record_pkey PRIMARY KEY, + created_time bigint NOT NULL, + tenant_id uuid, + entity_type varchar(32), + entity_id uuid, + CONSTRAINT usage_record_unq_key UNIQUE (tenant_id, entity_id) +); diff --git a/dao/src/main/resources/sql/schema-entities.sql b/dao/src/main/resources/sql/schema-entities.sql index e1ffba98eb..6cd3adb337 100644 --- a/dao/src/main/resources/sql/schema-entities.sql +++ b/dao/src/main/resources/sql/schema-entities.sql @@ -431,6 +431,15 @@ CREATE TABLE IF NOT EXISTS oauth2_client_registration_template ( CONSTRAINT oauth2_template_provider_id_unq_key UNIQUE (provider_id) ); +CREATE TABLE IF NOT EXISTS usage_record ( + id uuid NOT NULL CONSTRAINT usage_record_pkey PRIMARY KEY, + created_time bigint NOT NULL, + tenant_id uuid, + entity_type varchar(32), + entity_id uuid, + CONSTRAINT usage_record_unq_key UNIQUE (tenant_id, entity_id) +); + CREATE OR REPLACE PROCEDURE cleanup_events_by_ttl(IN ttl bigint, IN debug_ttl bigint, INOUT deleted bigint) LANGUAGE plpgsql AS $$ diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/AbstractServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/AbstractServiceTest.java index cf6f574180..8da1727631 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/AbstractServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/AbstractServiceTest.java @@ -61,6 +61,7 @@ import org.thingsboard.server.dao.settings.AdminSettingsService; import org.thingsboard.server.dao.tenant.TenantProfileService; import org.thingsboard.server.dao.tenant.TenantService; import org.thingsboard.server.dao.timeseries.TimeseriesService; +import org.thingsboard.server.dao.usagerecord.UsageRecordService; import org.thingsboard.server.dao.user.UserService; import org.thingsboard.server.dao.widget.WidgetTypeService; import org.thingsboard.server.dao.widget.WidgetsBundleService; @@ -85,6 +86,9 @@ public abstract class AbstractServiceTest { @Autowired protected UserService userService; + @Autowired + protected UsageRecordService usageRecordService; + @Autowired protected AdminSettingsService adminSettingsService; diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/BaseUsageRecordServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/BaseUsageRecordServiceTest.java new file mode 100644 index 0000000000..28e0a6d54a --- /dev/null +++ b/dao/src/test/java/org/thingsboard/server/dao/service/BaseUsageRecordServiceTest.java @@ -0,0 +1,56 @@ +/** + * Copyright © 2016-2020 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.dao.service; + +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.thingsboard.server.common.data.Customer; +import org.thingsboard.server.common.data.Tenant; +import org.thingsboard.server.common.data.UsageRecord; +import org.thingsboard.server.common.data.User; +import org.thingsboard.server.common.data.id.CustomerId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.dao.service.AbstractServiceTest; +import org.thingsboard.server.dao.service.DaoSqlTest; + + +public abstract class BaseUsageRecordServiceTest extends AbstractServiceTest { + + private TenantId tenantId; + + @Before + public void before() { + Tenant tenant = new Tenant(); + tenant.setTitle("My tenant"); + Tenant savedTenant = tenantService.saveTenant(tenant); + Assert.assertNotNull(savedTenant); + tenantId = savedTenant.getId(); + } + + @After + public void after() { + tenantService.deleteTenant(tenantId); + } + + @Test + public void testFindUsageRecordByTenantId() { + UsageRecord usageRecord = usageRecordService.findTenantUsageRecord(tenantId); + Assert.assertNotNull(usageRecord); + } + +} diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/sql/UsageRecordServiceSqlTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/sql/UsageRecordServiceSqlTest.java new file mode 100644 index 0000000000..8275b92044 --- /dev/null +++ b/dao/src/test/java/org/thingsboard/server/dao/service/sql/UsageRecordServiceSqlTest.java @@ -0,0 +1,24 @@ +/** + * Copyright © 2016-2020 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.dao.service.sql; + +import org.thingsboard.server.dao.service.BaseUsageRecordServiceTest; +import org.thingsboard.server.dao.service.BaseUserServiceTest; +import org.thingsboard.server.dao.service.DaoSqlTest; + +@DaoSqlTest +public class UsageRecordServiceSqlTest extends BaseUsageRecordServiceTest { +} diff --git a/dao/src/test/resources/sql/hsql/drop-all-tables.sql b/dao/src/test/resources/sql/hsql/drop-all-tables.sql index 160049a144..13c1eba943 100644 --- a/dao/src/test/resources/sql/hsql/drop-all-tables.sql +++ b/dao/src/test/resources/sql/hsql/drop-all-tables.sql @@ -27,4 +27,5 @@ DROP TABLE IF EXISTS rule_chain; DROP TABLE IF EXISTS oauth2_client_registration; DROP TABLE IF EXISTS oauth2_client_registration_info; DROP TABLE IF EXISTS oauth2_client_registration_template; +DROP TABLE IF EXISTS usage_record; DROP FUNCTION IF EXISTS to_uuid; diff --git a/dao/src/test/resources/sql/psql/drop-all-tables.sql b/dao/src/test/resources/sql/psql/drop-all-tables.sql index e277c39403..b8ab69c758 100644 --- a/dao/src/test/resources/sql/psql/drop-all-tables.sql +++ b/dao/src/test/resources/sql/psql/drop-all-tables.sql @@ -28,3 +28,4 @@ DROP TABLE IF EXISTS tb_schema_settings; DROP TABLE IF EXISTS oauth2_client_registration; DROP TABLE IF EXISTS oauth2_client_registration_info; DROP TABLE IF EXISTS oauth2_client_registration_template; +DROP TABLE IF EXISTS usage_record; \ No newline at end of file diff --git a/dao/src/test/resources/sql/timescale/drop-all-tables.sql b/dao/src/test/resources/sql/timescale/drop-all-tables.sql index da7eaff876..4333bc33ee 100644 --- a/dao/src/test/resources/sql/timescale/drop-all-tables.sql +++ b/dao/src/test/resources/sql/timescale/drop-all-tables.sql @@ -28,3 +28,4 @@ DROP TABLE IF EXISTS tb_schema_settings; DROP TABLE IF EXISTS oauth2_client_registration; DROP TABLE IF EXISTS oauth2_client_registration_info; DROP TABLE IF EXISTS oauth2_client_registration_template; +DROP TABLE IF EXISTS usage_record; \ No newline at end of file diff --git a/netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttClientImpl.java b/netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttClientImpl.java index aef2cad684..08e6fc4f71 100644 --- a/netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttClientImpl.java +++ b/netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttClientImpl.java @@ -340,6 +340,7 @@ final class MqttClientImpl implements MqttClient { MqttPublishVariableHeader variableHeader = new MqttPublishVariableHeader(topic, getNewMessageId().messageId()); MqttPublishMessage message = new MqttPublishMessage(fixedHeader, variableHeader, payload); MqttPendingPublish pendingPublish = new MqttPendingPublish(variableHeader.packetId(), future, payload.retain(), message, qos); + ChannelFuture channelFuture = this.sendAndFlushPacket(message); if (channelFuture != null) {