Browse Source

AI rule node: REST API for CRUD operations on AI settings

pull/13371/head
Dmytro Skarzhynets 1 year ago
parent
commit
1a3b1c3fe4
No known key found for this signature in database GPG Key ID: 2B51652F224037DF
  1. 123
      application/src/main/java/org/thingsboard/server/controller/AiSettingsController.java
  2. 13
      application/src/main/java/org/thingsboard/server/controller/BaseController.java
  3. 3
      application/src/main/java/org/thingsboard/server/controller/ControllerConstants.java
  4. 3
      application/src/main/java/org/thingsboard/server/service/security/permission/Resource.java
  5. 17
      application/src/main/java/org/thingsboard/server/service/security/permission/TenantAdminPermissions.java
  6. 2
      common/dao-api/src/main/java/org/thingsboard/server/dao/ai/AiSettingsService.java
  7. 6
      common/data/src/main/java/org/thingsboard/server/common/data/ai/AiSettings.java
  8. 8
      dao/src/main/java/org/thingsboard/server/dao/ai/AiSettingsServiceImpl.java
  9. 14
      dao/src/main/java/org/thingsboard/server/dao/sql/ai/AiSettingsRepository.java
  10. 15
      dao/src/main/java/org/thingsboard/server/dao/sql/ai/JpaAiSettingsDao.java

123
application/src/main/java/org/thingsboard/server/controller/AiSettingsController.java

@ -15,66 +15,135 @@
*/ */
package org.thingsboard.server.controller; package org.thingsboard.server.controller;
import lombok.RequiredArgsConstructor; import io.swagger.v3.oas.annotations.Parameter;
import org.springframework.security.core.annotation.AuthenticationPrincipal; import io.swagger.v3.oas.annotations.media.Schema;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import org.thingsboard.server.common.data.ai.AiSettings; import org.thingsboard.server.common.data.ai.AiSettings;
import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.AiSettingsId; import org.thingsboard.server.common.data.id.AiSettingsId;
import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.config.annotations.ApiOperation;
import org.thingsboard.server.dao.ai.AiSettingsService; import org.thingsboard.server.service.security.permission.Operation;
import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.security.permission.Resource;
import java.util.Optional;
import java.util.Set;
import java.util.UUID; import java.util.UUID;
// TODO: TbAiSettingsService? import static org.thingsboard.server.controller.ControllerConstants.AI_SETTINGS_TEXT_SEARCH_DESCRIPTION;
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_SIZE_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.SORT_ORDER_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.SORT_PROPERTY_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.TENANT_AUTHORITY_PARAGRAPH;
@RestController @RestController
@RequiredArgsConstructor
@RequestMapping("/api/ai-settings") @RequestMapping("/api/ai-settings")
public class AiSettingsController extends BaseController { public class AiSettingsController extends BaseController {
private final AiSettingsService aiSettingsService; private static final Set<String> ALLOWED_SORT_PROPERTIES = Set.of("createdTime", "name", "provider", "model");
@ApiOperation(
value = "Create or update AI settings (saveAiSettings)",
notes = "Creates or updates an AI settings record.\n\n" +
"• **Create:** Omit the `id` to create a new record. The platform assigns a UUID to the new settings and returns it in the `id` field of the response.\n\n" +
"• **Update:** Include an existing `id` to modify that record. If no matching record exists, the API responds with **404 Not Found**.\n\n" +
"Tenant ID for the AI settings will be taken from the authenticated user making the request, regardless of any value provided in the request body." +
TENANT_AUTHORITY_PARAGRAPH
)
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@PostMapping @PostMapping
public AiSettings saveAiSettings( public AiSettings saveAiSettings(@RequestBody AiSettings aiSettings) throws ThingsboardException {
@RequestBody AiSettings aiSettings, aiSettings.setTenantId(getTenantId());
checkEntity(aiSettings.getId(), aiSettings, Resource.AI_SETTINGS);
@AuthenticationPrincipal SecurityUser requestingUser return aiSettingsService.save(aiSettings);
) {
return aiSettingsService.save(requestingUser.getTenantId(), aiSettings);
} }
@ApiOperation(
value = "Get AI settings by ID (getAiSettingsById)",
notes = "Fetches an AI settings record by its `id`." +
TENANT_AUTHORITY_PARAGRAPH
)
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@GetMapping("/{aiSettingsId}") @GetMapping("/{aiSettingsId}")
public AiSettings getAiSettingsById( public AiSettings getAiSettingsById(
@PathVariable("aiSettingsId") UUID aiSettingsUuid, @Parameter(
description = "ID of the AI settings record",
@AuthenticationPrincipal SecurityUser requestingUser required = true,
example = "de7900d4-30e2-11f0-9cd2-0242ac120002"
)
@PathVariable("aiSettingsId") UUID aiSettingsUuid
) throws ThingsboardException { ) throws ThingsboardException {
return checkNotNull(aiSettingsService.findAiSettingsByTenantIdAndId(requestingUser.getTenantId(), new AiSettingsId(aiSettingsUuid))); return checkAiSettingsId(new AiSettingsId(aiSettingsUuid), Operation.READ);
} }
@ApiOperation(
value = "Get AI settings (getAiSettings)",
notes = "Returns a page of AI settings. " +
PAGE_DATA_PARAMETERS + TENANT_AUTHORITY_PARAGRAPH
)
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@GetMapping @GetMapping
public PageData<AiSettings> getAllAiSettings( public PageData<AiSettings> getAiSettings(
@AuthenticationPrincipal SecurityUser requestingUser @Parameter(description = PAGE_SIZE_DESCRIPTION, required = true)
) { @RequestParam int pageSize,
return aiSettingsService.findAiSettingsByTenantId(requestingUser.getTenantId(), new PageLink(Integer.MAX_VALUE)); @Parameter(description = PAGE_NUMBER_DESCRIPTION, required = true)
@RequestParam int page,
@Parameter(description = AI_SETTINGS_TEXT_SEARCH_DESCRIPTION)
@RequestParam(required = false) String textSearch,
@Parameter(description = SORT_PROPERTY_DESCRIPTION, schema = @Schema(allowableValues = {"createdTime", "name", "provider", "model"}))
@RequestParam(required = false) String sortProperty,
@Parameter(description = SORT_ORDER_DESCRIPTION, schema = @Schema(allowableValues = {"ASC", "DESC"}))
@RequestParam(required = false) String sortOrder
) throws ThingsboardException {
var user = getCurrentUser();
accessControlService.checkPermission(user, Resource.AI_SETTINGS, Operation.READ);
validateSortProperty(sortProperty);
var pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
return aiSettingsService.findAiSettingsByTenantId(user.getTenantId(), pageLink);
} }
private static void validateSortProperty(String sortProperty) {
if (sortProperty != null && !ALLOWED_SORT_PROPERTIES.contains(sortProperty)) {
throw new IllegalArgumentException("Unsupported sort property '" + sortProperty + "'! Only '" + String.join("', '", ALLOWED_SORT_PROPERTIES) + "' are allowed.");
}
}
@ApiOperation(
value = "Delete AI settings by ID (deleteAiSettingsById)",
notes = "Deletes the AI settings record by its `id`. " +
"If a record with the specified `id` exists, the record is deleted and the endpoint returns `true`. " +
"If no such record exists, the endpoint returns `false`." +
TENANT_AUTHORITY_PARAGRAPH
)
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@DeleteMapping("/{aiSettingsId}") @DeleteMapping("/{aiSettingsId}")
public boolean deleteAiSettingsById( public boolean deleteAiSettingsById(
@PathVariable("aiSettingsId") UUID aiSettingsUuid, @Parameter(
description = "ID of the AI settings record",
@AuthenticationPrincipal SecurityUser requestingUser required = true,
) { example = "de7900d4-30e2-11f0-9cd2-0242ac120002"
return aiSettingsService.deleteByTenantIdAndId(requestingUser.getTenantId(), new AiSettingsId(aiSettingsUuid)); )
@PathVariable("aiSettingsId") UUID aiSettingsUuid
) throws ThingsboardException {
var user = getCurrentUser();
var aiSettingsId = new AiSettingsId(aiSettingsUuid);
accessControlService.checkPermission(user, Resource.AI_SETTINGS, Operation.DELETE);
Optional<AiSettings> aiSettingsOpt = aiSettingsService.findAiSettingsByTenantIdAndId(user.getTenantId(), aiSettingsId);
if (aiSettingsOpt.isEmpty()) {
return false;
}
accessControlService.checkPermission(user, Resource.AI_SETTINGS, Operation.DELETE, aiSettingsId, aiSettingsOpt.get());
return aiSettingsService.deleteByTenantIdAndId(user.getTenantId(), aiSettingsId);
} }
} }

13
application/src/main/java/org/thingsboard/server/controller/BaseController.java

@ -63,6 +63,7 @@ import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.TenantInfo; import org.thingsboard.server.common.data.TenantInfo;
import org.thingsboard.server.common.data.TenantProfile; import org.thingsboard.server.common.data.TenantProfile;
import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.ai.AiSettings;
import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.alarm.Alarm;
import org.thingsboard.server.common.data.alarm.AlarmComment; import org.thingsboard.server.common.data.alarm.AlarmComment;
import org.thingsboard.server.common.data.alarm.AlarmInfo; import org.thingsboard.server.common.data.alarm.AlarmInfo;
@ -77,6 +78,7 @@ import org.thingsboard.server.common.data.edge.EdgeInfo;
import org.thingsboard.server.common.data.exception.EntityVersionMismatchException; import org.thingsboard.server.common.data.exception.EntityVersionMismatchException;
import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.AiSettingsId;
import org.thingsboard.server.common.data.id.AlarmCommentId; import org.thingsboard.server.common.data.id.AlarmCommentId;
import org.thingsboard.server.common.data.id.AlarmId; import org.thingsboard.server.common.data.id.AlarmId;
import org.thingsboard.server.common.data.id.AssetId; import org.thingsboard.server.common.data.id.AssetId;
@ -129,6 +131,7 @@ import org.thingsboard.server.common.data.util.ThrowingBiFunction;
import org.thingsboard.server.common.data.widget.WidgetTypeDetails; import org.thingsboard.server.common.data.widget.WidgetTypeDetails;
import org.thingsboard.server.common.data.widget.WidgetTypeInfo; import org.thingsboard.server.common.data.widget.WidgetTypeInfo;
import org.thingsboard.server.common.data.widget.WidgetsBundle; import org.thingsboard.server.common.data.widget.WidgetsBundle;
import org.thingsboard.server.dao.ai.AiSettingsService;
import org.thingsboard.server.dao.alarm.AlarmCommentService; import org.thingsboard.server.dao.alarm.AlarmCommentService;
import org.thingsboard.server.dao.asset.AssetProfileService; import org.thingsboard.server.dao.asset.AssetProfileService;
import org.thingsboard.server.dao.asset.AssetService; import org.thingsboard.server.dao.asset.AssetService;
@ -373,6 +376,9 @@ public abstract class BaseController {
@Autowired @Autowired
protected CalculatedFieldService calculatedFieldService; protected CalculatedFieldService calculatedFieldService;
@Autowired
protected AiSettingsService aiSettingsService;
@Value("${server.log_controller_error_stack_trace}") @Value("${server.log_controller_error_stack_trace}")
@Getter @Getter
private boolean logControllerErrorStackTrace; private boolean logControllerErrorStackTrace;
@ -681,6 +687,9 @@ public abstract class BaseController {
case CALCULATED_FIELD: case CALCULATED_FIELD:
checkCalculatedFieldId(new CalculatedFieldId(entityId.getId()), operation); checkCalculatedFieldId(new CalculatedFieldId(entityId.getId()), operation);
return; return;
case AI_SETTINGS:
checkAiSettingsId(new AiSettingsId(entityId.getId()), operation);
return;
default: default:
checkEntityId(entityId, entitiesService::findEntityByTenantIdAndId, operation); checkEntityId(entityId, entitiesService::findEntityByTenantIdAndId, operation);
} }
@ -881,6 +890,10 @@ public abstract class BaseController {
return checkEntityId(notificationTargetId, notificationTargetService::findNotificationTargetById, operation); return checkEntityId(notificationTargetId, notificationTargetService::findNotificationTargetById, operation);
} }
AiSettings checkAiSettingsId(AiSettingsId aiSettingsId, Operation operation) throws ThingsboardException {
return checkEntityId(aiSettingsId, (tenantId, id) -> aiSettingsService.findAiSettingsByTenantIdAndId(tenantId, id).orElse(null), operation);
}
protected <I extends EntityId> I emptyId(EntityType entityType) { protected <I extends EntityId> I emptyId(EntityType entityType) {
return (I) EntityIdFactory.getByTypeAndUuid(entityType, ModelConstants.NULL_UUID); return (I) EntityIdFactory.getByTypeAndUuid(entityType, ModelConstants.NULL_UUID);
} }

3
application/src/main/java/org/thingsboard/server/controller/ControllerConstants.java

@ -31,7 +31,7 @@ public class ControllerConstants {
protected static final String ASSIGNEE_ID = "assigneeId"; protected static final String ASSIGNEE_ID = "assigneeId";
protected static final String PAGE_DATA_PARAMETERS = "You can specify parameters to filter the results. " + protected static final String PAGE_DATA_PARAMETERS = "You can specify parameters to filter the results. " +
"The result is wrapped with PageData object that allows you to iterate over result set using pagination. " + "The result is wrapped with PageData object that allows you to iterate over result set using pagination. " +
"See the 'Model' tab of the Response Class for more details. "; "See response schema for more details. ";
protected static final String INLINE_IMAGES = "inlineImages"; protected static final String INLINE_IMAGES = "inlineImages";
protected static final String INLINE_IMAGES_DESCRIPTION = "Inline images as a data URL (Base64)"; protected static final String INLINE_IMAGES_DESCRIPTION = "Inline images as a data URL (Base64)";
@ -90,6 +90,7 @@ public class ControllerConstants {
protected static final String TENANT_PROFILE_TEXT_SEARCH_DESCRIPTION = "The case insensitive 'substring' filter based on the tenant profile name."; protected static final String TENANT_PROFILE_TEXT_SEARCH_DESCRIPTION = "The case insensitive 'substring' filter based on the tenant profile name.";
protected static final String RULE_CHAIN_TEXT_SEARCH_DESCRIPTION = "The case insensitive 'substring' filter based on the rule chain name."; protected static final String RULE_CHAIN_TEXT_SEARCH_DESCRIPTION = "The case insensitive 'substring' filter based on the rule chain name.";
protected static final String DEVICE_PROFILE_TEXT_SEARCH_DESCRIPTION = "The case insensitive 'substring' filter based on the device profile name."; protected static final String DEVICE_PROFILE_TEXT_SEARCH_DESCRIPTION = "The case insensitive 'substring' filter based on the device profile name.";
protected static final String AI_SETTINGS_TEXT_SEARCH_DESCRIPTION = "The case insensitive 'substring' filter based on the AI settings name";
protected static final String ASSET_PROFILE_TEXT_SEARCH_DESCRIPTION = "The case insensitive 'substring' filter based on the asset profile name."; protected static final String ASSET_PROFILE_TEXT_SEARCH_DESCRIPTION = "The case insensitive 'substring' filter based on the asset profile name.";
protected static final String CUSTOMER_TEXT_SEARCH_DESCRIPTION = "The case insensitive 'substring' filter based on the customer title."; protected static final String CUSTOMER_TEXT_SEARCH_DESCRIPTION = "The case insensitive 'substring' filter based on the customer title.";

3
application/src/main/java/org/thingsboard/server/service/security/permission/Resource.java

@ -51,7 +51,8 @@ public enum Resource {
NOTIFICATION(EntityType.NOTIFICATION_TARGET, EntityType.NOTIFICATION_TEMPLATE, NOTIFICATION(EntityType.NOTIFICATION_TARGET, EntityType.NOTIFICATION_TEMPLATE,
EntityType.NOTIFICATION_REQUEST, EntityType.NOTIFICATION_RULE), EntityType.NOTIFICATION_REQUEST, EntityType.NOTIFICATION_RULE),
MOBILE_APP_SETTINGS, MOBILE_APP_SETTINGS,
CALCULATED_FIELD(EntityType.CALCULATED_FIELD); CALCULATED_FIELD(EntityType.CALCULATED_FIELD),
AI_SETTINGS(EntityType.AI_SETTINGS);
private final Set<EntityType> entityTypes; private final Set<EntityType> entityTypes;

17
application/src/main/java/org/thingsboard/server/service/security/permission/TenantAdminPermissions.java

@ -18,6 +18,8 @@ package org.thingsboard.server.service.security.permission;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.HasTenantId; import org.thingsboard.server.common.data.HasTenantId;
import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.ai.AiSettings;
import org.thingsboard.server.common.data.id.AiSettingsId;
import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.id.UserId;
import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.common.data.security.Authority;
@ -56,6 +58,7 @@ public class TenantAdminPermissions extends AbstractPermissions {
put(Resource.MOBILE_APP, tenantEntityPermissionChecker); put(Resource.MOBILE_APP, tenantEntityPermissionChecker);
put(Resource.MOBILE_APP_BUNDLE, tenantEntityPermissionChecker); put(Resource.MOBILE_APP_BUNDLE, tenantEntityPermissionChecker);
put(Resource.CALCULATED_FIELD, tenantEntityPermissionChecker); put(Resource.CALCULATED_FIELD, tenantEntityPermissionChecker);
put(Resource.AI_SETTINGS, aiSettingsPermissionChecker);
} }
public static final PermissionChecker tenantEntityPermissionChecker = new PermissionChecker() { public static final PermissionChecker tenantEntityPermissionChecker = new PermissionChecker() {
@ -146,4 +149,18 @@ public class TenantAdminPermissions extends AbstractPermissions {
}; };
private static final PermissionChecker<AiSettingsId, AiSettings> aiSettingsPermissionChecker = new PermissionChecker<>() {
@Override
public boolean hasPermission(SecurityUser user, Operation operation) {
return true;
}
@Override
public boolean hasPermission(SecurityUser user, Operation operation, AiSettingsId entityId, AiSettings entity) {
return user.getTenantId().equals(entity.getTenantId());
}
};
} }

2
common/dao-api/src/main/java/org/thingsboard/server/dao/ai/AiSettingsService.java

@ -26,7 +26,7 @@ import java.util.Optional;
public interface AiSettingsService extends EntityDaoService { public interface AiSettingsService extends EntityDaoService {
AiSettings save(TenantId tenantId, AiSettings aiSettings); AiSettings save(AiSettings aiSettings);
Optional<AiSettings> findAiSettingsById(TenantId tenantId, AiSettingsId aiSettingsId); Optional<AiSettings> findAiSettingsById(TenantId tenantId, AiSettingsId aiSettingsId);

6
common/data/src/main/java/org/thingsboard/server/common/data/ai/AiSettings.java

@ -47,7 +47,7 @@ public final class AiSettings extends BaseData<AiSettingsId> implements HasTenan
TenantId tenantId; TenantId tenantId;
@Schema( @Schema(
requiredMode = Schema.RequiredMode.NOT_REQUIRED, requiredMode = Schema.RequiredMode.REQUIRED,
accessMode = Schema.AccessMode.READ_ONLY, accessMode = Schema.AccessMode.READ_ONLY,
description = "Version of the AI settings; increments automatically whenever the settings are changed", description = "Version of the AI settings; increments automatically whenever the settings are changed",
example = "7", example = "7",
@ -66,7 +66,7 @@ public final class AiSettings extends BaseData<AiSettingsId> implements HasTenan
@Schema( @Schema(
requiredMode = Schema.RequiredMode.REQUIRED, requiredMode = Schema.RequiredMode.REQUIRED,
accessMode = Schema.AccessMode.READ_WRITE, accessMode = Schema.AccessMode.READ_WRITE,
description = "Name of the LLM provider, e.g. 'openai', 'anthropic'", description = "Name of the LLM provider",
example = "openai" example = "openai"
) )
String provider; String provider;
@ -74,7 +74,7 @@ public final class AiSettings extends BaseData<AiSettingsId> implements HasTenan
@Schema( @Schema(
requiredMode = Schema.RequiredMode.REQUIRED, requiredMode = Schema.RequiredMode.REQUIRED,
accessMode = Schema.AccessMode.READ_WRITE, accessMode = Schema.AccessMode.READ_WRITE,
description = "Identifier of the LLM model to use, e.g. 'gpt-4o-mini'", description = "Identifier of the LLM model to use",
example = "gpt-4o-mini" example = "gpt-4o-mini"
) )
String model; String model;

8
dao/src/main/java/org/thingsboard/server/dao/ai/AiSettingsServiceImpl.java

@ -28,6 +28,8 @@ import org.thingsboard.server.common.data.page.PageLink;
import java.util.Optional; import java.util.Optional;
import static org.thingsboard.server.dao.service.Validator.validatePageLink;
@Service @Service
@RequiredArgsConstructor @RequiredArgsConstructor
class AiSettingsServiceImpl implements AiSettingsService { class AiSettingsServiceImpl implements AiSettingsService {
@ -35,9 +37,8 @@ class AiSettingsServiceImpl implements AiSettingsService {
private final AiSettingsDao aiSettingsDao; private final AiSettingsDao aiSettingsDao;
@Override @Override
public AiSettings save(TenantId tenantId, AiSettings aiSettings) { public AiSettings save(AiSettings aiSettings) {
aiSettings.setTenantId(tenantId); return aiSettingsDao.saveAndFlush(aiSettings.getTenantId(), aiSettings);
return aiSettingsDao.saveAndFlush(tenantId, aiSettings);
} }
@Override @Override
@ -47,6 +48,7 @@ class AiSettingsServiceImpl implements AiSettingsService {
@Override @Override
public PageData<AiSettings> findAiSettingsByTenantId(TenantId tenantId, PageLink pageLink) { public PageData<AiSettings> findAiSettingsByTenantId(TenantId tenantId, PageLink pageLink) {
validatePageLink(pageLink);
return aiSettingsDao.findAllByTenantId(tenantId, pageLink); return aiSettingsDao.findAllByTenantId(tenantId, pageLink);
} }

14
dao/src/main/java/org/thingsboard/server/dao/sql/ai/AiSettingsRepository.java

@ -15,23 +15,25 @@
*/ */
package org.thingsboard.server.dao.sql.ai; package org.thingsboard.server.dao.sql.ai;
import org.springframework.data.domain.Limit;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query; import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param; import org.springframework.data.repository.query.Param;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.thingsboard.server.common.data.edqs.fields.AiSettingsFields;
import org.thingsboard.server.dao.model.sql.AiSettingsEntity; import org.thingsboard.server.dao.model.sql.AiSettingsEntity;
import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.UUID; import java.util.UUID;
public interface AiSettingsRepository extends JpaRepository<AiSettingsEntity, UUID> { public interface AiSettingsRepository extends JpaRepository<AiSettingsEntity, UUID> {
Page<AiSettingsEntity> findByTenantId(UUID tenantId, Pageable pageable); @Query("SELECT ai " +
"FROM AiSettingsEntity ai " +
"WHERE ai.tenantId = :tenantId " +
"AND (:textSearch IS NULL OR ilike(ai.name, CONCAT('%', :textSearch, '%')) = true)")
Page<AiSettingsEntity> findByTenantId(@Param("tenantId") UUID tenantId, @Param("textSearch") String textSearch, Pageable pageable);
Optional<AiSettingsEntity> findByTenantIdAndId(UUID tenantId, UUID id); Optional<AiSettingsEntity> findByTenantIdAndId(UUID tenantId, UUID id);
@ -41,6 +43,8 @@ public interface AiSettingsRepository extends JpaRepository<AiSettingsEntity, UU
void deleteByTenantId(UUID tenantId); void deleteByTenantId(UUID tenantId);
@Transactional @Transactional
boolean deleteByTenantIdAndId(UUID tenantId, UUID id); @Modifying
@Query("DELETE FROM AiSettingsEntity ai WHERE ai.tenantId = :tenantId AND ai.id = :id")
int deleteByTenantIdAndId(@Param("tenantId") UUID tenantId, @Param("id") UUID id);
} }

15
dao/src/main/java/org/thingsboard/server/dao/sql/ai/JpaAiSettingsDao.java

@ -16,12 +16,11 @@
package org.thingsboard.server.dao.sql.ai; package org.thingsboard.server.dao.sql.ai;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Limit; import org.apache.commons.lang3.StringUtils;
import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.ai.AiSettings; import org.thingsboard.server.common.data.ai.AiSettings;
import org.thingsboard.server.common.data.edqs.fields.AiSettingsFields;
import org.thingsboard.server.common.data.id.AiSettingsId; import org.thingsboard.server.common.data.id.AiSettingsId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageData;
@ -32,7 +31,6 @@ import org.thingsboard.server.dao.model.sql.AiSettingsEntity;
import org.thingsboard.server.dao.sql.JpaAbstractDao; import org.thingsboard.server.dao.sql.JpaAbstractDao;
import org.thingsboard.server.dao.util.SqlDao; import org.thingsboard.server.dao.util.SqlDao;
import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.UUID; import java.util.UUID;
@ -48,14 +46,11 @@ class JpaAiSettingsDao extends JpaAbstractDao<AiSettingsEntity, AiSettings> impl
return aiSettingsRepository.findByTenantIdAndId(tenantId.getId(), aiSettingsId.getId()).map(DaoUtil::getData); return aiSettingsRepository.findByTenantIdAndId(tenantId.getId(), aiSettingsId.getId()).map(DaoUtil::getData);
} }
@Override
public List<AiSettingsFields> findNextBatch(UUID id, int batchSize) {
return aiSettingsRepository.findNextBatch(id, Limit.of(batchSize));
}
@Override @Override
public PageData<AiSettings> findAllByTenantId(TenantId tenantId, PageLink pageLink) { public PageData<AiSettings> findAllByTenantId(TenantId tenantId, PageLink pageLink) {
return DaoUtil.toPageData(aiSettingsRepository.findByTenantId(tenantId.getId(), DaoUtil.toPageable(pageLink))); return DaoUtil.toPageData(aiSettingsRepository.findByTenantId(
tenantId.getId(), StringUtils.defaultIfEmpty(pageLink.getTextSearch(), null), DaoUtil.toPageable(pageLink))
);
} }
@Override @Override
@ -70,7 +65,7 @@ class JpaAiSettingsDao extends JpaAbstractDao<AiSettingsEntity, AiSettings> impl
@Override @Override
public boolean deleteByTenantIdAndId(TenantId tenantId, AiSettingsId aiSettingsId) { public boolean deleteByTenantIdAndId(TenantId tenantId, AiSettingsId aiSettingsId) {
return aiSettingsRepository.deleteByTenantIdAndId(tenantId.getId(), aiSettingsId.getId()); return aiSettingsRepository.deleteByTenantIdAndId(tenantId.getId(), aiSettingsId.getId()) > 0;
} }
@Override @Override

Loading…
Cancel
Save