Browse Source

Unique constraint for external id

pull/6954/head
Andrii Shvaika 4 years ago
parent
commit
e6fe7e6833
  1. 83
      application/src/main/data/upgrade/3.3.4/schema_update.sql
  2. 11
      dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java
  3. 12
      dao/src/main/java/org/thingsboard/server/dao/customer/CustomerServiceImpl.java
  4. 7
      dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardServiceImpl.java
  5. 15
      dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileServiceImpl.java
  6. 10
      dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java
  7. 29
      dao/src/main/java/org/thingsboard/server/dao/entity/AbstractEntityService.java
  8. 12
      dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewServiceImpl.java
  9. 10
      dao/src/main/java/org/thingsboard/server/dao/ota/BaseOtaPackageService.java
  10. 7
      dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java
  11. 3
      dao/src/main/java/org/thingsboard/server/dao/timeseries/CassandraBaseTimeseriesDao.java
  12. 8
      dao/src/main/java/org/thingsboard/server/dao/widget/WidgetsBundleServiceImpl.java
  13. 22
      dao/src/main/resources/sql/schema-entities.sql

83
application/src/main/data/upgrade/3.3.4/schema_update.sql

@ -33,16 +33,7 @@ ALTER TABLE widgets_bundle
ALTER TABLE entity_view
ADD COLUMN IF NOT EXISTS external_id UUID;
CREATE INDEX IF NOT EXISTS idx_device_external_id ON device(tenant_id, external_id);
CREATE INDEX IF NOT EXISTS idx_device_profile_external_id ON device_profile(tenant_id, external_id);
CREATE INDEX IF NOT EXISTS idx_asset_external_id ON asset(tenant_id, external_id);
CREATE INDEX IF NOT EXISTS idx_rule_chain_external_id ON rule_chain(tenant_id, external_id);
CREATE INDEX IF NOT EXISTS idx_rule_node_external_id ON rule_node(rule_chain_id, external_id);
CREATE INDEX IF NOT EXISTS idx_dashboard_external_id ON dashboard(tenant_id, external_id);
CREATE INDEX IF NOT EXISTS idx_customer_external_id ON customer(tenant_id, external_id);
CREATE INDEX IF NOT EXISTS idx_widgets_bundle_external_id ON widgets_bundle(tenant_id, external_id);
CREATE INDEX IF NOT EXISTS idx_entity_view_external_id ON entity_view(tenant_id, external_id);
CREATE INDEX IF NOT EXISTS idx_rule_node_type ON rule_node(type);
ALTER TABLE admin_settings
@ -73,3 +64,77 @@ CREATE TABLE IF NOT EXISTS user_auth_settings (
CREATE INDEX IF NOT EXISTS idx_api_usage_state_entity_id ON api_usage_state(entity_id);
ALTER TABLE tenant_profile DROP COLUMN IF EXISTS isolated_tb_core;
DO
$$
BEGIN
IF NOT EXISTS(SELECT 1 FROM pg_constraint WHERE conname = 'device_external_id_unq_key') THEN
ALTER TABLE device ADD CONSTRAINT device_external_id_unq_key UNIQUE (tenant_id, external_id);
END IF;
END;
$$;
DO
$$
BEGIN
IF NOT EXISTS(SELECT 1 FROM pg_constraint WHERE conname = 'device_profile_external_id_unq_key') THEN
ALTER TABLE device_profile ADD CONSTRAINT device_profile_external_id_unq_key UNIQUE (tenant_id, external_id);
END IF;
END;
$$;
DO
$$
BEGIN
IF NOT EXISTS(SELECT 1 FROM pg_constraint WHERE conname = 'asset_external_id_unq_key') THEN
ALTER TABLE asset ADD CONSTRAINT asset_external_id_unq_key UNIQUE (tenant_id, external_id);
END IF;
END;
$$;
DO
$$
BEGIN
IF NOT EXISTS(SELECT 1 FROM pg_constraint WHERE conname = 'rule_chain_external_id_unq_key') THEN
ALTER TABLE rule_chain ADD CONSTRAINT rule_chain_external_id_unq_key UNIQUE (tenant_id, external_id);
END IF;
END;
$$;
DO
$$
BEGIN
IF NOT EXISTS(SELECT 1 FROM pg_constraint WHERE conname = 'dashboard_external_id_unq_key') THEN
ALTER TABLE dashboard ADD CONSTRAINT dashboard_external_id_unq_key UNIQUE (tenant_id, external_id);
END IF;
END;
$$;
DO
$$
BEGIN
IF NOT EXISTS(SELECT 1 FROM pg_constraint WHERE conname = 'customer_external_id_unq_key') THEN
ALTER TABLE customer ADD CONSTRAINT customer_external_id_unq_key UNIQUE (tenant_id, external_id);
END IF;
END;
$$;
DO
$$
BEGIN
IF NOT EXISTS(SELECT 1 FROM pg_constraint WHERE conname = 'widgets_bundle_external_id_unq_key') THEN
ALTER TABLE widgets_bundle ADD CONSTRAINT widgets_bundle_external_id_unq_key UNIQUE (tenant_id, external_id);
END IF;
END;
$$;
DO
$$
BEGIN
IF NOT EXISTS(SELECT 1 FROM pg_constraint WHERE conname = 'entity_view_external_id_unq_key') THEN
ALTER TABLE entity_view ADD CONSTRAINT entity_view_external_id_unq_key UNIQUE (tenant_id, external_id);
END IF;
END;
$$;

11
dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java

@ -22,6 +22,7 @@ import com.google.common.util.concurrent.MoreExecutors;
import lombok.extern.slf4j.Slf4j;
import org.hibernate.exception.ConstraintViolationException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.util.Pair;
import org.springframework.stereotype.Service;
import org.springframework.transaction.event.TransactionalEventListener;
import org.thingsboard.server.common.data.EntitySubtype;
@ -126,12 +127,10 @@ public class BaseAssetService extends AbstractCachedEntityService<AssetCacheKey,
publishEvictEvent(evictEvent);
} catch (Exception t) {
handleEvictEvent(evictEvent);
ConstraintViolationException e = extractConstraintViolationException(t).orElse(null);
if (e != null && e.getConstraintName() != null && e.getConstraintName().equalsIgnoreCase("asset_name_unq_key")) {
throw new DataValidationException("Asset with such name already exists!");
} else {
throw t;
}
checkConstraintViolation(t,
"asset_name_unq_key", "Asset with such name already exists!",
"asset_external_id_unq_key", "Asset with such external id already exists!");
throw t;
}
return savedAsset;
}

12
dao/src/main/java/org/thingsboard/server/dao/customer/CustomerServiceImpl.java

@ -98,9 +98,15 @@ public class CustomerServiceImpl extends AbstractEntityService implements Custom
public Customer saveCustomer(Customer customer) {
log.trace("Executing saveCustomer [{}]", customer);
customerValidator.validate(customer, Customer::getTenantId);
Customer savedCustomer = customerDao.save(customer.getTenantId(), customer);
dashboardService.updateCustomerDashboards(savedCustomer.getTenantId(), savedCustomer.getId());
return savedCustomer;
try {
Customer savedCustomer = customerDao.save(customer.getTenantId(), customer);
dashboardService.updateCustomerDashboards(savedCustomer.getTenantId(), savedCustomer.getId());
return savedCustomer;
} catch (Exception e) {
checkConstraintViolation(e, "customer_external_id_unq_key", "Customer with such external id already exists!");
throw e;
}
}
@Override

7
dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardServiceImpl.java

@ -97,7 +97,12 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb
public Dashboard saveDashboard(Dashboard dashboard) {
log.trace("Executing saveDashboard [{}]", dashboard);
dashboardValidator.validate(dashboard, DashboardInfo::getTenantId);
return dashboardDao.save(dashboard.getTenantId(), dashboard);
try {
return dashboardDao.save(dashboard.getTenantId(), dashboard);
} catch (Exception e) {
checkConstraintViolation(e, "dashboard_external_id_unq_key", "Dashboard with such external id already exists!");
throw e;
}
}
@Override

15
dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileServiceImpl.java

@ -46,6 +46,7 @@ import org.thingsboard.server.dao.service.Validator;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import static org.thingsboard.server.dao.service.Validator.validateId;
@ -126,15 +127,11 @@ public class DeviceProfileServiceImpl extends AbstractCachedEntityService<Device
} catch (Exception t) {
handleEvictEvent(new DeviceProfileEvictEvent(deviceProfile.getTenantId(), deviceProfile.getName(),
oldDeviceProfile != null ? oldDeviceProfile.getName() : null, null, deviceProfile.isDefault()));
ConstraintViolationException e = extractConstraintViolationException(t).orElse(null);
if (e != null && e.getConstraintName() != null && e.getConstraintName().equalsIgnoreCase("device_profile_name_unq_key")) {
//TODO: refactor this to return existing device profile. If they are equal - no need to throw exception. Then we can make this call @Transactional and tests will not fail.
throw new DataValidationException(DEVICE_PROFILE_WITH_SUCH_NAME_ALREADY_EXISTS);
} else if (e != null && e.getConstraintName() != null && e.getConstraintName().equalsIgnoreCase("device_provision_key_unq_key")) {
throw new DataValidationException("Device profile with such provision device key already exists!");
} else {
throw t;
}
checkConstraintViolation(t,
Map.of("device_profile_name_unq_key", DEVICE_PROFILE_WITH_SUCH_NAME_ALREADY_EXISTS,
"device_provision_key_unq_key", "Device profile with such provision device key already exists!",
"device_profile_external_id_unq_key", "Device profile with such external id already exists!"));
throw t;
}
if (oldDeviceProfile != null && !oldDeviceProfile.getName().equals(deviceProfile.getName())) {
PageLink pageLink = new PageLink(100);

10
dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java

@ -237,12 +237,10 @@ public class DeviceServiceImpl extends AbstractCachedEntityService<DeviceCacheKe
return result;
} catch (Exception t) {
handleEvictEvent(deviceCacheEvictEvent);
ConstraintViolationException e = extractConstraintViolationException(t).orElse(null);
if (e != null && e.getConstraintName() != null && e.getConstraintName().equalsIgnoreCase("device_name_unq_key")) {
throw new DataValidationException("Device with such name already exists!");
} else {
throw t;
}
checkConstraintViolation(t,
"device_name_unq_key", "Device with such name already exists!",
"device_external_id_unq_key", "Device with such external id already exists!");
throw t;
}
}

29
dao/src/main/java/org/thingsboard/server/dao/entity/AbstractEntityService.java

@ -19,7 +19,9 @@ import lombok.extern.slf4j.Slf4j;
import org.hibernate.exception.ConstraintViolationException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.util.Pair;
import org.thingsboard.server.common.data.EntityView;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
@ -31,7 +33,9 @@ import org.thingsboard.server.dao.entityview.EntityViewService;
import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.dao.relation.RelationService;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
@Slf4j
@ -73,7 +77,7 @@ public abstract class AbstractEntityService {
alarmService.deleteEntityAlarmRelations(tenantId, entityId);
}
protected Optional<ConstraintViolationException> extractConstraintViolationException(Exception t) {
protected static Optional<ConstraintViolationException> extractConstraintViolationException(Exception t) {
if (t instanceof ConstraintViolationException) {
return Optional.of((ConstraintViolationException) t);
} else if (t.getCause() instanceof ConstraintViolationException) {
@ -83,6 +87,29 @@ public abstract class AbstractEntityService {
}
}
public static final void checkConstraintViolation(Exception t, String constraintName, String constraintMessage) {
checkConstraintViolation(t, Collections.singletonMap(constraintName, constraintMessage));
}
public static final void checkConstraintViolation(Exception t, String constraintName1, String constraintMessage1, String constraintName2, String constraintMessage2) {
checkConstraintViolation(t, Map.of(constraintName1, constraintMessage1, constraintName2, constraintMessage2));
}
public static final void checkConstraintViolation(Exception t, Map<String, String> constraints) {
var exOpt = extractConstraintViolationException(t);
if (exOpt.isPresent()) {
var ex = exOpt.get();
if (StringUtils.isNotEmpty(ex.getConstraintName())) {
var constraintName = ex.getConstraintName();
for (var constraintMessage : constraints.entrySet()) {
if (constraintName.equals(constraintMessage.getKey())) {
throw new DataValidationException(constraintMessage.getValue());
}
}
}
}
}
protected void checkAssignedEntityViewsToEdge(TenantId tenantId, EntityId entityId, EdgeId edgeId) {
List<EntityView> entityViews = entityViewService.findEntityViewsByTenantIdAndEntityId(tenantId, entityId);
if (entityViews != null && !entityViews.isEmpty()) {

12
dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewServiceImpl.java

@ -98,9 +98,15 @@ public class EntityViewServiceImpl extends AbstractCachedEntityService<EntityVie
public EntityView saveEntityView(EntityView entityView) {
log.trace("Executing save entity view [{}]", entityView);
EntityView old = entityViewValidator.validate(entityView, EntityView::getTenantId);
EntityView saved = entityViewDao.save(entityView.getTenantId(), entityView);
publishEvictEvent(new EntityViewEvictEvent(saved.getTenantId(), saved.getId(), saved.getEntityId(), old != null ? old.getEntityId() : null, saved.getName(), old != null ? old.getName() : null));
return saved;
try {
EntityView saved = entityViewDao.save(entityView.getTenantId(), entityView);
publishEvictEvent(new EntityViewEvictEvent(saved.getTenantId(), saved.getId(), saved.getEntityId(), old != null ? old.getEntityId() : null, saved.getName(), old != null ? old.getName() : null));
return saved;
} catch (Exception t) {
checkConstraintViolation(t,
"entity_view_external_id_unq_key", "Entity View with such external id already exists!");
throw t;
}
}
@Override

10
dao/src/main/java/org/thingsboard/server/dao/ota/BaseOtaPackageService.java

@ -234,14 +234,4 @@ public class BaseOtaPackageService extends AbstractCachedEntityService<OtaPackag
}
};
protected Optional<ConstraintViolationException> extractConstraintViolationException(Exception t) {
if (t instanceof ConstraintViolationException) {
return Optional.of((ConstraintViolationException) t);
} else if (t.getCause() instanceof ConstraintViolationException) {
return Optional.of((ConstraintViolationException) (t.getCause()));
} else {
return Optional.empty();
}
}
}

7
dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java

@ -98,7 +98,12 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC
@Transactional
public RuleChain saveRuleChain(RuleChain ruleChain) {
ruleChainValidator.validate(ruleChain, RuleChain::getTenantId);
return ruleChainDao.save(ruleChain.getTenantId(), ruleChain);
try {
return ruleChainDao.save(ruleChain.getTenantId(), ruleChain);
} catch (Exception e) {
checkConstraintViolation(e, "rule_chain_external_id_unq_key", "Rule Chain with such external id already exists!");
throw e;
}
}
@Override

3
dao/src/main/java/org/thingsboard/server/dao/timeseries/CassandraBaseTimeseriesDao.java

@ -256,8 +256,7 @@ public class CassandraBaseTimeseriesDao extends AbstractCassandraBaseTimeseriesD
deletePartitionAsync(tenantId, cursor, resultFuture);
for (Long partition : partitionsToDelete) {
CassandraPartitionCacheKey key = new CassandraPartitionCacheKey(entityId, query.getKey(), partition);
cassandraTsPartitionsCache.invalidate(key);
cassandraTsPartitionsCache.invalidate(new CassandraPartitionCacheKey(entityId, query.getKey(), partition));
}
}

8
dao/src/main/java/org/thingsboard/server/dao/widget/WidgetsBundleServiceImpl.java

@ -23,6 +23,7 @@ import org.thingsboard.server.common.data.id.WidgetsBundleId;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.widget.WidgetsBundle;
import org.thingsboard.server.dao.entity.AbstractCachedEntityService;
import org.thingsboard.server.dao.exception.IncorrectParameterException;
import org.thingsboard.server.dao.service.DataValidator;
import org.thingsboard.server.dao.service.PaginatedRemover;
@ -59,7 +60,12 @@ public class WidgetsBundleServiceImpl implements WidgetsBundleService {
public WidgetsBundle saveWidgetsBundle(WidgetsBundle widgetsBundle) {
log.trace("Executing saveWidgetsBundle [{}]", widgetsBundle);
widgetsBundleValidator.validate(widgetsBundle, WidgetsBundle::getTenantId);
return widgetsBundleDao.save(widgetsBundle.getTenantId(), widgetsBundle);
try {
return widgetsBundleDao.save(widgetsBundle.getTenantId(), widgetsBundle);
} catch (Exception e) {
AbstractCachedEntityService.checkConstraintViolation(e, "widgets_bundle_external_id_unq_key", "Widget Bundle with such external id already exists!");
throw e;
}
}
@Override

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

@ -84,7 +84,8 @@ CREATE TABLE IF NOT EXISTS asset (
tenant_id uuid,
type varchar(255),
external_id uuid,
CONSTRAINT asset_name_unq_key UNIQUE (tenant_id, name)
CONSTRAINT asset_name_unq_key UNIQUE (tenant_id, name),
CONSTRAINT asset_external_id_unq_key UNIQUE (tenant_id, external_id)
);
CREATE TABLE IF NOT EXISTS audit_log (
@ -144,7 +145,8 @@ CREATE TABLE IF NOT EXISTS customer (
tenant_id uuid,
title varchar(255),
zip varchar(255),
external_id uuid
external_id uuid,
CONSTRAINT customer_external_id_unq_key UNIQUE (tenant_id, external_id)
);
CREATE TABLE IF NOT EXISTS dashboard (
@ -158,7 +160,8 @@ CREATE TABLE IF NOT EXISTS dashboard (
mobile_hide boolean DEFAULT false,
mobile_order int,
image varchar(1000000),
external_id uuid
external_id uuid,
CONSTRAINT dashboard_external_id_unq_key UNIQUE (tenant_id, external_id)
);
CREATE TABLE IF NOT EXISTS rule_chain (
@ -173,7 +176,8 @@ CREATE TABLE IF NOT EXISTS rule_chain (
debug_mode boolean,
search_text varchar(255),
tenant_id uuid,
external_id uuid
external_id uuid,
CONSTRAINT rule_chain_external_id_unq_key UNIQUE (tenant_id, external_id)
);
CREATE TABLE IF NOT EXISTS rule_node (
@ -221,7 +225,7 @@ CREATE TABLE IF NOT EXISTS ota_package (
CONSTRAINT ota_package_tenant_title_version_unq_key UNIQUE (tenant_id, title, version)
);
CREATE TABLE IF NOT EXISTS queue(
CREATE TABLE IF NOT EXISTS queue (
id uuid NOT NULL CONSTRAINT queue_pkey PRIMARY KEY,
created_time bigint NOT NULL,
tenant_id uuid,
@ -258,6 +262,7 @@ CREATE TABLE IF NOT EXISTS device_profile (
external_id uuid,
CONSTRAINT device_profile_name_unq_key UNIQUE (tenant_id, name),
CONSTRAINT device_provision_key_unq_key UNIQUE (provision_device_key),
CONSTRAINT device_profile_external_id_unq_key UNIQUE (tenant_id, external_id),
CONSTRAINT fk_default_rule_chain_device_profile FOREIGN KEY (default_rule_chain_id) REFERENCES rule_chain(id),
CONSTRAINT fk_default_dashboard_device_profile FOREIGN KEY (default_dashboard_id) REFERENCES dashboard(id),
CONSTRAINT fk_firmware_device_profile FOREIGN KEY (firmware_id) REFERENCES ota_package(id),
@ -301,6 +306,7 @@ CREATE TABLE IF NOT EXISTS device (
software_id uuid,
external_id uuid,
CONSTRAINT device_name_unq_key UNIQUE (tenant_id, name),
CONSTRAINT device_external_id_unq_key UNIQUE (tenant_id, external_id),
CONSTRAINT fk_device_profile FOREIGN KEY (device_profile_id) REFERENCES device_profile(id),
CONSTRAINT fk_firmware_device FOREIGN KEY (firmware_id) REFERENCES ota_package(id),
CONSTRAINT fk_software_device FOREIGN KEY (software_id) REFERENCES ota_package(id)
@ -424,7 +430,8 @@ CREATE TABLE IF NOT EXISTS widgets_bundle (
title varchar(255),
image varchar(1000000),
description varchar(255),
external_id uuid
external_id uuid,
CONSTRAINT widgets_bundle_external_id_unq_key UNIQUE (tenant_id, external_id)
);
CREATE TABLE IF NOT EXISTS entity_view (
@ -441,7 +448,8 @@ CREATE TABLE IF NOT EXISTS entity_view (
end_ts bigint,
search_text varchar(255),
additional_info varchar,
external_id uuid
external_id uuid,
CONSTRAINT entity_view_external_id_unq_key UNIQUE (tenant_id, external_id)
);
CREATE TABLE IF NOT EXISTS ts_kv_latest

Loading…
Cancel
Save