added uniquifyStrategy

This commit is contained in:
dashevchenko 2025-10-10 17:46:15 +03:00
parent 7b8120fa1b
commit f1da967a7d
26 changed files with 170 additions and 87 deletions

View File

@ -47,5 +47,3 @@ WHERE NOT (
-- UPDATE TENANT PROFILE CONFIGURATION END -- UPDATE TENANT PROFILE CONFIGURATION END
ALTER TABLE entity_view ADD CONSTRAINT entity_view_name_unq_key UNIQUE (tenant_id, name);

View File

@ -36,6 +36,7 @@ import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.EntitySubtype; import org.thingsboard.server.common.data.EntitySubtype;
import org.thingsboard.server.common.data.NameConflictPolicy; import org.thingsboard.server.common.data.NameConflictPolicy;
import org.thingsboard.server.common.data.NameConflictStrategy; import org.thingsboard.server.common.data.NameConflictStrategy;
import org.thingsboard.server.common.data.UniquifyStrategy;
import org.thingsboard.server.common.data.asset.Asset; import org.thingsboard.server.common.data.asset.Asset;
import org.thingsboard.server.common.data.asset.AssetInfo; import org.thingsboard.server.common.data.asset.AssetInfo;
import org.thingsboard.server.common.data.asset.AssetSearchQuery; import org.thingsboard.server.common.data.asset.AssetSearchQuery;
@ -79,7 +80,7 @@ import static org.thingsboard.server.controller.ControllerConstants.EDGE_ID_PARA
import static org.thingsboard.server.controller.ControllerConstants.EDGE_UNASSIGN_ASYNC_FIRST_STEP_DESCRIPTION; import static org.thingsboard.server.controller.ControllerConstants.EDGE_UNASSIGN_ASYNC_FIRST_STEP_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.EDGE_UNASSIGN_RECEIVE_STEP_DESCRIPTION; import static org.thingsboard.server.controller.ControllerConstants.EDGE_UNASSIGN_RECEIVE_STEP_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.NAME_CONFLICT_POLICY_DESC; import static org.thingsboard.server.controller.ControllerConstants.NAME_CONFLICT_POLICY_DESC;
import static org.thingsboard.server.controller.ControllerConstants.NAME_CONFLICT_SEPARATOR_DESC; import static org.thingsboard.server.controller.ControllerConstants.UNIQUIFY_SEPARATOR_DESC;
import static org.thingsboard.server.controller.ControllerConstants.PAGE_DATA_PARAMETERS; import static org.thingsboard.server.controller.ControllerConstants.PAGE_DATA_PARAMETERS;
import static org.thingsboard.server.controller.ControllerConstants.PAGE_NUMBER_DESCRIPTION; import static org.thingsboard.server.controller.ControllerConstants.PAGE_NUMBER_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.PAGE_SIZE_DESCRIPTION; import static org.thingsboard.server.controller.ControllerConstants.PAGE_SIZE_DESCRIPTION;
@ -87,6 +88,7 @@ import static org.thingsboard.server.controller.ControllerConstants.SORT_ORDER_D
import static org.thingsboard.server.controller.ControllerConstants.SORT_PROPERTY_DESCRIPTION; import static org.thingsboard.server.controller.ControllerConstants.SORT_PROPERTY_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.TENANT_AUTHORITY_PARAGRAPH; import static org.thingsboard.server.controller.ControllerConstants.TENANT_AUTHORITY_PARAGRAPH;
import static org.thingsboard.server.controller.ControllerConstants.TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH; import static org.thingsboard.server.controller.ControllerConstants.TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH;
import static org.thingsboard.server.controller.ControllerConstants.UNIQUIFY_STRATEGY_DESC;
import static org.thingsboard.server.controller.ControllerConstants.UUID_WIKI_LINK; import static org.thingsboard.server.controller.ControllerConstants.UUID_WIKI_LINK;
import static org.thingsboard.server.controller.EdgeController.EDGE_ID; import static org.thingsboard.server.controller.EdgeController.EDGE_ID;
@ -143,12 +145,14 @@ public class AssetController extends BaseController {
@ResponseBody @ResponseBody
public Asset saveAsset(@io.swagger.v3.oas.annotations.parameters.RequestBody(description = "A JSON value representing the asset.") @RequestBody Asset asset, public Asset saveAsset(@io.swagger.v3.oas.annotations.parameters.RequestBody(description = "A JSON value representing the asset.") @RequestBody Asset asset,
@Parameter(description = NAME_CONFLICT_POLICY_DESC) @Parameter(description = NAME_CONFLICT_POLICY_DESC)
@RequestParam(name = "policy", defaultValue = "FAIL") NameConflictPolicy policy, @RequestParam(name = "nameConflictPolicy", defaultValue = "FAIL") NameConflictPolicy nameConflictPolicy,
@Parameter(description = NAME_CONFLICT_SEPARATOR_DESC) @Parameter(description = UNIQUIFY_SEPARATOR_DESC)
@RequestParam(name = "separator", defaultValue = "_") String separator) throws Exception { @RequestParam(name = "uniquifySeparator", defaultValue = "_") String uniquifySeparator,
@Parameter(description = UNIQUIFY_STRATEGY_DESC)
@RequestParam(name = "uniquifyStrategy", defaultValue = "RANDOM") UniquifyStrategy uniquifyStrategy) throws Exception {
asset.setTenantId(getTenantId()); asset.setTenantId(getTenantId());
checkEntity(asset.getId(), asset, Resource.ASSET); checkEntity(asset.getId(), asset, Resource.ASSET);
return tbAssetService.save(asset, new NameConflictStrategy(policy, separator), getCurrentUser()); return tbAssetService.save(asset, new NameConflictStrategy(nameConflictPolicy, uniquifySeparator, uniquifyStrategy), getCurrentUser());
} }
@ApiOperation(value = "Delete asset (deleteAsset)", @ApiOperation(value = "Delete asset (deleteAsset)",

View File

@ -1749,7 +1749,12 @@ public class ControllerConstants {
" If omitted, FAIL policy is applied. FAIL policy implies exception will be thrown if an entity with the same name already exists. " + " If omitted, FAIL policy is applied. FAIL policy implies exception will be thrown if an entity with the same name already exists. " +
" UNIQUIFY policy appends a suffix to the entity name, if a name conflict occurs."; " UNIQUIFY policy appends a suffix to the entity name, if a name conflict occurs.";
public static final String NAME_CONFLICT_SEPARATOR_DESC = "Optional value of name suffix separator used by UNIQUIFY policy. By default, underscore separator is used. " + public static final String UNIQUIFY_SEPARATOR_DESC = "Optional value of name suffix separator used by UNIQUIFY policy. By default, underscore separator is used. " +
"For example, strategy is UNIQUIFY, separator is '-'; if a name conflict occurs for entity name 'test-name', " + "For example, strategy is UNIQUIFY, separator is '-'; if a name conflict occurs for entity name 'test-name', " +
"created entity will have name like 'test-name-7fsh4f'."; "created entity will have name like 'test-name-7fsh4f'.";
public static final String UNIQUIFY_STRATEGY_DESC = "Optional value of uniquify strategy used by UNIQUIFY policy. Possible values: RANDOM or INCREMENTAL. " +
"By default, RANDOM strategy is used, which means random alphanumeric string will be added as a suffix to entity name. " +
"For example, strategy is UNIQUIFY, uniquify strategy is INCREMENTAL; if a name conflict occurs for entity name 'test-name', " +
"created entity will have name like 'test-name-1.";
} }

View File

@ -34,6 +34,7 @@ import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.NameConflictStrategy; import org.thingsboard.server.common.data.NameConflictStrategy;
import org.thingsboard.server.common.data.NameConflictPolicy; import org.thingsboard.server.common.data.NameConflictPolicy;
import org.thingsboard.server.common.data.UniquifyStrategy;
import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
@ -50,7 +51,7 @@ import static org.thingsboard.server.controller.ControllerConstants.CUSTOMER_ID_
import static org.thingsboard.server.controller.ControllerConstants.CUSTOMER_TEXT_SEARCH_DESCRIPTION; import static org.thingsboard.server.controller.ControllerConstants.CUSTOMER_TEXT_SEARCH_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.HOME_DASHBOARD; import static org.thingsboard.server.controller.ControllerConstants.HOME_DASHBOARD;
import static org.thingsboard.server.controller.ControllerConstants.NAME_CONFLICT_POLICY_DESC; import static org.thingsboard.server.controller.ControllerConstants.NAME_CONFLICT_POLICY_DESC;
import static org.thingsboard.server.controller.ControllerConstants.NAME_CONFLICT_SEPARATOR_DESC; import static org.thingsboard.server.controller.ControllerConstants.UNIQUIFY_SEPARATOR_DESC;
import static org.thingsboard.server.controller.ControllerConstants.PAGE_DATA_PARAMETERS; import static org.thingsboard.server.controller.ControllerConstants.PAGE_DATA_PARAMETERS;
import static org.thingsboard.server.controller.ControllerConstants.PAGE_NUMBER_DESCRIPTION; import static org.thingsboard.server.controller.ControllerConstants.PAGE_NUMBER_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.PAGE_SIZE_DESCRIPTION; import static org.thingsboard.server.controller.ControllerConstants.PAGE_SIZE_DESCRIPTION;
@ -58,6 +59,7 @@ import static org.thingsboard.server.controller.ControllerConstants.SORT_ORDER_D
import static org.thingsboard.server.controller.ControllerConstants.SORT_PROPERTY_DESCRIPTION; import static org.thingsboard.server.controller.ControllerConstants.SORT_PROPERTY_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.TENANT_AUTHORITY_PARAGRAPH; import static org.thingsboard.server.controller.ControllerConstants.TENANT_AUTHORITY_PARAGRAPH;
import static org.thingsboard.server.controller.ControllerConstants.TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH; import static org.thingsboard.server.controller.ControllerConstants.TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH;
import static org.thingsboard.server.controller.ControllerConstants.UNIQUIFY_STRATEGY_DESC;
import static org.thingsboard.server.controller.ControllerConstants.UUID_WIKI_LINK; import static org.thingsboard.server.controller.ControllerConstants.UUID_WIKI_LINK;
@RestController @RestController
@ -134,12 +136,14 @@ public class CustomerController extends BaseController {
@ResponseBody @ResponseBody
public Customer saveCustomer(@io.swagger.v3.oas.annotations.parameters.RequestBody(description = "A JSON value representing the customer.") @RequestBody Customer customer, public Customer saveCustomer(@io.swagger.v3.oas.annotations.parameters.RequestBody(description = "A JSON value representing the customer.") @RequestBody Customer customer,
@Parameter(description = NAME_CONFLICT_POLICY_DESC) @Parameter(description = NAME_CONFLICT_POLICY_DESC)
@RequestParam(name = "policy", defaultValue = "FAIL") NameConflictPolicy policy, @RequestParam(name = "nameConflictPolicy", defaultValue = "FAIL") NameConflictPolicy nameConflictPolicy,
@Parameter(description = NAME_CONFLICT_SEPARATOR_DESC) @Parameter(description = UNIQUIFY_SEPARATOR_DESC)
@RequestParam(name = "separator", defaultValue = "_") String separator) throws Exception { @RequestParam(name = "uniquifySeparator", defaultValue = "_") String uniquifySeparator,
@Parameter(description = UNIQUIFY_STRATEGY_DESC)
@RequestParam(name = "uniquifyStrategy", defaultValue = "RANDOM") UniquifyStrategy uniquifyStrategy) throws Exception {
customer.setTenantId(getTenantId()); customer.setTenantId(getTenantId());
checkEntity(customer.getId(), customer, Resource.CUSTOMER); checkEntity(customer.getId(), customer, Resource.CUSTOMER);
return tbCustomerService.save(customer, new NameConflictStrategy(policy, separator), getCurrentUser()); return tbCustomerService.save(customer, new NameConflictStrategy(nameConflictPolicy, uniquifySeparator, uniquifyStrategy), getCurrentUser());
} }
@ApiOperation(value = "Delete Customer (deleteCustomer)", @ApiOperation(value = "Delete Customer (deleteCustomer)",

View File

@ -50,6 +50,7 @@ import org.thingsboard.server.common.data.NameConflictPolicy;
import org.thingsboard.server.common.data.NameConflictStrategy; import org.thingsboard.server.common.data.NameConflictStrategy;
import org.thingsboard.server.common.data.SaveDeviceWithCredentialsRequest; import org.thingsboard.server.common.data.SaveDeviceWithCredentialsRequest;
import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.UniquifyStrategy;
import org.thingsboard.server.common.data.device.DeviceSearchQuery; import org.thingsboard.server.common.data.device.DeviceSearchQuery;
import org.thingsboard.server.common.data.edge.Edge; import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
@ -111,7 +112,7 @@ import static org.thingsboard.server.controller.ControllerConstants.EDGE_ID_PARA
import static org.thingsboard.server.controller.ControllerConstants.EDGE_UNASSIGN_ASYNC_FIRST_STEP_DESCRIPTION; import static org.thingsboard.server.controller.ControllerConstants.EDGE_UNASSIGN_ASYNC_FIRST_STEP_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.EDGE_UNASSIGN_RECEIVE_STEP_DESCRIPTION; import static org.thingsboard.server.controller.ControllerConstants.EDGE_UNASSIGN_RECEIVE_STEP_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.NAME_CONFLICT_POLICY_DESC; import static org.thingsboard.server.controller.ControllerConstants.NAME_CONFLICT_POLICY_DESC;
import static org.thingsboard.server.controller.ControllerConstants.NAME_CONFLICT_SEPARATOR_DESC; import static org.thingsboard.server.controller.ControllerConstants.UNIQUIFY_SEPARATOR_DESC;
import static org.thingsboard.server.controller.ControllerConstants.PAGE_DATA_PARAMETERS; import static org.thingsboard.server.controller.ControllerConstants.PAGE_DATA_PARAMETERS;
import static org.thingsboard.server.controller.ControllerConstants.PAGE_NUMBER_DESCRIPTION; import static org.thingsboard.server.controller.ControllerConstants.PAGE_NUMBER_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.PAGE_SIZE_DESCRIPTION; import static org.thingsboard.server.controller.ControllerConstants.PAGE_SIZE_DESCRIPTION;
@ -121,6 +122,7 @@ import static org.thingsboard.server.controller.ControllerConstants.TENANT_AUTHO
import static org.thingsboard.server.controller.ControllerConstants.TENANT_ID; import static org.thingsboard.server.controller.ControllerConstants.TENANT_ID;
import static org.thingsboard.server.controller.ControllerConstants.TENANT_ID_PARAM_DESCRIPTION; import static org.thingsboard.server.controller.ControllerConstants.TENANT_ID_PARAM_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH; import static org.thingsboard.server.controller.ControllerConstants.TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH;
import static org.thingsboard.server.controller.ControllerConstants.UNIQUIFY_STRATEGY_DESC;
import static org.thingsboard.server.controller.ControllerConstants.UUID_WIKI_LINK; import static org.thingsboard.server.controller.ControllerConstants.UUID_WIKI_LINK;
import static org.thingsboard.server.controller.EdgeController.EDGE_ID; import static org.thingsboard.server.controller.EdgeController.EDGE_ID;
@ -184,16 +186,18 @@ public class DeviceController extends BaseController {
"If omitted, access token will be auto-generated.") "If omitted, access token will be auto-generated.")
@RequestParam(name = "accessToken", required = false) String accessToken, @RequestParam(name = "accessToken", required = false) String accessToken,
@Parameter(description = NAME_CONFLICT_POLICY_DESC) @Parameter(description = NAME_CONFLICT_POLICY_DESC)
@RequestParam(name = "policy", defaultValue = "FAIL") NameConflictPolicy policy, @RequestParam(name = "nameConflictPolicy", defaultValue = "FAIL") NameConflictPolicy nameConflictPolicy,
@Parameter(description = NAME_CONFLICT_SEPARATOR_DESC) @Parameter(description = UNIQUIFY_SEPARATOR_DESC)
@RequestParam(name = "separator", defaultValue = "_") String separator) throws Exception { @RequestParam(name = "uniquifySeparator", defaultValue = "_") String uniquifySeparator,
@Parameter(description = UNIQUIFY_STRATEGY_DESC)
@RequestParam(name = "uniquifyStrategy", defaultValue = "RANDOM") UniquifyStrategy uniquifyStrategy) throws Exception {
device.setTenantId(getCurrentUser().getTenantId()); device.setTenantId(getCurrentUser().getTenantId());
if (device.getId() != null) { if (device.getId() != null) {
checkDeviceId(device.getId(), Operation.WRITE); checkDeviceId(device.getId(), Operation.WRITE);
} else { } else {
checkEntity(null, device, Resource.DEVICE); checkEntity(null, device, Resource.DEVICE);
} }
return tbDeviceService.save(device, accessToken, new NameConflictStrategy(policy, separator), getCurrentUser()); return tbDeviceService.save(device, accessToken, new NameConflictStrategy(nameConflictPolicy, uniquifySeparator, uniquifyStrategy), getCurrentUser());
} }
@ApiOperation(value = "Create Device (saveDevice) with credentials ", @ApiOperation(value = "Create Device (saveDevice) with credentials ",
@ -220,14 +224,16 @@ public class DeviceController extends BaseController {
public Device saveDeviceWithCredentials(@Parameter(description = "The JSON object with device and credentials. See method description above for example.") public Device saveDeviceWithCredentials(@Parameter(description = "The JSON object with device and credentials. See method description above for example.")
@Valid @RequestBody SaveDeviceWithCredentialsRequest deviceAndCredentials, @Valid @RequestBody SaveDeviceWithCredentialsRequest deviceAndCredentials,
@Parameter(description = NAME_CONFLICT_POLICY_DESC) @Parameter(description = NAME_CONFLICT_POLICY_DESC)
@RequestParam(name = "policy", defaultValue = "FAIL") NameConflictPolicy policy, @RequestParam(name = "nameConflictPolicy", defaultValue = "FAIL") NameConflictPolicy nameConflictPolicy,
@Parameter(description = NAME_CONFLICT_SEPARATOR_DESC) @Parameter(description = UNIQUIFY_SEPARATOR_DESC)
@RequestParam(name = "separator", defaultValue = "_") String separator) throws ThingsboardException { @RequestParam(name = "uniquifySeparator", defaultValue = "_") String uniquifySeparator,
@Parameter(description = UNIQUIFY_STRATEGY_DESC)
@RequestParam(name = "uniquifyStrategy", defaultValue = "RANDOM") UniquifyStrategy uniquifyStrategy) throws ThingsboardException {
Device device = deviceAndCredentials.getDevice(); Device device = deviceAndCredentials.getDevice();
DeviceCredentials credentials = deviceAndCredentials.getCredentials(); DeviceCredentials credentials = deviceAndCredentials.getCredentials();
device.setTenantId(getCurrentUser().getTenantId()); device.setTenantId(getCurrentUser().getTenantId());
checkEntity(device.getId(), device, Resource.DEVICE); checkEntity(device.getId(), device, Resource.DEVICE);
return tbDeviceService.saveDeviceWithCredentials(device, credentials, new NameConflictStrategy(policy, separator), getCurrentUser()); return tbDeviceService.saveDeviceWithCredentials(device, credentials, new NameConflictStrategy(nameConflictPolicy, uniquifySeparator, uniquifyStrategy), getCurrentUser());
} }
@ApiOperation(value = "Delete device (deleteDevice)", @ApiOperation(value = "Delete device (deleteDevice)",

View File

@ -36,6 +36,7 @@ import org.thingsboard.server.common.data.EntityView;
import org.thingsboard.server.common.data.EntityViewInfo; import org.thingsboard.server.common.data.EntityViewInfo;
import org.thingsboard.server.common.data.NameConflictPolicy; import org.thingsboard.server.common.data.NameConflictPolicy;
import org.thingsboard.server.common.data.NameConflictStrategy; import org.thingsboard.server.common.data.NameConflictStrategy;
import org.thingsboard.server.common.data.UniquifyStrategy;
import org.thingsboard.server.common.data.edge.Edge; import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.entityview.EntityViewSearchQuery; import org.thingsboard.server.common.data.entityview.EntityViewSearchQuery;
import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.exception.ThingsboardException;
@ -72,7 +73,7 @@ import static org.thingsboard.server.controller.ControllerConstants.ENTITY_VIEW_
import static org.thingsboard.server.controller.ControllerConstants.ENTITY_VIEW_TYPE; import static org.thingsboard.server.controller.ControllerConstants.ENTITY_VIEW_TYPE;
import static org.thingsboard.server.controller.ControllerConstants.MODEL_DESCRIPTION; import static org.thingsboard.server.controller.ControllerConstants.MODEL_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.NAME_CONFLICT_POLICY_DESC; import static org.thingsboard.server.controller.ControllerConstants.NAME_CONFLICT_POLICY_DESC;
import static org.thingsboard.server.controller.ControllerConstants.NAME_CONFLICT_SEPARATOR_DESC; import static org.thingsboard.server.controller.ControllerConstants.UNIQUIFY_SEPARATOR_DESC;
import static org.thingsboard.server.controller.ControllerConstants.PAGE_DATA_PARAMETERS; import static org.thingsboard.server.controller.ControllerConstants.PAGE_DATA_PARAMETERS;
import static org.thingsboard.server.controller.ControllerConstants.PAGE_NUMBER_DESCRIPTION; import static org.thingsboard.server.controller.ControllerConstants.PAGE_NUMBER_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.PAGE_SIZE_DESCRIPTION; import static org.thingsboard.server.controller.ControllerConstants.PAGE_SIZE_DESCRIPTION;
@ -80,6 +81,7 @@ import static org.thingsboard.server.controller.ControllerConstants.SORT_ORDER_D
import static org.thingsboard.server.controller.ControllerConstants.SORT_PROPERTY_DESCRIPTION; import static org.thingsboard.server.controller.ControllerConstants.SORT_PROPERTY_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.TENANT_AUTHORITY_PARAGRAPH; import static org.thingsboard.server.controller.ControllerConstants.TENANT_AUTHORITY_PARAGRAPH;
import static org.thingsboard.server.controller.ControllerConstants.TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH; import static org.thingsboard.server.controller.ControllerConstants.TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH;
import static org.thingsboard.server.controller.ControllerConstants.UNIQUIFY_STRATEGY_DESC;
import static org.thingsboard.server.controller.EdgeController.EDGE_ID; import static org.thingsboard.server.controller.EdgeController.EDGE_ID;
/** /**
@ -134,9 +136,11 @@ public class EntityViewController extends BaseController {
@Parameter(description = "A JSON object representing the entity view.") @Parameter(description = "A JSON object representing the entity view.")
@RequestBody EntityView entityView, @RequestBody EntityView entityView,
@Parameter(description = NAME_CONFLICT_POLICY_DESC) @Parameter(description = NAME_CONFLICT_POLICY_DESC)
@RequestParam(name = "policy", defaultValue = "FAIL") NameConflictPolicy policy, @RequestParam(name = "nameConflictPolicy", defaultValue = "FAIL") NameConflictPolicy nameConflictPolicy,
@Parameter(description = NAME_CONFLICT_SEPARATOR_DESC) @Parameter(description = UNIQUIFY_SEPARATOR_DESC)
@RequestParam(name = "separator", defaultValue = "_") String separator) throws Exception { @RequestParam(name = "uniquifySeparator", defaultValue = "_") String uniquifySeparator,
@Parameter(description = UNIQUIFY_STRATEGY_DESC)
@RequestParam(name = "uniquifyStrategy", defaultValue = "RANDOM") UniquifyStrategy uniquifyStrategy) throws Exception {
entityView.setTenantId(getCurrentUser().getTenantId()); entityView.setTenantId(getCurrentUser().getTenantId());
EntityView existingEntityView = null; EntityView existingEntityView = null;
if (entityView.getId() == null) { if (entityView.getId() == null) {
@ -145,7 +149,7 @@ public class EntityViewController extends BaseController {
} else { } else {
existingEntityView = checkEntityViewId(entityView.getId(), Operation.WRITE); existingEntityView = checkEntityViewId(entityView.getId(), Operation.WRITE);
} }
return tbEntityViewService.save(entityView, existingEntityView, new NameConflictStrategy(policy, separator), getCurrentUser()); return tbEntityViewService.save(entityView, existingEntityView, new NameConflictStrategy(nameConflictPolicy, uniquifySeparator, uniquifyStrategy), getCurrentUser());
} }
@ApiOperation(value = "Delete entity view (deleteEntityView)", @ApiOperation(value = "Delete entity view (deleteEntityView)",

View File

@ -76,6 +76,6 @@ public class Lwm2mController extends BaseController {
public Device saveDeviceWithCredentials(@RequestBody Map<Class<?>, Object> deviceWithDeviceCredentials) throws ThingsboardException { public Device saveDeviceWithCredentials(@RequestBody Map<Class<?>, Object> deviceWithDeviceCredentials) throws ThingsboardException {
Device device = checkNotNull(JacksonUtil.convertValue(deviceWithDeviceCredentials.get(Device.class), Device.class)); Device device = checkNotNull(JacksonUtil.convertValue(deviceWithDeviceCredentials.get(Device.class), Device.class));
DeviceCredentials credentials = checkNotNull(JacksonUtil.convertValue(deviceWithDeviceCredentials.get(DeviceCredentials.class), DeviceCredentials.class)); DeviceCredentials credentials = checkNotNull(JacksonUtil.convertValue(deviceWithDeviceCredentials.get(DeviceCredentials.class), DeviceCredentials.class));
return deviceController.saveDeviceWithCredentials(new SaveDeviceWithCredentialsRequest(device, credentials), DEFAULT.policy(), DEFAULT.separator()); return deviceController.saveDeviceWithCredentials(new SaveDeviceWithCredentialsRequest(device, credentials), DEFAULT.policy(), DEFAULT.separator(), DEFAULT.uniquifyStrategy());
} }
} }

View File

@ -1083,19 +1083,25 @@ public class AssetControllerTest extends AbstractControllerTest {
@Test @Test
public void testSaveAssetWithUniquifyStrategy() throws Exception { public void testSaveAssetWithUniquifyStrategy() throws Exception {
Asset asset = new Asset(); Asset asset = new Asset();
asset.setName("My asset"); asset.setName("My unique asset");
asset.setType("default"); asset.setType("default");
doPost("/api/asset", asset, Asset.class); doPost("/api/asset", asset, Asset.class);
doPost("/api/asset", asset).andExpect(status().isBadRequest()); doPost("/api/asset", asset).andExpect(status().isBadRequest());
doPost("/api/asset?policy=FAIL", asset).andExpect(status().isBadRequest()); doPost("/api/asset?nameConflictPolicy=FAIL", asset).andExpect(status().isBadRequest());
Asset secondAsset = doPost("/api/asset?policy=UNIQUIFY", asset, Asset.class); Asset secondAsset = doPost("/api/asset?nameConflictPolicy=UNIQUIFY", asset, Asset.class);
assertThat(secondAsset.getName()).startsWith("My asset_"); assertThat(secondAsset.getName()).startsWith("My unique asset_");
Asset thirdAsset = doPost("/api/asset?policy=UNIQUIFY&separator=-", asset, Asset.class); Asset thirdAsset = doPost("/api/asset?nameConflictPolicy=UNIQUIFY&uniquifySeparator=-", asset, Asset.class);
assertThat(thirdAsset.getName()).startsWith("My asset-"); assertThat(thirdAsset.getName()).startsWith("My unique asset-");
Asset fourthAsset = doPost("/api/asset?nameConflictPolicy=UNIQUIFY&uniquifyStrategy=INCREMENTAL", asset, Asset.class);
assertThat(fourthAsset.getName()).isEqualTo("My unique asset_1");
Asset fifthAsset = doPost("/api/asset?nameConflictPolicy=UNIQUIFY&uniquifyStrategy=INCREMENTAL", asset, Asset.class);
assertThat(fifthAsset.getName()).isEqualTo("My unique asset_2");
} }
private Asset createAsset(String name) { private Asset createAsset(String name) {

View File

@ -33,6 +33,7 @@ import org.springframework.context.annotation.Primary;
import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.ContextConfiguration;
import org.thingsboard.common.util.ThingsBoardExecutors; import org.thingsboard.common.util.ThingsBoardExecutors;
import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.User;
@ -465,16 +466,22 @@ public class CustomerControllerTest extends AbstractControllerTest {
@Test @Test
public void testSaveCustomerWithUniquifyStrategy() throws Exception { public void testSaveCustomerWithUniquifyStrategy() throws Exception {
Customer customer = new Customer(); Customer customer = new Customer();
customer.setTitle("My customer"); customer.setTitle("My unique customer");
Customer savedCustomer = doPost("/api/customer", customer, Customer.class); Customer savedCustomer = doPost("/api/customer", customer, Customer.class);
doPost("/api/customer?policy=FAIL", customer).andExpect(status().isBadRequest()); doPost("/api/customer?nameConflictPolicy=FAIL", customer).andExpect(status().isBadRequest());
Customer secondCustomer = doPost("/api/customer?policy=UNIQUIFY", customer, Customer.class); Customer secondCustomer = doPost("/api/customer?nameConflictPolicy=UNIQUIFY", customer, Customer.class);
assertThat(secondCustomer.getName()).startsWith("My customer_"); assertThat(secondCustomer.getName()).startsWith("My unique customer_");
Customer thirdCustomer = doPost("/api/customer?policy=UNIQUIFY&separator=-", customer, Customer.class); Customer thirdCustomer = doPost("/api/customer?nameConflictPolicy=UNIQUIFY&uniquifySeparator=-", customer, Customer.class);
assertThat(thirdCustomer.getName()).startsWith("My customer-"); assertThat(thirdCustomer.getName()).startsWith("My unique customer-");
Customer fourthCustomer = doPost("/api/customer?nameConflictPolicy=UNIQUIFY&uniquifyStrategy=INCREMENTAL", customer, Customer.class);
assertThat(fourthCustomer.getName()).isEqualTo("My unique customer_1");
Customer fifthCustomer = doPost("/api/customer?nameConflictPolicy=UNIQUIFY&uniquifyStrategy=INCREMENTAL", customer, Customer.class);
assertThat(fifthCustomer.getName()).isEqualTo("My unique customer_2");
} }
private Customer createCustomer(String title) { private Customer createCustomer(String title) {

View File

@ -1611,19 +1611,25 @@ public class DeviceControllerTest extends AbstractControllerTest {
@Test @Test
public void testSaveDeviceWithUniquifyStrategy() throws Exception { public void testSaveDeviceWithUniquifyStrategy() throws Exception {
Device device = new Device(); Device device = new Device();
device.setName("My device"); device.setName("My unique device");
device.setType("default"); device.setType("default");
Device savedDevice = doPost("/api/device", device, Device.class); Device savedDevice = doPost("/api/device", device, Device.class);
doPost("/api/device", device).andExpect(status().isBadRequest()); doPost("/api/device", device).andExpect(status().isBadRequest());
doPost("/api/device?policy=FAIL", device).andExpect(status().isBadRequest()); doPost("/api/device?nameConflictPolicy=FAIL", device).andExpect(status().isBadRequest());
Device secondDevice = doPost("/api/device?policy=UNIQUIFY", device, Device.class); Device secondDevice = doPost("/api/device?nameConflictPolicy=UNIQUIFY", device, Device.class);
assertThat(secondDevice.getName()).startsWith("My device_"); assertThat(secondDevice.getName()).startsWith("My unique device_");
Device thirdDevice = doPost("/api/device?policy=UNIQUIFY&separator=-", device, Device.class); Device thirdDevice = doPost("/api/device?nameConflictPolicy=UNIQUIFY&uniquifySeparator=-", device, Device.class);
assertThat(thirdDevice.getName()).startsWith("My device-"); assertThat(thirdDevice.getName()).startsWith("My unique device-");
Device fourthDevice = doPost("/api/device?nameConflictPolicy=UNIQUIFY&uniquifyStrategy=INCREMENTAL", device, Device.class);
assertThat(fourthDevice.getName()).isEqualTo("My unique device_1");
Device fifthDevice = doPost("/api/device?nameConflictPolicy=UNIQUIFY&uniquifyStrategy=INCREMENTAL", device, Device.class);
assertThat(fifthDevice.getName()).isEqualTo("My unique device_2");
} }
private Device createDevice(String name) { private Device createDevice(String name) {

View File

@ -860,16 +860,22 @@ public class EntityViewControllerTest extends AbstractControllerTest {
view.setEntityId(testDevice.getId()); view.setEntityId(testDevice.getId());
view.setTenantId(tenantId); view.setTenantId(tenantId);
view.setType("default"); view.setType("default");
view.setName("Test device view"); view.setName("My unique view");
EntityView savedView = doPost("/api/entityView", view, EntityView.class); EntityView savedView = doPost("/api/entityView", view, EntityView.class);
doPost("/api/entityView?policy=FAIL", view).andExpect(status().isBadRequest()); doPost("/api/entityView?nameConflictPolicy=FAIL", view).andExpect(status().isBadRequest());
EntityView secondView = doPost("/api/entityView?policy=UNIQUIFY", view, EntityView.class); EntityView secondView = doPost("/api/entityView?nameConflictPolicy=UNIQUIFY", view, EntityView.class);
assertThat(secondView.getName()).startsWith("Test device view_"); assertThat(secondView.getName()).startsWith("My unique view_");
EntityView thirdView = doPost("/api/entityView?policy=UNIQUIFY&separator=-", view, EntityView.class); EntityView thirdView = doPost("/api/entityView?nameConflictPolicy=UNIQUIFY&uniquifySeparator=-", view, EntityView.class);
assertThat(thirdView.getName()).startsWith("Test device view-"); assertThat(thirdView.getName()).startsWith("My unique view-");
EntityView fourthView = doPost("/api/entityView?nameConflictPolicy=UNIQUIFY&uniquifyStrategy=INCREMENTAL", view, EntityView.class);
assertThat(fourthView.getName()).isEqualTo("My unique view_1");
EntityView fifthEntityView = doPost("/api/entityView?nameConflictPolicy=UNIQUIFY&uniquifyStrategy=INCREMENTAL", view, EntityView.class);
assertThat(fifthEntityView.getName()).isEqualTo("My unique view_2");
} }
} }

View File

@ -18,8 +18,8 @@ package org.thingsboard.server.common.data;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
@Schema @Schema
public record NameConflictStrategy(NameConflictPolicy policy, String separator) { public record NameConflictStrategy(NameConflictPolicy policy, String separator, UniquifyStrategy uniquifyStrategy) {
public static final NameConflictStrategy DEFAULT = new NameConflictStrategy(NameConflictPolicy.FAIL, null); public static final NameConflictStrategy DEFAULT = new NameConflictStrategy(NameConflictPolicy.FAIL, null, null);
} }

View File

@ -0,0 +1,23 @@
/**
* 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.common.data;
public enum UniquifyStrategy {
RANDOM,
INCREMENTAL;
}

View File

@ -33,7 +33,7 @@ public interface Dao<T> {
ListenableFuture<T> findByIdAsync(TenantId tenantId, UUID id); ListenableFuture<T> findByIdAsync(TenantId tenantId, UUID id);
default EntityInfo findEntityInfoByName(TenantId tenantId, String name) { default List<EntityInfo> findEntityInfosByNamePrefix(TenantId tenantId, String name) {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }

View File

@ -51,8 +51,12 @@ import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.Set;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.stream.Collectors;
import static org.thingsboard.server.common.data.UniquifyStrategy.RANDOM;
@Slf4j @Slf4j
public abstract class AbstractEntityService { public abstract class AbstractEntityService {
@ -167,18 +171,23 @@ public abstract class AbstractEntityService {
return now + TimeUnit.MINUTES.toMillis(DebugModeUtil.getMaxDebugAllDuration(tbTenantProfileCache.get(tenantId).getDefaultProfileConfiguration().getMaxDebugModeDurationMinutes(), defaultDebugDurationMinutes)); return now + TimeUnit.MINUTES.toMillis(DebugModeUtil.getMaxDebugAllDuration(tbTenantProfileCache.get(tenantId).getDefaultProfileConfiguration().getMaxDebugModeDurationMinutes(), defaultDebugDurationMinutes));
} }
protected <E extends HasId<?> & HasTenantId & HasName> void uniquifyEntityName(E entity, E oldEntity, Consumer<String> setName, EntityType entityType, NameConflictStrategy nameConflictStrategy) { protected <E extends HasId<?> & HasTenantId & HasName> void uniquifyEntityName(E entity, E oldEntity, Consumer<String> setName, EntityType entityType, NameConflictStrategy strategy) {
Dao<?> dao = entityDaoRegistry.getDao(entityType); Dao<?> dao = entityDaoRegistry.getDao(entityType);
EntityInfo existingEntity = dao.findEntityInfoByName(entity.getTenantId(), entity.getName()); List<EntityInfo> existingEntities = dao.findEntityInfosByNamePrefix(entity.getTenantId(), entity.getName());
if (existingEntity != null && (oldEntity == null || !existingEntity.getId().equals(oldEntity.getId()))) { Set<String> existingNames = existingEntities.stream()
String suffix = StringUtils.randomAlphanumeric(6); .filter(e -> (oldEntity == null || !e.getId().equals(oldEntity.getId())))
.map(EntityInfo::getName)
.collect(Collectors.toSet());
if (!existingNames.isEmpty()) {
int idx = 1;
String suffix = (strategy.uniquifyStrategy() == RANDOM) ? StringUtils.randomAlphanumeric(6) : String.valueOf(idx);
while (true) { while (true) {
String newName = entity.getName() + nameConflictStrategy.separator() + suffix; String newName = entity.getName() + strategy.separator() + suffix;
if (dao.findEntityInfoByName(entity.getTenantId(), newName) == null) { if (!existingNames.contains(newName)) {
setName.accept(newName); setName.accept(newName);
break; break;
} }
suffix = StringUtils.randomAlphanumeric(6); suffix = (strategy.uniquifyStrategy() == RANDOM) ? StringUtils.randomAlphanumeric(6) : String.valueOf(idx++);
} }
} }
} }

View File

@ -124,16 +124,14 @@ public class EntityViewServiceImpl extends CachedVersionedEntityService<EntityVi
private EntityView saveEntityView(EntityView entityView, boolean doValidate, NameConflictStrategy nameConflictStrategy) { private EntityView saveEntityView(EntityView entityView, boolean doValidate, NameConflictStrategy nameConflictStrategy) {
log.trace("Executing save entity view [{}]", entityView); log.trace("Executing save entity view [{}]", entityView);
EntityView old = null; EntityView old = (entityView.getId() != null) ? findEntityViewById(entityView.getTenantId(), entityView.getId(), false) : null;
if (nameConflictStrategy.policy() == NameConflictPolicy.UNIQUIFY) {
uniquifyEntityName(entityView, old, entityView::setName, EntityType.ENTITY_VIEW, nameConflictStrategy);
}
if (doValidate) { if (doValidate) {
old = entityViewValidator.validate(entityView, EntityView::getTenantId); entityViewValidator.validate(entityView, EntityView::getTenantId);
} else if (entityView.getId() != null) {
old = findEntityViewById(entityView.getTenantId(), entityView.getId(), false);
} }
try { try {
if (nameConflictStrategy.policy() == NameConflictPolicy.UNIQUIFY) {
uniquifyEntityName(entityView, old, entityView::setName, EntityType.ENTITY_VIEW, nameConflictStrategy);
}
EntityView saved = entityViewDao.save(entityView.getTenantId(), entityView); 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, saved)); publishEvictEvent(new EntityViewEvictEvent(saved.getTenantId(), saved.getId(), saved.getEntityId(), old != null ? old.getEntityId() : null, saved.getName(), old != null ? old.getName() : null, saved));
eventPublisher.publishEvent(SaveEntityEvent.builder().tenantId(saved.getTenantId()) eventPublisher.publishEvent(SaveEntityEvent.builder().tenantId(saved.getTenantId())

View File

@ -37,6 +37,14 @@ public class EntityViewDataValidator extends DataValidator<EntityView> {
private final TenantService tenantService; private final TenantService tenantService;
private final CustomerDao customerDao; private final CustomerDao customerDao;
@Override
protected void validateCreate(TenantId tenantId, EntityView entityView) {
entityViewDao.findEntityViewByTenantIdAndName(entityView.getTenantId().getId(), entityView.getName())
.ifPresent(e -> {
throw new DataValidationException("Entity view with such name already exists!");
});
}
@Override @Override
protected EntityView validateUpdate(TenantId tenantId, EntityView entityView) { protected EntityView validateUpdate(TenantId tenantId, EntityView entityView) {
var opt = entityViewDao.findEntityViewByTenantIdAndName(entityView.getTenantId().getId(), entityView.getName()); var opt = entityViewDao.findEntityViewByTenantIdAndName(entityView.getTenantId().getId(), entityView.getName());

View File

@ -105,8 +105,8 @@ public interface AssetRepository extends JpaRepository<AssetEntity, UUID>, Expor
AssetEntity findByTenantIdAndName(UUID tenantId, String name); AssetEntity findByTenantIdAndName(UUID tenantId, String name);
@Query("SELECT new org.thingsboard.server.common.data.EntityInfo(a.id, 'ASSET', a.name) " + @Query("SELECT new org.thingsboard.server.common.data.EntityInfo(a.id, 'ASSET', a.name) " +
"FROM AssetEntity a WHERE a.tenantId = :tenantId AND a.name = :name") "FROM AssetEntity a WHERE a.tenantId = :tenantId AND a.name LIKE CONCAT(:prefix, '%')")
EntityInfo findEntityInfoByName(UUID tenantId, String name); List<EntityInfo> findEntityInfosByNamePrefix(UUID tenantId, String prefix);
@Query("SELECT a FROM AssetEntity a WHERE a.tenantId = :tenantId " + @Query("SELECT a FROM AssetEntity a WHERE a.tenantId = :tenantId " +
"AND a.type = :type " + "AND a.type = :type " +

View File

@ -269,9 +269,9 @@ public class JpaAssetDao extends JpaAbstractDao<AssetEntity, Asset> implements A
} }
@Override @Override
public EntityInfo findEntityInfoByName(TenantId tenantId, String name) { public List<EntityInfo> findEntityInfosByNamePrefix(TenantId tenantId, String name) {
log.debug("Find asset entity info by name [{}]", name); log.debug("Find asset entity infos by name [{}]", name);
return assetRepository.findEntityInfoByName(tenantId.getId(), name); return assetRepository.findEntityInfosByNamePrefix(tenantId.getId(), name);
} }
@Override @Override

View File

@ -43,8 +43,8 @@ public interface CustomerRepository extends JpaRepository<CustomerEntity, UUID>,
CustomerEntity findByTenantIdAndTitle(UUID tenantId, String title); CustomerEntity findByTenantIdAndTitle(UUID tenantId, String title);
@Query("SELECT new org.thingsboard.server.common.data.EntityInfo(a.id, 'CUSTOMER', a.title) " + @Query("SELECT new org.thingsboard.server.common.data.EntityInfo(a.id, 'CUSTOMER', a.title) " +
"FROM CustomerEntity a WHERE a.tenantId = :tenantId AND a.title = :name") "FROM CustomerEntity a WHERE a.tenantId = :tenantId AND a.title LIKE CONCAT(:prefix, '%')")
EntityInfo findEntityInfoByName(UUID tenantId, String name); List<EntityInfo> findEntityInfosByNamePrefix(UUID tenantId, String prefix);
@Query(value = "SELECT * FROM customer c WHERE c.tenant_id = :tenantId " + @Query(value = "SELECT * FROM customer c WHERE c.tenant_id = :tenantId " +
"AND c.is_public IS TRUE ORDER BY c.id ASC LIMIT 1", nativeQuery = true) "AND c.is_public IS TRUE ORDER BY c.id ASC LIMIT 1", nativeQuery = true)

View File

@ -119,8 +119,8 @@ public class JpaCustomerDao extends JpaAbstractDao<CustomerEntity, Customer> imp
} }
@Override @Override
public EntityInfo findEntityInfoByName(TenantId tenantId, String name) { public List<EntityInfo> findEntityInfosByNamePrefix(TenantId tenantId, String name) {
return customerRepository.findEntityInfoByName(tenantId.getId(), name); return customerRepository.findEntityInfosByNamePrefix(tenantId.getId(), name);
} }
@Override @Override

View File

@ -153,8 +153,8 @@ public interface DeviceRepository extends JpaRepository<DeviceEntity, UUID>, Exp
DeviceEntity findByTenantIdAndName(UUID tenantId, String name); DeviceEntity findByTenantIdAndName(UUID tenantId, String name);
@Query("SELECT new org.thingsboard.server.common.data.EntityInfo(a.id, 'DEVICE', a.name) " + @Query("SELECT new org.thingsboard.server.common.data.EntityInfo(a.id, 'DEVICE', a.name) " +
"FROM DeviceEntity a WHERE a.tenantId = :tenantId AND a.name = :name") "FROM DeviceEntity a WHERE a.tenantId = :tenantId AND a.name LIKE CONCAT(:prefix, '%')")
EntityInfo findEntityInfoByName(UUID tenantId, String name); List<EntityInfo> findEntityInfosByNamePrefix(UUID tenantId, String prefix);
List<DeviceEntity> findDevicesByTenantIdAndCustomerIdAndIdIn(UUID tenantId, UUID customerId, List<UUID> deviceIds); List<DeviceEntity> findDevicesByTenantIdAndCustomerIdAndIdIn(UUID tenantId, UUID customerId, List<UUID> deviceIds);

View File

@ -116,8 +116,8 @@ public class JpaDeviceDao extends JpaAbstractDao<DeviceEntity, Device> implement
} }
@Override @Override
public EntityInfo findEntityInfoByName(TenantId tenantId, String name) { public List<EntityInfo> findEntityInfosByNamePrefix(TenantId tenantId, String name) {
return deviceRepository.findEntityInfoByName(tenantId.getId(), name); return deviceRepository.findEntityInfosByNamePrefix(tenantId.getId(), name);
} }
@Override @Override

View File

@ -120,8 +120,8 @@ public interface EntityViewRepository extends JpaRepository<EntityViewEntity, UU
EntityViewEntity findByTenantIdAndName(UUID tenantId, String name); EntityViewEntity findByTenantIdAndName(UUID tenantId, String name);
@Query("SELECT new org.thingsboard.server.common.data.EntityInfo(a.id, 'ENTITY_VIEW', a.name) " + @Query("SELECT new org.thingsboard.server.common.data.EntityInfo(a.id, 'ENTITY_VIEW', a.name) " +
"FROM EntityViewEntity a WHERE a.tenantId = :tenantId AND a.name = :name") "FROM EntityViewEntity a WHERE a.tenantId = :tenantId AND a.name LIKE CONCAT(:prefix, '%')")
EntityInfo findEntityInfoByName(UUID tenantId, String name); List<EntityInfo> findEntityInfosByNamePrefix(UUID tenantId, String prefix);
List<EntityViewEntity> findAllByTenantIdAndEntityId(UUID tenantId, UUID entityId); List<EntityViewEntity> findAllByTenantIdAndEntityId(UUID tenantId, UUID entityId);

View File

@ -232,8 +232,8 @@ public class JpaEntityViewDao extends JpaAbstractDao<EntityViewEntity, EntityVie
} }
@Override @Override
public EntityInfo findEntityInfoByName(TenantId tenantId, String name) { public List<EntityInfo> findEntityInfosByNamePrefix(TenantId tenantId, String name) {
return entityViewRepository.findEntityInfoByName(tenantId.getId(), name); return entityViewRepository.findEntityInfosByNamePrefix(tenantId.getId(), name);
} }
@Override @Override

View File

@ -551,7 +551,6 @@ CREATE TABLE IF NOT EXISTS entity_view (
additional_info varchar, additional_info varchar,
external_id uuid, external_id uuid,
version BIGINT DEFAULT 1, version BIGINT DEFAULT 1,
CONSTRAINT entity_view_name_unq_key UNIQUE (tenant_id, name),
CONSTRAINT entity_view_external_id_unq_key UNIQUE (tenant_id, external_id) CONSTRAINT entity_view_external_id_unq_key UNIQUE (tenant_id, external_id)
); );