Merge pull request #8440 from thingsboard/improvements/notification-system

Improvements for notification system
This commit is contained in:
Andrew Shvayka 2023-05-01 12:43:48 +03:00 committed by GitHub
commit d03f236263
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 302 additions and 59 deletions

View File

@ -16,6 +16,7 @@
package org.thingsboard.server.controller;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.access.prepost.PreAuthorize;
@ -76,6 +77,16 @@ import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;
import static org.thingsboard.server.controller.ControllerConstants.AVAILABLE_FOR_ANY_AUTHORIZED_USER;
import static org.thingsboard.server.controller.ControllerConstants.MARKDOWN_CODE_BLOCK_END;
import static org.thingsboard.server.controller.ControllerConstants.MARKDOWN_CODE_BLOCK_START;
import static org.thingsboard.server.controller.ControllerConstants.NEW_LINE;
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.SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH;
import static org.thingsboard.server.service.security.permission.Resource.NOTIFICATION;
@RestController
@ -93,7 +104,10 @@ public class NotificationController extends BaseController {
private final NotificationSettingsService notificationSettingsService;
@ApiOperation(value = "Get notifications (getNotifications)",
notes = "**WebSocket API**:\n\n" +
notes = "Returns the page of notifications for current user." + NEW_LINE +
PAGE_DATA_PARAMETERS +
AVAILABLE_FOR_ANY_AUTHORIZED_USER + NEW_LINE +
"**WebSocket API**:\n\n" +
"There are 2 types of subscriptions: one for unread notifications count, another for unread notifications themselves.\n\n" +
"The URI for opening WS session for notifications: `/api/ws/plugins/notifications`.\n\n" +
"Subscription command for unread notifications count:\n" +
@ -144,11 +158,17 @@ public class NotificationController extends BaseController {
"}\n```")
@GetMapping("/notifications")
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
public PageData<Notification> getNotifications(@RequestParam int pageSize,
public PageData<Notification> getNotifications(@ApiParam(value = PAGE_SIZE_DESCRIPTION, required = true)
@RequestParam int pageSize,
@ApiParam(value = PAGE_NUMBER_DESCRIPTION, required = true)
@RequestParam int page,
@ApiParam(value = "Case-insensitive 'substring' filter based on notification subject or text")
@RequestParam(required = false) String textSearch,
@ApiParam(value = SORT_PROPERTY_DESCRIPTION)
@RequestParam(required = false) String sortProperty,
@ApiParam(value = SORT_ORDER_DESCRIPTION)
@RequestParam(required = false) String sortOrder,
@ApiParam(value = "To search for unread notifications only")
@RequestParam(defaultValue = "false") boolean unreadOnly,
@AuthenticationPrincipal SecurityUser user) throws ThingsboardException {
// no permissions
@ -156,6 +176,9 @@ public class NotificationController extends BaseController {
return notificationService.findNotificationsByRecipientIdAndReadStatus(user.getTenantId(), user.getId(), unreadOnly, pageLink);
}
@ApiOperation(value = "Mark notification as read (markNotificationAsRead)",
notes = "Marks notification as read by its id." +
AVAILABLE_FOR_ANY_AUTHORIZED_USER)
@PutMapping("/notification/{id}/read")
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
public void markNotificationAsRead(@PathVariable UUID id,
@ -165,6 +188,9 @@ public class NotificationController extends BaseController {
notificationCenter.markNotificationAsRead(user.getTenantId(), user.getId(), notificationId);
}
@ApiOperation(value = "Mark all notifications as read (markAllNotificationsAsRead)",
notes = "Marks all unread notifications as read." +
AVAILABLE_FOR_ANY_AUTHORIZED_USER)
@PutMapping("/notifications/read")
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
public void markAllNotificationsAsRead(@AuthenticationPrincipal SecurityUser user) {
@ -172,6 +198,9 @@ public class NotificationController extends BaseController {
notificationCenter.markAllNotificationsAsRead(user.getTenantId(), user.getId());
}
@ApiOperation(value = "Delete notification (deleteNotification)",
notes = "Deletes notification by its id." +
AVAILABLE_FOR_ANY_AUTHORIZED_USER)
@DeleteMapping("/notification/{id}")
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
public void deleteNotification(@PathVariable UUID id,
@ -181,6 +210,35 @@ public class NotificationController extends BaseController {
notificationCenter.deleteNotification(user.getTenantId(), user.getId(), notificationId);
}
@ApiOperation(value = "Create notification request (createNotificationRequest)",
notes = "Processes notification request.\n" +
"Mandatory request properties are `targets` (list of targets ids to send notification to), " +
"and either `templateId` (existing notification template id) or `template` (to send notification without saving the template).\n" +
"Optionally, you can set `sendingDelayInSec` inside the `additionalConfig` field to schedule the notification." + NEW_LINE +
"For each enabled delivery method in the notification template, there must be a target in the `targets` list that supports this delivery method: " +
"if you chose `WEB`, `EMAIL` or `SMS` - there must be at least one target in `targets` of `PLATFORM_USERS` type.\n" +
"For `SLACK` delivery method - you need to chose at least one `SLACK` notification target." + NEW_LINE +
"Notification request object with `PROCESSING` status will be returned immediately, " +
"and the notification sending itself is done asynchronously. After all notifications are sent, " +
"the `status` of the request becomes `SENT`. Use `getNotificationRequestById` to see " +
"the notification request processing status and some sending stats. " + NEW_LINE +
"Here is an example of notification request to one target using saved template:\n" +
MARKDOWN_CODE_BLOCK_START +
"{\n" +
" \"templateId\": {\n" +
" \"entityType\": \"NOTIFICATION_TEMPLATE\",\n" +
" \"id\": \"6dbc3670-e4dd-11ed-9401-dbcc5dff78be\"\n" +
" },\n" +
" \"targets\": [\n" +
" \"320e3ed0-d785-11ed-a06c-21dd57dd88ca\"\n" +
" ],\n" +
" \"additionalConfig\": {\n" +
" \"sendingDelayInSec\": 0\n" +
" }\n" +
"}" +
MARKDOWN_CODE_BLOCK_END +
SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH
)
@PostMapping("/notification/request")
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
public NotificationRequest createNotificationRequest(@RequestBody @Valid NotificationRequest notificationRequest,
@ -200,9 +258,15 @@ public class NotificationController extends BaseController {
return doSaveAndLog(EntityType.NOTIFICATION_REQUEST, notificationRequest, (tenantId, request) -> notificationCenter.processNotificationRequest(tenantId, request, null));
}
@ApiOperation(value = "Get notification request preview (getNotificationRequestPreview)",
notes = "Returns preview for notification request." + NEW_LINE +
"`processedTemplates` shows how the notifications for each delivery method will look like " +
"for the first recipient of the corresponding notification target." +
SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)
@PostMapping("/notification/request/preview")
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
public NotificationRequestPreview getNotificationRequestPreview(@RequestBody @Valid NotificationRequest request,
@ApiParam(value = "Amount of the recipients to show in preview")
@RequestParam(defaultValue = "20") int recipientsPreviewSize,
@AuthenticationPrincipal SecurityUser user) throws ThingsboardException {
NotificationTemplate template;
@ -281,6 +345,9 @@ public class NotificationController extends BaseController {
return preview;
}
@ApiOperation(value = "Get notification request by id (getNotificationRequestById)",
notes = "Fetches notification request info by request id." +
SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)
@GetMapping("/notification/request/{id}")
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
public NotificationRequestInfo getNotificationRequestById(@PathVariable UUID id) throws ThingsboardException {
@ -288,12 +355,21 @@ public class NotificationController extends BaseController {
return checkEntityId(notificationRequestId, notificationRequestService::findNotificationRequestInfoById, Operation.READ);
}
@ApiOperation(value = "Get notification requests (getNotificationRequests)",
notes = "Returns the page of notification requests submitted by users of this tenant or sysadmins." + NEW_LINE +
PAGE_DATA_PARAMETERS +
SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)
@GetMapping("/notification/requests")
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
public PageData<NotificationRequestInfo> getNotificationRequests(@RequestParam int pageSize,
public PageData<NotificationRequestInfo> getNotificationRequests(@ApiParam(value = PAGE_SIZE_DESCRIPTION, required = true)
@RequestParam int pageSize,
@ApiParam(value = PAGE_NUMBER_DESCRIPTION, required = true)
@RequestParam int page,
@ApiParam(value = "Case-insensitive 'substring' filed based on the used template name")
@RequestParam(required = false) String textSearch,
@ApiParam(value = SORT_PROPERTY_DESCRIPTION)
@RequestParam(required = false) String sortProperty,
@ApiParam(value = SORT_ORDER_DESCRIPTION)
@RequestParam(required = false) String sortOrder,
@AuthenticationPrincipal SecurityUser user) throws ThingsboardException {
// generic permission
@ -301,6 +377,11 @@ public class NotificationController extends BaseController {
return notificationRequestService.findNotificationRequestsInfosByTenantIdAndOriginatorType(user.getTenantId(), EntityType.USER, pageLink);
}
@ApiOperation(value = "Delete notification request (deleteNotificationRequest)",
notes = "Deletes notification request by its id." + NEW_LINE +
"If the request has status `SENT` - all sent notifications for this request will be deleted. " +
"If it is `SCHEDULED`, the request will be cancelled." +
SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)
@DeleteMapping("/notification/request/{id}")
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
public void deleteNotificationRequest(@PathVariable UUID id) throws Exception {
@ -310,6 +391,21 @@ public class NotificationController extends BaseController {
}
@ApiOperation(value = "Save notification settings (saveNotificationSettings)",
notes = "Saves notification settings for this tenant or sysadmin.\n" +
"`deliveryMethodsConfigs` of the settings must be specified." + NEW_LINE +
"Here is an example of the notification settings with Slack configuration:\n" +
MARKDOWN_CODE_BLOCK_START +
"{\n" +
" \"deliveryMethodsConfigs\": {\n" +
" \"SLACK\": {\n" +
" \"method\": \"SLACK\",\n" +
" \"botToken\": \"xoxb-....\"\n" +
" }\n" +
" }\n" +
"}" +
MARKDOWN_CODE_BLOCK_END +
SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)
@PostMapping("/notification/settings")
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
public NotificationSettings saveNotificationSettings(@RequestBody @Valid NotificationSettings notificationSettings,
@ -320,6 +416,9 @@ public class NotificationController extends BaseController {
return notificationSettings;
}
@ApiOperation(value = "Get notification settings (getNotificationSettings)",
notes = "Retrieves notification settings for this tenant or sysadmin." +
SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)
@GetMapping("/notification/settings")
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
public NotificationSettings getNotificationSettings(@AuthenticationPrincipal SecurityUser user) throws ThingsboardException {
@ -328,6 +427,9 @@ public class NotificationController extends BaseController {
return notificationSettingsService.findNotificationSettings(tenantId);
}
@ApiOperation(value = "Get available delivery methods (getAvailableDeliveryMethods)",
notes = "Returns the list of delivery methods that are properly configured and are allowed to be used for sending notifications." +
SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)
@GetMapping("/notification/deliveryMethods")
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
public Set<NotificationDeliveryMethod> getAvailableDeliveryMethods(@AuthenticationPrincipal SecurityUser user) throws ThingsboardException {

View File

@ -15,6 +15,8 @@
*/
package org.thingsboard.server.controller;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.access.prepost.PreAuthorize;
@ -44,6 +46,15 @@ import org.thingsboard.server.service.security.permission.Operation;
import javax.validation.Valid;
import java.util.UUID;
import static org.thingsboard.server.controller.ControllerConstants.MARKDOWN_CODE_BLOCK_END;
import static org.thingsboard.server.controller.ControllerConstants.MARKDOWN_CODE_BLOCK_START;
import static org.thingsboard.server.controller.ControllerConstants.NEW_LINE;
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.SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH;
import static org.thingsboard.server.service.security.permission.Resource.NOTIFICATION;
@RestController
@ -55,6 +66,53 @@ public class NotificationRuleController extends BaseController {
private final NotificationRuleService notificationRuleService;
@ApiOperation(value = "Save notification rule (saveNotificationRule)",
notes = "Creates or updates notification rule. " + NEW_LINE +
"Mandatory properties are `name`, `templateId` (of a template with `notificationType` matching to rule's `triggerType`), " +
"`triggerType`, `triggerConfig` and `recipientConfig`. Additionally, you may specify rule `description` " +
"inside of `additionalConfig`." + NEW_LINE +
"Trigger type of the rule cannot be changed. " +
"Available trigger types for tenant: `ENTITY_ACTION`, `ALARM`, `ALARM_COMMENT`, `ALARM_ASSIGNMENT`, " +
"`DEVICE_ACTIVITY`, `RULE_ENGINE_COMPONENT_LIFECYCLE_EVENT`.\n" +
"For sysadmin, there are following trigger types available: `ENTITIES_LIMIT`, `API_USAGE_LIMIT`, " +
"`NEW_PLATFORM_VERSION`." + NEW_LINE +
"Here is an example of notification rule to send notification when a " +
"device, asset or customer is created or deleted:\n" +
MARKDOWN_CODE_BLOCK_START +
"{\n" +
" \"name\": \"Entity action\",\n" +
" \"templateId\": {\n" +
" \"entityType\": \"NOTIFICATION_TEMPLATE\",\n" +
" \"id\": \"32117320-d785-11ed-a06c-21dd57dd88ca\"\n" +
" },\n" +
" \"triggerType\": \"ENTITY_ACTION\",\n" +
" \"triggerConfig\": {\n" +
" \"entityTypes\": [\n" +
" \"CUSTOMER\",\n" +
" \"DEVICE\",\n" +
" \"ASSET\"\n" +
" ],\n" +
" \"created\": true,\n" +
" \"updated\": false,\n" +
" \"deleted\": true,\n" +
" \"triggerType\": \"ENTITY_ACTION\"\n" +
" },\n" +
" \"recipientsConfig\": {\n" +
" \"targets\": [\n" +
" \"320f2930-d785-11ed-a06c-21dd57dd88ca\"\n" +
" ],\n" +
" \"triggerType\": \"ENTITY_ACTION\"\n" +
" },\n" +
" \"additionalConfig\": {\n" +
" \"description\": \"Send notification to tenant admins or customer users when a device, asset or customer is created\"\n" +
" },\n" +
" \"templateName\": \"Entity action notification\",\n" +
" \"deliveryMethods\": [\n" +
" \"WEB\"\n" +
" ]\n" +
"}" +
MARKDOWN_CODE_BLOCK_END +
SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)
@PostMapping("/rule")
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
public NotificationRule saveNotificationRule(@RequestBody @Valid NotificationRule notificationRule,
@ -74,6 +132,11 @@ public class NotificationRuleController extends BaseController {
return notificationRule;
}
@ApiOperation(value = "Get notification rule by id (getNotificationRuleById)",
notes = "Fetches notification rule info by rule's id.\n" +
"In addition to regular notification rule fields, " +
"there are `templateName` and `deliveryMethods` in the response." +
SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)
@GetMapping("/rule/{id}")
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
public NotificationRuleInfo getNotificationRuleById(@PathVariable UUID id) throws ThingsboardException {
@ -81,12 +144,21 @@ public class NotificationRuleController extends BaseController {
return checkEntityId(notificationRuleId, notificationRuleService::findNotificationRuleInfoById, Operation.READ);
}
@ApiOperation(value = "Get notification rules (getNotificationRules)",
notes = "Returns the page of notification rules." + NEW_LINE +
PAGE_DATA_PARAMETERS +
SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)
@GetMapping("/rules")
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
public PageData<NotificationRuleInfo> getNotificationRules(@RequestParam int pageSize,
public PageData<NotificationRuleInfo> getNotificationRules(@ApiParam(value = PAGE_SIZE_DESCRIPTION, required = true)
@RequestParam int pageSize,
@ApiParam(value = PAGE_NUMBER_DESCRIPTION, required = true)
@RequestParam int page,
@ApiParam(value = "Case-insensitive 'substring' filter based on rule's name")
@RequestParam(required = false) String textSearch,
@ApiParam(value = SORT_PROPERTY_DESCRIPTION)
@RequestParam(required = false) String sortProperty,
@ApiParam(value = SORT_ORDER_DESCRIPTION)
@RequestParam(required = false) String sortOrder,
@AuthenticationPrincipal SecurityUser user) throws ThingsboardException {
// generic permission
@ -94,6 +166,10 @@ public class NotificationRuleController extends BaseController {
return notificationRuleService.findNotificationRulesInfosByTenantId(user.getTenantId(), pageLink);
}
@ApiOperation(value = "Delete notification rule (deleteNotificationRule)",
notes = "Deletes notification rule by id.\n" +
"Cancels all related scheduled notification requests (e.g. due to escalation table)" +
SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)
@DeleteMapping("/rule/{id}")
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
public void deleteNotificationRule(@PathVariable UUID id,

View File

@ -16,6 +16,7 @@
package org.thingsboard.server.controller;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
@ -58,6 +59,14 @@ import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;
import static org.thingsboard.server.controller.ControllerConstants.MARKDOWN_CODE_BLOCK_END;
import static org.thingsboard.server.controller.ControllerConstants.MARKDOWN_CODE_BLOCK_START;
import static org.thingsboard.server.controller.ControllerConstants.NEW_LINE;
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.SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH;
import static org.thingsboard.server.service.security.permission.Resource.NOTIFICATION;
@ -71,7 +80,28 @@ public class NotificationTargetController extends BaseController {
private final NotificationTargetService notificationTargetService;
@ApiOperation(value = "Save notification target (saveNotificationTarget)",
notes = "Create or update notification target." +
notes = "Creates or updates notification target." + NEW_LINE +
"Available `configuration` types are `PLATFORM_USERS` and `SLACK`.\n" +
"For `PLATFORM_USERS` the `usersFilter` must be specified. " +
"For tenant, there are following users filter types available: " +
"`USER_LIST`, `CUSTOMER_USERS`, `TENANT_ADMINISTRATORS`, `ALL_USERS`, " +
"`ORIGINATOR_ENTITY_OWNER_USERS`, `AFFECTED_USER`.\n" +
"For sysadmin: `TENANT_ADMINISTRATORS`, `AFFECTED_TENANT_ADMINISTRATORS`, " +
"`SYSTEM_ADMINISTRATORS`, `ALL_USERS`." + NEW_LINE +
"Here is an example of tenant-level notification target to send notification to customer's users:\n" +
MARKDOWN_CODE_BLOCK_START +
"{\n" +
" \"name\": \"Users of Customer A\",\n" +
" \"configuration\": {\n" +
" \"type\": \"PLATFORM_USERS\",\n" +
" \"usersFilter\": {\n" +
" \"type\": \"CUSTOMER_USERS\",\n" +
" \"customerId\": \"32499a20-d785-11ed-a06c-21dd57dd88ca\"\n" +
" },\n" +
" \"description\": \"Users of Customer A\"\n" +
" }\n" +
"}" +
MARKDOWN_CODE_BLOCK_END +
SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)
@PostMapping("/target")
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@ -89,7 +119,7 @@ public class NotificationTargetController extends BaseController {
}
@ApiOperation(value = "Get notification target by id (getNotificationTargetById)",
notes = "Fetch saved notification target by id." +
notes = "Fetches notification target by id." +
SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)
@GetMapping("/target/{id}")
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@ -99,12 +129,14 @@ public class NotificationTargetController extends BaseController {
}
@ApiOperation(value = "Get recipients for notification target config (getRecipientsForNotificationTargetConfig)",
notes = "Get the list (page) of recipients (users) for such notification target configuration." +
notes = "Returns the page of recipients for such notification target configuration." +
SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)
@PostMapping("/target/recipients")
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
public PageData<User> getRecipientsForNotificationTargetConfig(@RequestBody NotificationTarget notificationTarget,
@ApiParam(value = PAGE_SIZE_DESCRIPTION, required = true)
@RequestParam int pageSize,
@ApiParam(value = PAGE_NUMBER_DESCRIPTION, required = true)
@RequestParam int page,
@AuthenticationPrincipal SecurityUser user) throws ThingsboardException {
// generic permission
@ -119,9 +151,13 @@ public class NotificationTargetController extends BaseController {
return notificationTargetService.findRecipientsForNotificationTargetConfig(user.getTenantId(), (PlatformUsersNotificationTargetConfig) notificationTarget.getConfiguration(), pageLink);
}
@ApiOperation(value = "Get notification targets by ids (getNotificationTargetsByIds)",
notes = "Returns the list of notification targets found by provided ids." +
SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)
@GetMapping(value = "/targets", params = {"ids"})
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
public List<NotificationTarget> getNotificationTargetsByIds(@RequestParam("ids") UUID[] ids,
public List<NotificationTarget> getNotificationTargetsByIds(@ApiParam(value = "Comma-separated list of uuids representing targets ids", required = true)
@RequestParam("ids") UUID[] ids,
@AuthenticationPrincipal SecurityUser user) {
// generic permission
List<NotificationTargetId> targetsIds = Arrays.stream(ids).map(NotificationTargetId::new).collect(Collectors.toList());
@ -129,14 +165,20 @@ public class NotificationTargetController extends BaseController {
}
@ApiOperation(value = "Get notification targets (getNotificationTargets)",
notes = "Fetch the page of notification targets owned by sysadmin or tenant." +
notes = "Returns the page of notification targets owned by sysadmin or tenant." + NEW_LINE +
PAGE_DATA_PARAMETERS +
SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)
@GetMapping("/targets")
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
public PageData<NotificationTarget> getNotificationTargets(@RequestParam int pageSize,
public PageData<NotificationTarget> getNotificationTargets(@ApiParam(value = PAGE_SIZE_DESCRIPTION, required = true)
@RequestParam int pageSize,
@ApiParam(value = PAGE_NUMBER_DESCRIPTION, required = true)
@RequestParam int page,
@ApiParam(value = "Case-insensitive 'substring' filed based on the target's name")
@RequestParam(required = false) String textSearch,
@ApiParam(value = SORT_PROPERTY_DESCRIPTION)
@RequestParam(required = false) String sortProperty,
@ApiParam(value = SORT_ORDER_DESCRIPTION)
@RequestParam(required = false) String sortOrder,
@AuthenticationPrincipal SecurityUser user) throws ThingsboardException {
// generic permission
@ -144,6 +186,10 @@ public class NotificationTargetController extends BaseController {
return notificationTargetService.findNotificationTargetsByTenantId(user.getTenantId(), pageLink);
}
@ApiOperation(value = "Get notification targets by supported notification type (getNotificationTargetsBySupportedNotificationType)",
notes = "Returns the page of notification targets filtered by notification type that they can be used for." + NEW_LINE +
PAGE_DATA_PARAMETERS +
SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)
@GetMapping(value = "/targets", params = "notificationType")
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
public PageData<NotificationTarget> getNotificationTargetsBySupportedNotificationType(@RequestParam int pageSize,
@ -158,7 +204,7 @@ public class NotificationTargetController extends BaseController {
}
@ApiOperation(value = "Delete notification target by id (deleteNotificationTargetById)",
notes = "Delete notification target by its id.\n\n" +
notes = "Deletes notification target by its id." + NEW_LINE +
"This target cannot be referenced by existing scheduled notification requests or any notification rules." +
SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)
@DeleteMapping("/target/{id}")

View File

@ -16,6 +16,7 @@
package org.thingsboard.server.controller;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.springframework.security.access.prepost.PreAuthorize;
@ -36,9 +37,9 @@ import org.thingsboard.server.common.data.notification.NotificationDeliveryMetho
import org.thingsboard.server.common.data.notification.NotificationType;
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.targets.slack.SlackConversation;
import org.thingsboard.server.common.data.notification.targets.slack.SlackConversationType;
import org.thingsboard.server.common.data.notification.template.NotificationTemplate;
import org.thingsboard.server.common.data.notification.targets.slack.SlackConversation;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.dao.notification.NotificationSettingsService;
@ -51,6 +52,14 @@ import javax.validation.Valid;
import java.util.List;
import java.util.UUID;
import static org.thingsboard.server.controller.ControllerConstants.MARKDOWN_CODE_BLOCK_END;
import static org.thingsboard.server.controller.ControllerConstants.MARKDOWN_CODE_BLOCK_START;
import static org.thingsboard.server.controller.ControllerConstants.NEW_LINE;
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.SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH;
import static org.thingsboard.server.service.security.permission.Resource.NOTIFICATION;
@ -65,19 +74,44 @@ public class NotificationTemplateController extends BaseController {
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" +
notes = "Creates or updates notification template." + NEW_LINE +
"Here is an example of template to send notification via Web, SMS and Slack:\n" +
MARKDOWN_CODE_BLOCK_START +
"{\n" +
" \"name\": \"Greetings\",\n" +
" \"notificationType\": \"GENERAL\",\n" +
" \"configuration\": {\n" +
" \"defaultTextTemplate\": \"Hello everyone\", # required if any of the templates' bodies is not set\n" +
" \"templates\": {\n" +
" \"PUSH\": {\n \"method\": \"PUSH\",\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: <body>Hello everyone</body>\",\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" +
" \"deliveryMethodsTemplates\": {\n" +
" \"WEB\": {\n" +
" \"enabled\": true,\n" +
" \"subject\": \"Greetings\",\n" +
" \"body\": \"Hi there, ${recipientTitle}\",\n" +
" \"additionalConfig\": {\n" +
" \"icon\": {\n" +
" \"enabled\": true,\n" +
" \"icon\": \"back_hand\",\n" +
" \"color\": \"#757575\"\n" +
" },\n" +
" \"actionButtonConfig\": {\n" +
" \"enabled\": false\n" +
" }\n" +
" },\n" +
" \"method\": \"WEB\"\n" +
" },\n" +
" \"SMS\": {\n" +
" \"enabled\": true,\n" +
" \"body\": \"Hi there, ${recipientTitle}\",\n" +
" \"method\": \"SMS\"\n" +
" },\n" +
" \"SLACK\": {\n" +
" \"enabled\": true,\n" +
" \"body\": \"Hi there, @${recipientTitle}\",\n" +
" \"method\": \"SLACK\"\n" +
" }\n" +
" }\n" +
" }\n}\n```" +
" }\n" +
"}" +
MARKDOWN_CODE_BLOCK_END +
SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)
@PostMapping("/template")
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@ -88,7 +122,7 @@ public class NotificationTemplateController extends BaseController {
}
@ApiOperation(value = "Get notification template by id (getNotificationTemplateById)",
notes = "Fetch notification template by id." +
notes = "Fetches notification template by id." +
SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)
@GetMapping("/template/{id}")
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@ -98,15 +132,22 @@ public class NotificationTemplateController extends BaseController {
}
@ApiOperation(value = "Get notification templates (getNotificationTemplates)",
notes = "Fetch the page of notification templates owned by sysadmin or tenant." +
notes = "Returns the page of notification templates owned by sysadmin or tenant." + NEW_LINE +
PAGE_DATA_PARAMETERS +
SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)
@GetMapping("/templates")
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
public PageData<NotificationTemplate> getNotificationTemplates(@RequestParam int pageSize,
public PageData<NotificationTemplate> getNotificationTemplates(@ApiParam(value = PAGE_SIZE_DESCRIPTION, required = true)
@RequestParam int pageSize,
@ApiParam(value = PAGE_NUMBER_DESCRIPTION, required = true)
@RequestParam int page,
@ApiParam(value = "Case-insensitive 'substring' filter based on template's name and notification type")
@RequestParam(required = false) String textSearch,
@ApiParam(value = SORT_PROPERTY_DESCRIPTION)
@RequestParam(required = false) String sortProperty,
@ApiParam(value = SORT_ORDER_DESCRIPTION)
@RequestParam(required = false) String sortOrder,
@ApiParam(value = "Comma-separated list of notification types to filter the templates")
@RequestParam(required = false) NotificationType[] notificationTypes,
@AuthenticationPrincipal SecurityUser user) throws ThingsboardException {
// generic permission
@ -119,7 +160,7 @@ public class NotificationTemplateController extends BaseController {
}
@ApiOperation(value = "Delete notification template by id (deleteNotificationTemplateById",
notes = "Delete notification template by its id.\n\n" +
notes = "Deletes notification template by its id." + NEW_LINE +
"This template cannot be referenced by existing scheduled notification requests or any notification rules." +
SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)
@DeleteMapping("/template/{id}")
@ -131,12 +172,12 @@ public class NotificationTemplateController extends BaseController {
}
@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." +
notes = "List available Slack conversations by type." +
SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)
@GetMapping("/slack/conversations")
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
public List<SlackConversation> listSlackConversations(@RequestParam SlackConversationType type,
@ApiParam(value = "Slack bot token. If absent - system Slack settings will be used")
@RequestParam(required = false) String token,
@AuthenticationPrincipal SecurityUser user) {
// generic permission

View File

@ -39,7 +39,6 @@ import org.thingsboard.server.common.data.notification.NotificationRequestConfig
import org.thingsboard.server.common.data.notification.NotificationRequestStats;
import org.thingsboard.server.common.data.notification.NotificationRequestStatus;
import org.thingsboard.server.common.data.notification.NotificationStatus;
import org.thingsboard.server.common.data.notification.NotificationType;
import org.thingsboard.server.common.data.notification.info.RuleOriginatedNotificationInfo;
import org.thingsboard.server.common.data.notification.settings.NotificationSettings;
import org.thingsboard.server.common.data.notification.targets.NotificationRecipient;
@ -294,24 +293,6 @@ public class DefaultNotificationCenter extends AbstractSubscriptionService imple
return onNotificationUpdate(recipient.getTenantId(), recipient.getId(), update);
}
@Override
public void sendBasicNotification(TenantId tenantId, UserId recipientId, String subject, String text) {
Notification notification = Notification.builder()
.recipientId(recipientId)
.type(NotificationType.GENERAL)
.subject(subject)
.text(text)
.status(NotificationStatus.SENT)
.build();
notification = notificationService.saveNotification(TenantId.SYS_TENANT_ID, notification);
NotificationUpdate update = NotificationUpdate.builder()
.created(true)
.notification(notification)
.build();
onNotificationUpdate(tenantId, recipientId, update);
}
@Override
public void markNotificationAsRead(TenantId tenantId, UserId recipientId, NotificationId notificationId) {
boolean updated = notificationService.markNotificationAsRead(tenantId, recipientId, notificationId);

View File

@ -55,7 +55,6 @@ public class NotificationRequest extends BaseData<NotificationRequestId> impleme
private NotificationTemplate template;
@Valid
private NotificationInfo info;
@NotNull
@Valid
private NotificationRequestConfig additionalConfig;

View File

@ -20,12 +20,12 @@ import lombok.Getter;
@Getter
public enum NotificationRuleTriggerType {
ENTITY_ACTION,
ALARM,
ALARM_COMMENT,
DEVICE_ACTIVITY,
ENTITY_ACTION,
RULE_ENGINE_COMPONENT_LIFECYCLE_EVENT,
ALARM_ASSIGNMENT,
DEVICE_ACTIVITY,
RULE_ENGINE_COMPONENT_LIFECYCLE_EVENT,
NEW_PLATFORM_VERSION(false),
ENTITIES_LIMIT(false),
API_USAGE_LIMIT(false);

View File

@ -109,7 +109,7 @@ public class DefaultNotificationSettingsService implements NotificationSettingsS
defaultNotifications.create(tenantId, DefaultNotifications.newAlarm, tenantAdmins.getId());
defaultNotifications.create(tenantId, DefaultNotifications.alarmUpdate, tenantAdmins.getId());
defaultNotifications.create(tenantId, DefaultNotifications.deviceAction, tenantAdmins.getId());
defaultNotifications.create(tenantId, DefaultNotifications.entityAction, tenantAdmins.getId());
defaultNotifications.create(tenantId, DefaultNotifications.deviceActivity, tenantAdmins.getId());
defaultNotifications.create(tenantId, DefaultNotifications.alarmComment, tenantAdmins.getId());
defaultNotifications.create(tenantId, DefaultNotifications.alarmAssignment, affectedUser.getId());

View File

@ -179,13 +179,13 @@ public class DefaultNotifications {
.description("Send notification to tenant admins when any alarm is updated or cleared")
.build())
.build();
public static final DefaultNotification deviceAction = DefaultNotification.builder()
.name("Device action notification")
public static final DefaultNotification entityAction = DefaultNotification.builder()
.name("Entity action notification")
.type(NotificationType.ENTITY_ACTION)
.subject("${entityType} was ${actionType}")
.text("${entityType} '${entityName}' was ${actionType} by user ${userEmail}")
.icon("info").color(null)
.button("Go to device").link("/devices/${entityId}")
.button("Go to ${entityType:lowerCase}").link("/${entityType:lowerCase}s/${entityId}")
.rule(DefaultRule.builder()
.name("Device created")
.triggerConfig(EntityActionNotificationRuleTriggerConfig.builder()
@ -341,7 +341,7 @@ public class DefaultNotifications {
public NotificationRule toRule(NotificationTemplateId templateId, NotificationTargetId... targets) {
DefaultRule defaultRule = this.rule;
NotificationRule rule = new NotificationRule();
rule.setName(name);
rule.setName(defaultRule.getName());
rule.setTemplateId(templateId);
rule.setTriggerType(defaultRule.getTriggerConfig().getTriggerType());
rule.setTriggerConfig(defaultRule.getTriggerConfig());

View File

@ -32,8 +32,6 @@ public interface NotificationCenter {
void deleteNotificationRequest(TenantId tenantId, NotificationRequestId notificationRequestId);
void sendBasicNotification(TenantId tenantId, UserId recipientId, String subject, String text);
void markNotificationAsRead(TenantId tenantId, UserId recipientId, NotificationId notificationId);
void markAllNotificationsAsRead(TenantId tenantId, UserId recipientId);