From 141bd74113f784002a6f1c11aac069c720ca81ac Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Mon, 2 Jan 2023 17:25:03 +0200 Subject: [PATCH] Notification target and template api docs; improvements --- .../controller/NotificationController.java | 23 +----- .../NotificationRuleController.java | 3 +- .../NotificationTargetController.java | 32 +++++++- .../NotificationTemplateController.java | 74 ++++++++++++++++++- .../controller/TwoFactorAuthConfigTest.java | 20 ++--- .../NotificationTemplateService.java | 7 +- .../server/common/data/EntityType.java | 2 +- .../server/common/data/id/NotificationId.java | 10 ++- ...ackDeliveryMethodNotificationTemplate.java | 1 - .../model/mfa/PlatformTwoFaSettings.java | 8 +- .../mfa/account/SmsTwoFaAccountConfig.java | 4 +- .../mfa/account/TotpTwoFaAccountConfig.java | 4 +- .../BackupCodeTwoFaProviderConfig.java | 2 +- .../provider/OtpBasedTwoFaProviderConfig.java | 2 +- .../mfa/provider/SmsTwoFaProviderConfig.java | 4 +- .../mfa/provider/TotpTwoFaProviderConfig.java | 2 +- .../org/thingsboard/server/dao/DaoUtil.java | 18 +++-- .../DefaultNotificationRuleService.java | 7 -- .../DefaultNotificationTemplateService.java | 7 ++ .../notification/NotificationTemplateDao.java | 6 ++ .../sql/notification/JpaNotificationDao.java | 6 ++ .../JpaNotificationRequestDao.java | 4 +- .../notification/JpaNotificationRuleDao.java | 4 +- .../JpaNotificationTargetDao.java | 4 +- .../JpaNotificationTemplateDao.java | 13 ++++ .../NotificationTemplateRepository.java | 12 +++ 26 files changed, 211 insertions(+), 68 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/controller/NotificationController.java b/application/src/main/java/org/thingsboard/server/controller/NotificationController.java index 3d6153afa0..5c8f8f5303 100644 --- a/application/src/main/java/org/thingsboard/server/controller/NotificationController.java +++ b/application/src/main/java/org/thingsboard/server/controller/NotificationController.java @@ -29,19 +29,15 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import org.thingsboard.rule.engine.api.NotificationManager; -import org.thingsboard.server.common.data.notification.template.SlackConversation; -import org.thingsboard.rule.engine.api.slack.SlackService; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.NotificationId; import org.thingsboard.server.common.data.id.NotificationRequestId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.notification.Notification; -import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod; import org.thingsboard.server.common.data.notification.NotificationOriginatorType; import org.thingsboard.server.common.data.notification.NotificationRequest; import org.thingsboard.server.common.data.notification.settings.NotificationSettings; -import org.thingsboard.server.common.data.notification.settings.SlackNotificationDeliveryMethodConfig; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.dao.notification.NotificationRequestService; @@ -52,7 +48,7 @@ import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.security.permission.Operation; import org.thingsboard.server.service.security.permission.Resource; -import java.util.List; +import javax.validation.Valid; import java.util.UUID; @RestController @@ -66,7 +62,6 @@ public class NotificationController extends BaseController { private final NotificationRequestService notificationRequestService; private final NotificationManager notificationManager; private final NotificationSettingsService notificationSettingsService; - private final SlackService slackService; @GetMapping("/notifications") @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @@ -93,7 +88,7 @@ public class NotificationController extends BaseController { @PostMapping("/notification/request") @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") - public NotificationRequest createNotificationRequest(@RequestBody NotificationRequest notificationRequest, + public NotificationRequest createNotificationRequest(@RequestBody @Valid NotificationRequest notificationRequest, @AuthenticationPrincipal SecurityUser user) throws Exception { if (notificationRequest.getId() != null) { throw new IllegalArgumentException("Notification request cannot be updated. You may only cancel/delete it"); @@ -157,18 +152,4 @@ public class NotificationController extends BaseController { return notificationSettingsService.findNotificationSettings(tenantId); } - @GetMapping("/notification/slack/conversations") - @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") - public List listSlackConversations(@RequestParam SlackConversation.Type type, - @AuthenticationPrincipal SecurityUser user) { - NotificationSettings settings = getNotificationSettings(user); - SlackNotificationDeliveryMethodConfig slackConfig = (SlackNotificationDeliveryMethodConfig) - settings.getDeliveryMethodsConfigs().get(NotificationDeliveryMethod.SLACK); - if (slackConfig == null) { - throw new IllegalArgumentException("Slack is not configured"); - } - - return slackService.listConversations(user.getTenantId(), slackConfig.getBotToken(), type); - } - } diff --git a/application/src/main/java/org/thingsboard/server/controller/NotificationRuleController.java b/application/src/main/java/org/thingsboard/server/controller/NotificationRuleController.java index 8d5a4bde34..73f2a35531 100644 --- a/application/src/main/java/org/thingsboard/server/controller/NotificationRuleController.java +++ b/application/src/main/java/org/thingsboard/server/controller/NotificationRuleController.java @@ -40,6 +40,7 @@ import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.security.permission.Operation; import org.thingsboard.server.service.security.permission.Resource; +import javax.validation.Valid; import java.util.UUID; @RestController @@ -53,7 +54,7 @@ public class NotificationRuleController extends BaseController { @PostMapping("/rule") @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") - public NotificationRule saveNotificationRule(@RequestBody NotificationRule notificationRule) throws Exception { + public NotificationRule saveNotificationRule(@RequestBody @Valid NotificationRule notificationRule) throws Exception { checkEntity(notificationRule.getId(), notificationRule, Resource.NOTIFICATION_RULE); return doSaveAndLog(EntityType.NOTIFICATION_RULE, notificationRule, notificationRuleService::saveNotificationRule); } diff --git a/application/src/main/java/org/thingsboard/server/controller/NotificationTargetController.java b/application/src/main/java/org/thingsboard/server/controller/NotificationTargetController.java index 00ac6f4a93..fd81ac7c17 100644 --- a/application/src/main/java/org/thingsboard/server/controller/NotificationTargetController.java +++ b/application/src/main/java/org/thingsboard/server/controller/NotificationTargetController.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.controller; +import io.swagger.annotations.ApiOperation; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.security.access.prepost.PreAuthorize; @@ -45,6 +46,8 @@ import org.thingsboard.server.service.security.permission.Resource; import javax.validation.Valid; import java.util.UUID; +import static org.thingsboard.server.controller.ControllerConstants.SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH; + @RestController @TbCoreComponent @RequestMapping("/api/notification") @@ -54,6 +57,20 @@ public class NotificationTargetController extends BaseController { private final NotificationTargetService notificationTargetService; + @ApiOperation(value = "Save notification target (saveNotificationTarget)", + notes = "Create or update notification target.\n\n" + + "Examples with different configuration types:\n" + + "- SINGLE_USER:\n" + + "```\n{\n \"name\": \"Me\",\n \"configuration\": {\n \"type\": \"SINGLE_USER\",\n \"userId\": \"ea31a460-3d85-11ed-9200-77fc04fa14fa\"\n }\n}\n```\n" + + "- USER_LIST:\n" + + "```\n{\n \"name\": \"Special users\",\n \"configuration\": {\n \"type\": \"USER_LIST\",\n \"usersIds\": [\n \"ea31a460-3d85-11ed-9200-77fc04fa14fa\",\n \"86f7b260-3d88-11ed-ad72-ad2ee0f70ba1\"\n ]\n }\n}\n```\n" + + "- CUSTOMER_USERS (not accessible to system administrator):\n" + + "```\n{\n \"name\": \"Users of my customer\",\n \"configuration\": {\n \"type\": \"CUSTOMER_USERS\",\n \"customerId\": \"ea31a460-3d85-11ed-9200-77fc04fa14fa\"\n }\n}\n```\n" + + "or if you would like to use the target in notification rule and get customerId from alarm:\n" + + "```\n{\n \"name\": \"Alarm's customer users\",\n \"configuration\": {\n \"type\": \"CUSTOMER_USERS\",\n \"customerId\": null,\n \"getCustomerIdFromOriginatorEntity\": true\n }\n}\n```\n" + + "- ALL_USERS:\n" + + "```\n{\n \"name\": \"All my users\",\n \"configuration\": {\n \"type\": \"ALL_USERS\"\n }\n}\n```\n\n" + + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH) @PostMapping("/target") @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") public NotificationTarget saveNotificationTarget(@RequestBody @Valid NotificationTarget notificationTarget, @@ -72,6 +89,9 @@ public class NotificationTargetController extends BaseController { return doSaveAndLog(EntityType.NOTIFICATION_TARGET, notificationTarget, notificationTargetService::saveNotificationTarget); } + @ApiOperation(value = "Get notification target by id (getNotificationTargetById)", + notes = "Fetch saved notification target by id." + + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH) @GetMapping("/target/{id}") @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") public NotificationTarget getNotificationTargetById(@PathVariable UUID id) throws ThingsboardException { @@ -79,6 +99,9 @@ public class NotificationTargetController extends BaseController { return checkEntityId(notificationTargetId, notificationTargetService::findNotificationTargetById, Operation.READ); } + @ApiOperation(value = "Get recipient for notification target config (getRecipientsForNotificationTargetConfig)", + notes = "Get the list (page) of recipients (users) for such notification target configuration." + + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH) @PostMapping("/target/recipients") @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") public PageData getRecipientsForNotificationTargetConfig(@RequestBody NotificationTarget notificationTarget, @@ -95,6 +118,9 @@ public class NotificationTargetController extends BaseController { return recipients; } + @ApiOperation(value = "Get notification targets (getNotificationTargets)", + notes = "Fetch the page of notification targets owned by sysadmin or tenant." + + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH) @GetMapping("/targets") @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") public PageData getNotificationTargets(@RequestParam int pageSize, @@ -107,9 +133,13 @@ public class NotificationTargetController extends BaseController { return notificationTargetService.findNotificationTargetsByTenantId(user.getTenantId(), pageLink); } + @ApiOperation(value = "Delete notification target by id (deleteNotificationTargetById)", + notes = "Delete notification target by its id.\n\n" + + "This target cannot be referenced by existing scheduled notification requests or any notification rules." + + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH) @DeleteMapping("/target/{id}") @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") - public void deleteNotificationTarget(@PathVariable UUID id) throws Exception { + public void deleteNotificationTargetById(@PathVariable UUID id) throws Exception { NotificationTargetId notificationTargetId = new NotificationTargetId(id); NotificationTarget notificationTarget = checkEntityId(notificationTargetId, notificationTargetService::findNotificationTargetById, Operation.DELETE); doDeleteAndLog(EntityType.NOTIFICATION_TARGET, notificationTarget, notificationTargetService::deleteNotificationTargetById); diff --git a/application/src/main/java/org/thingsboard/server/controller/NotificationTemplateController.java b/application/src/main/java/org/thingsboard/server/controller/NotificationTemplateController.java index 5194d57f84..dde554b08f 100644 --- a/application/src/main/java/org/thingsboard/server/controller/NotificationTemplateController.java +++ b/application/src/main/java/org/thingsboard/server/controller/NotificationTemplateController.java @@ -15,27 +15,42 @@ */ package org.thingsboard.server.controller; +import io.swagger.annotations.ApiOperation; import lombok.RequiredArgsConstructor; import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; +import org.thingsboard.rule.engine.api.slack.SlackService; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.NotificationTemplateId; +import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod; +import org.thingsboard.server.common.data.notification.settings.NotificationSettings; +import org.thingsboard.server.common.data.notification.settings.SlackNotificationDeliveryMethodConfig; import org.thingsboard.server.common.data.notification.template.NotificationTemplate; +import org.thingsboard.server.common.data.notification.template.SlackConversation; +import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.page.PageLink; +import org.thingsboard.server.dao.notification.NotificationSettingsService; import org.thingsboard.server.dao.notification.NotificationTemplateService; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.security.permission.Operation; import org.thingsboard.server.service.security.permission.Resource; import javax.validation.Valid; +import java.util.List; import java.util.UUID; +import static org.thingsboard.server.controller.ControllerConstants.SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH; + @RestController @TbCoreComponent @RequiredArgsConstructor @@ -43,7 +58,24 @@ import java.util.UUID; public class NotificationTemplateController extends BaseController { private final NotificationTemplateService notificationTemplateService; + private final NotificationSettingsService notificationSettingsService; + private final SlackService slackService; + @ApiOperation(value = "Save notification template (saveNotificationTemplate)", + notes = "Create or update notification template.\n\n" + + "Example:\n" + + "```\n{\n \"name\": \"Hello to all my users\",\n" + + " \"notificationType\": \"Message from administrator\",\n" + + " \"configuration\": {\n" + + " \"defaultTextTemplate\": \"Hello everyone\", # required if any of the templates' bodies is not set\n" + + " \"templates\": {\n" + + " \"WEBSOCKET\": {\n \"method\": \"WEBSOCKET\",\n \"body\": null # defaultTextTemplate will be used if body is not set\n },\n" + + " \"SMS\": {\n \"method\": \"SMS\",\n \"body\": null\n },\n" + + " \"EMAIL\": {\n \"method\": \"EMAIL\",\n \"body\": \"Non-default value for email notification: Hello everyone\",\n \"subject\": \"Message from administrator\"\n },\n" + + " \"SLACK\": {\n \"method\": \"SLACK\",\n \"body\": null,\n \"conversationType\": \"PUBLIC_CHANNEL\",\n \"conversationId\": \"U02LD7BJOU2\" # received from listSlackConversations API method\n }\n" + + " }\n" + + " }\n}\n```" + + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH) @PostMapping("/template") @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") public NotificationTemplate saveNotificationTemplate(@RequestBody @Valid NotificationTemplate notificationTemplate) throws Exception { @@ -51,6 +83,9 @@ public class NotificationTemplateController extends BaseController { return doSaveAndLog(EntityType.NOTIFICATION_TEMPLATE, notificationTemplate, notificationTemplateService::saveNotificationTemplate); } + @ApiOperation(value = "Get notification template by id (getNotificationTemplateById)", + notes = "Fetch notification template by id." + + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH) @GetMapping("/template/{id}") @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") public NotificationTemplate getNotificationTemplateById(@PathVariable UUID id) throws ThingsboardException { @@ -58,12 +93,49 @@ public class NotificationTemplateController extends BaseController { return checkEntityId(notificationTemplateId, notificationTemplateService::findNotificationTemplateById, Operation.READ); } + @ApiOperation(value = "Get notification templates (getNotificationTemplates)", + notes = "Fetch the page of notification templates owned by sysadmin or tenant." + + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH) + @GetMapping("/templates") + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") + public PageData getNotificationTemplates(@RequestParam int pageSize, + @RequestParam int page, + @RequestParam(required = false) String textSearch, + @RequestParam(required = false) String sortProperty, + @RequestParam(required = false) String sortOrder, + @AuthenticationPrincipal SecurityUser user) throws ThingsboardException { + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); + return notificationTemplateService.findNotificationTemplatesByTenantId(user.getTenantId(), pageLink); + } + + @ApiOperation(value = "Delete notification template by id (deleteNotificationTemplateById", + notes = "Delete notification template by its id.\n\n" + + "This template cannot be referenced by existing scheduled notification requests or any notification rules." + + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH) @DeleteMapping("/template/{id}") @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") - public void deleteNotificationTemplate(@PathVariable UUID id) throws Exception { + public void deleteNotificationTemplateById(@PathVariable UUID id) throws Exception { NotificationTemplateId notificationTemplateId = new NotificationTemplateId(id); NotificationTemplate notificationTemplate = checkEntityId(notificationTemplateId, notificationTemplateService::findNotificationTemplateById, Operation.DELETE); doDeleteAndLog(EntityType.NOTIFICATION_TEMPLATE, notificationTemplate, notificationTemplateService::deleteNotificationTemplateById); } + @ApiOperation(value = "List Slack conversations (listSlackConversations)", + notes = "List available Slack conversations by type to use in notification template.\n\n" + + "Slack must be configured in notification settings." + + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH) + @GetMapping("/slack/conversations") + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") + public List listSlackConversations(@RequestParam SlackConversation.Type type, + @AuthenticationPrincipal SecurityUser user) { + NotificationSettings settings = notificationSettingsService.findNotificationSettings(user.getTenantId()); + SlackNotificationDeliveryMethodConfig slackConfig = (SlackNotificationDeliveryMethodConfig) + settings.getDeliveryMethodsConfigs().get(NotificationDeliveryMethod.SLACK); + if (slackConfig == null) { + throw new IllegalArgumentException("Slack is not configured"); + } + + return slackService.listConversations(user.getTenantId(), slackConfig.getBotToken(), type); + } + } diff --git a/application/src/test/java/org/thingsboard/server/controller/TwoFactorAuthConfigTest.java b/application/src/test/java/org/thingsboard/server/controller/TwoFactorAuthConfigTest.java index db15e6fd82..9dd2249178 100644 --- a/application/src/test/java/org/thingsboard/server/controller/TwoFactorAuthConfigTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/TwoFactorAuthConfigTest.java @@ -124,9 +124,9 @@ public abstract class TwoFactorAuthConfigTest extends AbstractControllerTest { .andExpect(status().isBadRequest())); assertThat(errorMessage).contains( - "verification code check rate limit configuration is invalid", - "maximum number of verification failure before user lockout must be positive", - "total amount of time allotted for verification must be greater than or equal 60" + "verificationCodeCheckRateLimit is invalid", + "maxVerificationFailuresBeforeUserLockout must be positive", + "totalAllowedTimeForVerification must be greater than or equal to 60" ); } @@ -136,7 +136,7 @@ public abstract class TwoFactorAuthConfigTest extends AbstractControllerTest { invalidTotpTwoFaProviderConfig.setIssuerName(" "); String errorResponse = savePlatformTwoFaSettingsAndGetError(invalidTotpTwoFaProviderConfig); - assertThat(errorResponse).containsIgnoringCase("issuer name must not be blank"); + assertThat(errorResponse).containsIgnoringCase("issuerName must not be blank"); } @Test @@ -151,8 +151,8 @@ public abstract class TwoFactorAuthConfigTest extends AbstractControllerTest { invalidSmsTwoFaProviderConfig.setSmsVerificationMessageTemplate(null); invalidSmsTwoFaProviderConfig.setVerificationCodeLifetime(0); errorResponse = savePlatformTwoFaSettingsAndGetError(invalidSmsTwoFaProviderConfig); - assertThat(errorResponse).containsIgnoringCase("verification message template is required"); - assertThat(errorResponse).containsIgnoringCase("verification code lifetime is required"); + assertThat(errorResponse).containsIgnoringCase("smsVerificationMessageTemplate is required"); + assertThat(errorResponse).containsIgnoringCase("verificationCodeLifetime is required"); } private String savePlatformTwoFaSettingsAndGetError(TwoFaProviderConfig invalidTwoFaProviderConfig) throws Exception { @@ -216,12 +216,12 @@ public abstract class TwoFactorAuthConfigTest extends AbstractControllerTest { String errorMessage = getErrorMessage(doPost("/api/2fa/account/config/submit", totpTwoFaAccountConfig) .andExpect(status().isBadRequest())); - assertThat(errorMessage).containsIgnoringCase("otp auth url cannot be blank"); + assertThat(errorMessage).containsIgnoringCase("authUrl must not be blank"); totpTwoFaAccountConfig.setAuthUrl("otpauth://totp/T B: aba"); errorMessage = getErrorMessage(doPost("/api/2fa/account/config/submit", totpTwoFaAccountConfig) .andExpect(status().isBadRequest())); - assertThat(errorMessage).containsIgnoringCase("otp auth url is invalid"); + assertThat(errorMessage).containsIgnoringCase("authUrl is invalid"); totpTwoFaAccountConfig.setAuthUrl("otpauth://totp/ThingsBoard%20(Tenant):tenant@thingsboard.org?issuer=ThingsBoard+%28Tenant%29&secret=FUNBIM3CXFNNGQR6ZIPVWHP65PPFWDII"); doPost("/api/2fa/account/config/submit", totpTwoFaAccountConfig) @@ -336,14 +336,14 @@ public abstract class TwoFactorAuthConfigTest extends AbstractControllerTest { String errorMessage = getErrorMessage(doPost("/api/2fa/account/config/submit", smsTwoFaAccountConfig) .andExpect(status().isBadRequest())); - assertThat(errorMessage).containsIgnoringCase("phone number cannot be blank"); + assertThat(errorMessage).containsIgnoringCase("phoneNumber must not be blank"); String nonE164PhoneNumber = "8754868"; smsTwoFaAccountConfig.setPhoneNumber(nonE164PhoneNumber); errorMessage = getErrorMessage(doPost("/api/2fa/account/config/submit", smsTwoFaAccountConfig) .andExpect(status().isBadRequest())); - assertThat(errorMessage).containsIgnoringCase("phone number is not of E.164 format"); + assertThat(errorMessage).containsIgnoringCase("phoneNumber is not of E.164 format"); } @Test diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationTemplateService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationTemplateService.java index 02c4d98249..bc1b95a37b 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationTemplateService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationTemplateService.java @@ -17,10 +17,9 @@ package org.thingsboard.server.dao.notification; import org.thingsboard.server.common.data.id.NotificationTemplateId; import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod; import org.thingsboard.server.common.data.notification.template.NotificationTemplate; - -import java.util.List; +import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.page.PageLink; public interface NotificationTemplateService { @@ -28,6 +27,8 @@ public interface NotificationTemplateService { NotificationTemplate saveNotificationTemplate(TenantId tenantId, NotificationTemplate notificationTemplate); + PageData findNotificationTemplatesByTenantId(TenantId tenantId, PageLink pageLink); + void deleteNotificationTemplateById(TenantId tenantId, NotificationTemplateId id); } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/EntityType.java b/common/data/src/main/java/org/thingsboard/server/common/data/EntityType.java index 4f62519690..355e36237e 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/EntityType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/EntityType.java @@ -21,5 +21,5 @@ package org.thingsboard.server.common.data; public enum EntityType { TENANT, CUSTOMER, USER, DASHBOARD, ASSET, DEVICE, ALARM, RULE_CHAIN, RULE_NODE, ENTITY_VIEW, WIDGETS_BUNDLE, WIDGET_TYPE, TENANT_PROFILE, DEVICE_PROFILE, ASSET_PROFILE, API_USAGE_STATE, TB_RESOURCE, OTA_PACKAGE, EDGE, RPC, QUEUE, - NOTIFICATION_TARGET, NOTIFICATION_TEMPLATE, NOTIFICATION_REQUEST, NOTIFICATION_RULE; + NOTIFICATION_TARGET, NOTIFICATION_TEMPLATE, NOTIFICATION_REQUEST, NOTIFICATION, NOTIFICATION_RULE; } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/id/NotificationId.java b/common/data/src/main/java/org/thingsboard/server/common/data/id/NotificationId.java index 2a467d3a07..a7f3051d09 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/id/NotificationId.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/id/NotificationId.java @@ -17,12 +17,20 @@ package org.thingsboard.server.common.data.id; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; +import org.thingsboard.server.common.data.EntityType; import java.util.UUID; -public class NotificationId extends UUIDBased { +public class NotificationId extends UUIDBased implements EntityId { + @JsonCreator public NotificationId(@JsonProperty("id") UUID id) { super(id); } + + @Override + public EntityType getEntityType() { + return EntityType.NOTIFICATION; + } + } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/SlackDeliveryMethodNotificationTemplate.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/SlackDeliveryMethodNotificationTemplate.java index 241614f401..f9e568148f 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/SlackDeliveryMethodNotificationTemplate.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/SlackDeliveryMethodNotificationTemplate.java @@ -23,7 +23,6 @@ import lombok.EqualsAndHashCode; public class SlackDeliveryMethodNotificationTemplate extends DeliveryMethodNotificationTemplate { private SlackConversation.Type conversationType; - // add Conversation type! private String conversationId; // not required, set from user's name if not set } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/security/model/mfa/PlatformTwoFaSettings.java b/common/data/src/main/java/org/thingsboard/server/common/data/security/model/mfa/PlatformTwoFaSettings.java index ec2acf8733..c173d1ff6a 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/security/model/mfa/PlatformTwoFaSettings.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/security/model/mfa/PlatformTwoFaSettings.java @@ -36,14 +36,14 @@ public class PlatformTwoFaSettings { private List providers; @NotNull - @Min(value = 5, message = "minimum verification code sent period must be greater than or equal 5") + @Min(value = 5) private Integer minVerificationCodeSendPeriod; - @Pattern(regexp = "[1-9]\\d*:[1-9]\\d*", message = "verification code check rate limit configuration is invalid") + @Pattern(regexp = "[1-9]\\d*:[1-9]\\d*", message = "is invalid") private String verificationCodeCheckRateLimit; - @Min(value = 0, message = "maximum number of verification failure before user lockout must be positive") + @Min(value = 0, message = "must be positive") private Integer maxVerificationFailuresBeforeUserLockout; @NotNull - @Min(value = 60, message = "total amount of time allotted for verification must be greater than or equal 60") + @Min(value = 60) private Integer totalAllowedTimeForVerification; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/security/model/mfa/account/SmsTwoFaAccountConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/security/model/mfa/account/SmsTwoFaAccountConfig.java index 84a7b6924e..9906705cb3 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/security/model/mfa/account/SmsTwoFaAccountConfig.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/security/model/mfa/account/SmsTwoFaAccountConfig.java @@ -26,8 +26,8 @@ import javax.validation.constraints.Pattern; @Data public class SmsTwoFaAccountConfig extends OtpBasedTwoFaAccountConfig { - @NotBlank(message = "phone number cannot be blank") - @Pattern(regexp = "^\\+[1-9]\\d{1,14}$", message = "phone number is not of E.164 format") + @NotBlank + @Pattern(regexp = "^\\+[1-9]\\d{1,14}$", message = "is not of E.164 format") private String phoneNumber; @Override diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/security/model/mfa/account/TotpTwoFaAccountConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/security/model/mfa/account/TotpTwoFaAccountConfig.java index 5cdec02e90..f8e7153703 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/security/model/mfa/account/TotpTwoFaAccountConfig.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/security/model/mfa/account/TotpTwoFaAccountConfig.java @@ -26,8 +26,8 @@ import javax.validation.constraints.Pattern; @EqualsAndHashCode(callSuper = true) public class TotpTwoFaAccountConfig extends TwoFaAccountConfig { - @NotBlank(message = "OTP auth URL cannot be blank") - @Pattern(regexp = "otpauth://totp/(\\S+?):(\\S+?)\\?issuer=(\\S+?)&secret=(\\w+?)", message = "OTP auth url is invalid") + @NotBlank + @Pattern(regexp = "otpauth://totp/(\\S+?):(\\S+?)\\?issuer=(\\S+?)&secret=(\\w+?)", message = "is invalid") private String authUrl; @Override diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/security/model/mfa/provider/BackupCodeTwoFaProviderConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/security/model/mfa/provider/BackupCodeTwoFaProviderConfig.java index e8d4b90e03..98b98f6296 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/security/model/mfa/provider/BackupCodeTwoFaProviderConfig.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/security/model/mfa/provider/BackupCodeTwoFaProviderConfig.java @@ -22,7 +22,7 @@ import javax.validation.constraints.Min; @Data public class BackupCodeTwoFaProviderConfig implements TwoFaProviderConfig { - @Min(value = 1, message = "backup codes quantity must be greater than 0") + @Min(value = 1, message = "must be greater than 0") private int codesQuantity; @Override diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/security/model/mfa/provider/OtpBasedTwoFaProviderConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/security/model/mfa/provider/OtpBasedTwoFaProviderConfig.java index f1595e733f..a215de4266 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/security/model/mfa/provider/OtpBasedTwoFaProviderConfig.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/security/model/mfa/provider/OtpBasedTwoFaProviderConfig.java @@ -22,7 +22,7 @@ import javax.validation.constraints.Min; @Data public abstract class OtpBasedTwoFaProviderConfig implements TwoFaProviderConfig { - @Min(value = 1, message = "verification code lifetime is required") + @Min(value = 1, message = "is required") private int verificationCodeLifetime; } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/security/model/mfa/provider/SmsTwoFaProviderConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/security/model/mfa/provider/SmsTwoFaProviderConfig.java index 973a2d0c3b..30f476e70e 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/security/model/mfa/provider/SmsTwoFaProviderConfig.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/security/model/mfa/provider/SmsTwoFaProviderConfig.java @@ -25,8 +25,8 @@ import javax.validation.constraints.Pattern; @Data public class SmsTwoFaProviderConfig extends OtpBasedTwoFaProviderConfig { - @NotBlank(message = "verification message template is required") - @Pattern(regexp = ".*\\$\\{code}.*", message = "template must contain verification code") + @NotBlank(message = "is required") + @Pattern(regexp = ".*\\$\\{code}.*", message = "must contain verification code") private String smsVerificationMessageTemplate; @Override diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/security/model/mfa/provider/TotpTwoFaProviderConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/security/model/mfa/provider/TotpTwoFaProviderConfig.java index 32f443f785..c9a9183f78 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/security/model/mfa/provider/TotpTwoFaProviderConfig.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/security/model/mfa/provider/TotpTwoFaProviderConfig.java @@ -22,7 +22,7 @@ import javax.validation.constraints.NotBlank; @Data public class TotpTwoFaProviderConfig implements TwoFaProviderConfig { - @NotBlank(message = "issuer name must not be blank") + @NotBlank private String issuerName; @Override diff --git a/dao/src/main/java/org/thingsboard/server/dao/DaoUtil.java b/dao/src/main/java/org/thingsboard/server/dao/DaoUtil.java index 816069ef9a..c11f854d42 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/DaoUtil.java +++ b/dao/src/main/java/org/thingsboard/server/dao/DaoUtil.java @@ -18,6 +18,7 @@ package org.thingsboard.server.dao; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; +import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.UUIDBased; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; @@ -94,11 +95,18 @@ public abstract class DaoUtil { } public static UUID getId(UUIDBased idBased) { - UUID id = null; - if (idBased != null) { - id = idBased.getId(); + return getId(idBased, false); + } + + public static UUID getId(UUIDBased uuidBased, boolean nullUuidToNull) { + UUID uuid = null; + if (uuidBased != null) { + uuid = uuidBased.getId(); + if (nullUuidToNull && uuid.equals(EntityId.NULL_UUID)) { + uuid = null; + } } - return id; + return uuid; } public static List toUUIDs(List idBasedIds) { @@ -110,7 +118,7 @@ public abstract class DaoUtil { } public static void processInBatches(Function> finder, int batchSize, Consumer processor) { - processBatches(finder, batchSize, batch -> batch.getData().forEach(processor)); + processBatches(finder, batchSize, batch -> batch.getData().forEach(processor)); } public static void processBatches(Function> finder, int batchSize, Consumer> processor) { diff --git a/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationRuleService.java b/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationRuleService.java index a29ea2f892..0164684145 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationRuleService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationRuleService.java @@ -22,7 +22,6 @@ import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.notification.rule.NotificationRule; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; -import org.thingsboard.server.dao.service.DataValidator; @Service @RequiredArgsConstructor @@ -30,11 +29,8 @@ public class DefaultNotificationRuleService implements NotificationRuleService { private final NotificationRuleDao notificationRuleDao; - private final NotificationRuleValidator validator = new NotificationRuleValidator(); - @Override public NotificationRule saveNotificationRule(TenantId tenantId, NotificationRule notificationRule) { - validator.validate(notificationRule, NotificationRule::getTenantId); return notificationRuleDao.save(tenantId, notificationRule); } @@ -53,7 +49,4 @@ public class DefaultNotificationRuleService implements NotificationRuleService { notificationRuleDao.removeById(tenantId, id.getId()); } - private static class NotificationRuleValidator extends DataValidator { - } - } diff --git a/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationTemplateService.java b/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationTemplateService.java index 5dac9fc45c..b5d60e13ee 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationTemplateService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationTemplateService.java @@ -21,6 +21,8 @@ import org.thingsboard.server.common.data.id.NotificationTemplateId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.notification.NotificationRequestStatus; import org.thingsboard.server.common.data.notification.template.NotificationTemplate; +import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.dao.entity.AbstractEntityService; import java.util.Map; @@ -42,6 +44,11 @@ public class DefaultNotificationTemplateService extends AbstractEntityService im return notificationTemplateDao.save(tenantId, notificationTemplate); } + @Override + public PageData findNotificationTemplatesByTenantId(TenantId tenantId, PageLink pageLink) { + return notificationTemplateDao.findByTenantIdAndPageLink(tenantId, pageLink); + } + @Override public void deleteNotificationTemplateById(TenantId tenantId, NotificationTemplateId id) { if (notificationRequestDao.existsByStatusAndTemplateId(tenantId, NotificationRequestStatus.SCHEDULED, id)) { diff --git a/dao/src/main/java/org/thingsboard/server/dao/notification/NotificationTemplateDao.java b/dao/src/main/java/org/thingsboard/server/dao/notification/NotificationTemplateDao.java index 7417c7f43d..489c35d6a8 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/notification/NotificationTemplateDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/notification/NotificationTemplateDao.java @@ -15,8 +15,14 @@ */ package org.thingsboard.server.dao.notification; +import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.notification.template.NotificationTemplate; +import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.dao.Dao; public interface NotificationTemplateDao extends Dao { + + PageData findByTenantIdAndPageLink(TenantId tenantId, PageLink pageLink); + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationDao.java index f3a0698b1e..bb0946c1bc 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationDao.java @@ -20,6 +20,7 @@ import lombok.RequiredArgsConstructor; import org.springframework.beans.factory.annotation.Value; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.id.NotificationId; import org.thingsboard.server.common.data.id.NotificationRequestId; import org.thingsboard.server.common.data.id.TenantId; @@ -97,4 +98,9 @@ public class JpaNotificationDao extends JpaAbstractDao findByTenantIdAndPageLink(TenantId tenantId, PageLink pageLink) { - return DaoUtil.toPageData(notificationRequestRepository.findByTenantId(tenantId.getId(), DaoUtil.toPageable(pageLink))); + return DaoUtil.toPageData(notificationRequestRepository.findByTenantId(getId(tenantId, true), DaoUtil.toPageable(pageLink))); } @Override diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationRuleDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationRuleDao.java index e83e02d0a0..a4e02a66b8 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationRuleDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationRuleDao.java @@ -33,6 +33,8 @@ import org.thingsboard.server.dao.util.SqlDao; import java.util.UUID; +import static org.thingsboard.server.dao.DaoUtil.getId; + @Component @SqlDao @RequiredArgsConstructor @@ -42,7 +44,7 @@ public class JpaNotificationRuleDao extends JpaAbstractDao findByTenantIdAndPageLink(TenantId tenantId, PageLink pageLink) { - return DaoUtil.toPageData(notificationRuleRepository.findByTenantIdAndNameContainingIgnoreCase(tenantId.getId(), + return DaoUtil.toPageData(notificationRuleRepository.findByTenantIdAndNameContainingIgnoreCase(getId(tenantId, true), Strings.nullToEmpty(pageLink.getTextSearch()), DaoUtil.toPageable(pageLink))); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationTargetDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationTargetDao.java index cd60c4c2e3..789574c3d0 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationTargetDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationTargetDao.java @@ -32,6 +32,8 @@ import org.thingsboard.server.dao.util.SqlDao; import java.util.UUID; +import static org.thingsboard.server.dao.DaoUtil.getId; + @Component @SqlDao @RequiredArgsConstructor @@ -41,7 +43,7 @@ public class JpaNotificationTargetDao extends JpaAbstractDao findByTenantIdAndPageLink(TenantId tenantId, PageLink pageLink) { - return DaoUtil.toPageData(notificationTargetRepository.findByTenantIdAndNameContainingIgnoreCase(tenantId.getId(), + return DaoUtil.toPageData(notificationTargetRepository.findByTenantIdAndNameContainingIgnoreCase(getId(tenantId, true), Strings.nullToEmpty(pageLink.getTextSearch()), DaoUtil.toPageable(pageLink))); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationTemplateDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationTemplateDao.java index df1f1c9ee9..7942decb6a 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationTemplateDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationTemplateDao.java @@ -15,11 +15,16 @@ */ package org.thingsboard.server.dao.sql.notification; +import com.google.common.base.Strings; import lombok.RequiredArgsConstructor; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.notification.template.NotificationTemplate; +import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.page.PageLink; +import org.thingsboard.server.dao.DaoUtil; import org.thingsboard.server.dao.model.sql.NotificationTemplateEntity; import org.thingsboard.server.dao.notification.NotificationTemplateDao; import org.thingsboard.server.dao.sql.JpaAbstractDao; @@ -27,6 +32,8 @@ import org.thingsboard.server.dao.util.SqlDao; import java.util.UUID; +import static org.thingsboard.server.dao.DaoUtil.getId; + @Component @SqlDao @RequiredArgsConstructor @@ -39,6 +46,12 @@ public class JpaNotificationTemplateDao extends JpaAbstractDao findByTenantIdAndPageLink(TenantId tenantId, PageLink pageLink) { + return DaoUtil.toPageData(notificationTemplateRepository.findByTenantIdAndSearchText(getId(tenantId, true), + Strings.nullToEmpty(pageLink.getTextSearch()), DaoUtil.toPageable(pageLink))); + } + @Override protected JpaRepository getRepository() { return notificationTemplateRepository; diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationTemplateRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationTemplateRepository.java index 5d007fc677..abfa0cdf59 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationTemplateRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationTemplateRepository.java @@ -15,7 +15,11 @@ */ package org.thingsboard.server.dao.sql.notification; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; import org.thingsboard.server.dao.model.sql.NotificationTemplateEntity; @@ -23,4 +27,12 @@ import java.util.UUID; @Repository public interface NotificationTemplateRepository extends JpaRepository { + + @Query("SELECT t FROM NotificationTemplateEntity t WHERE t.tenantId = :tenantId AND " + + "(lower(t.name) LIKE lower(concat('%', :searchText, '%')) OR " + + "lower(t.notificationType) LIKE lower(concat('%', :searchText, '%')))") + Page findByTenantIdAndSearchText(@Param("tenantId") UUID tenantId, + @Param("searchText") String searchText, + Pageable pageable); + }