@ -20,6 +20,7 @@ import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.BooleanNode ;
import org.junit.Before ;
import org.junit.Test ;
import org.junit.function.ThrowingRunnable ;
import org.springframework.beans.factory.annotation.Autowired ;
import org.springframework.boot.test.mock.mockito.SpyBean ;
import org.springframework.data.util.Pair ;
@ -34,6 +35,7 @@ import org.thingsboard.server.common.data.alarm.Alarm;
import org.thingsboard.server.common.data.alarm.AlarmSearchStatus ;
import org.thingsboard.server.common.data.alarm.AlarmSeverity ;
import org.thingsboard.server.common.data.alarm.AlarmStatus ;
import org.thingsboard.server.common.data.asset.Asset ;
import org.thingsboard.server.common.data.device.profile.AlarmCondition ;
import org.thingsboard.server.common.data.device.profile.AlarmConditionFilter ;
import org.thingsboard.server.common.data.device.profile.AlarmConditionFilterKey ;
@ -41,6 +43,7 @@ import org.thingsboard.server.common.data.device.profile.AlarmConditionKeyType;
import org.thingsboard.server.common.data.device.profile.AlarmRule ;
import org.thingsboard.server.common.data.device.profile.DeviceProfileAlarm ;
import org.thingsboard.server.common.data.device.profile.SimpleAlarmConditionSpec ;
import org.thingsboard.server.common.data.id.NotificationTargetId ;
import org.thingsboard.server.common.data.id.TenantId ;
import org.thingsboard.server.common.data.notification.Notification ;
import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod ;
@ -54,7 +57,9 @@ import org.thingsboard.server.common.data.notification.rule.NotificationRule;
import org.thingsboard.server.common.data.notification.rule.NotificationRuleInfo ;
import org.thingsboard.server.common.data.notification.rule.trigger.AlarmNotificationRuleTriggerConfig ;
import org.thingsboard.server.common.data.notification.rule.trigger.AlarmNotificationRuleTriggerConfig.AlarmAction ;
import org.thingsboard.server.common.data.notification.rule.trigger.EntitiesLimitNotificationRuleTriggerConfig ;
import org.thingsboard.server.common.data.notification.rule.trigger.EntityActionNotificationRuleTriggerConfig ;
import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTriggerConfig ;
import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTriggerType ;
import org.thingsboard.server.common.data.notification.targets.NotificationTarget ;
import org.thingsboard.server.common.data.notification.template.NotificationTemplate ;
@ -66,21 +71,27 @@ import org.thingsboard.server.common.data.query.FilterPredicateValue;
import org.thingsboard.server.common.data.security.Authority ;
import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration ;
import org.thingsboard.server.common.data.tenant.profile.TenantProfileData ;
import org.thingsboard.server.dao.DaoUtil ;
import org.thingsboard.server.dao.notification.NotificationRequestService ;
import org.thingsboard.server.dao.service.DaoSqlTest ;
import org.thingsboard.server.dao.tenant.TenantProfileService ;
import org.thingsboard.server.service.apiusage.limits.LimitedApi ;
import org.thingsboard.server.service.apiusage.limits.RateLimitService ;
import org.thingsboard.server.service.entitiy.tenant.profile.TbTenantProfileService ;
import org.thingsboard.server.service.telemetry.AlarmSubscriptionService ;
import java.util.ArrayList ;
import java.util.Arrays ;
import java.util.HashMap ;
import java.util.List ;
import java.util.Map ;
import java.util.Set ;
import java.util.TreeMap ;
import java.util.UUID ;
import java.util.concurrent.Callable ;
import java.util.concurrent.TimeUnit ;
import java.util.function.BiConsumer ;
import java.util.function.Consumer ;
import static org.assertj.core.api.Assertions.assertThat ;
import static org.assertj.core.api.Assertions.offset ;
@ -98,6 +109,8 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest {
@Autowired
private TenantProfileService tenantProfileService ;
@Autowired
private TbTenantProfileService tbTenantProfileService ;
@Autowired
private RateLimitService rateLimitService ;
@Before
@ -107,56 +120,33 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest {
@Test
public void testNotificationRuleProcessing_entityActionTrigger ( ) throws Exception {
String notificationSubject = "${actionType}: ${entityType} [${entityId}]" ;
String notificationText = "User: ${userEmail}" ;
NotificationTemplate notificationTemplate = createNotificationTemplate ( NotificationType . GENERAL , notificationSubject , notificationText , NotificationDeliveryMethod . WEB ) ;
NotificationRule notificationRule = new NotificationRule ( ) ;
notificationRule . setName ( "Web notification when any device is created, updated or deleted" ) ;
notificationRule . setTemplateId ( notificationTemplate . getId ( ) ) ;
notificationRule . setTriggerType ( NotificationRuleTriggerType . ENTITY_ACTION ) ;
EntityActionNotificationRuleTriggerConfig triggerConfig = new EntityActionNotificationRuleTriggerConfig ( ) ;
triggerConfig . setEntityTypes ( Set . of ( EntityType . DEVICE ) ) ;
triggerConfig . setCreated ( true ) ;
triggerConfig . setUpdated ( true ) ;
triggerConfig . setDeleted ( true ) ;
createNotificationRule ( triggerConfig , "${actionType}: ${entityType} [${entityId}]" ,
"User: ${userEmail}" , createNotificationTarget ( tenantAdminUserId ) . getId ( ) ) ;
Device device = checkNotificationAfter ( ( ) - > {
return createDevice ( "DEVICE!!!" , "default" , "12345" ) ;
} , ( notification , newDevice ) - > {
assertThat ( notification . getSubject ( ) ) . isEqualTo ( "added: Device [" + newDevice . getId ( ) + "]" ) ;
assertThat ( notification . getText ( ) ) . isEqualTo ( "User: " + TENANT_ADMIN_EMAIL ) ;
} ) ;
DefaultNotificationRuleRecipientsConfig recipientsConfig = new DefaultNotificationRuleRecipientsConfig ( ) ;
recipientsConfig . setTriggerType ( NotificationRuleTriggerType . ENTITY_ACTION ) ;
recipientsConfig . setTargets ( List . of ( createNotificationTarget ( tenantAdminUserId ) . getUuidId ( ) ) ) ;
notificationRule . setTriggerConfig ( triggerConfig ) ;
notificationRule . setRecipientsConfig ( recipientsConfig ) ;
notificationRule = saveNotificationRule ( notificationRule ) ;
getWsClient ( ) . subscribeForUnreadNotifications ( 10 ) . waitForReply ( true ) ;
getWsClient ( ) . registerWaitForUpdate ( ) ;
Device device = createDevice ( "DEVICE!!!" , "default" , "12345" ) ;
getWsClient ( ) . waitForUpdate ( true ) ;
Notification notification = getWsClient ( ) . getLastDataUpdate ( ) . getUpdate ( ) ;
assertThat ( notification . getSubject ( ) ) . isEqualTo ( "added: Device [" + device . getId ( ) + "]" ) ;
assertThat ( notification . getText ( ) ) . isEqualTo ( "User: " + TENANT_ADMIN_EMAIL ) ;
getWsClient ( ) . registerWaitForUpdate ( ) ;
device . setName ( "Updated name" ) ;
device = doPost ( "/api/device" , device , Device . class ) ;
getWsClient ( ) . waitForUpdate ( true ) ;
notification = getWsClient ( ) . getLastDataUpdate ( ) . getUpdate ( ) ;
assertThat ( notification . getSubject ( ) ) . isEqualTo ( "updated: Device [" + device . getId ( ) + "]" ) ;
getWsClient ( ) . registerWaitForUpdate ( ) ;
doDelete ( "/api/device/" + device . getId ( ) ) . andExpect ( status ( ) . isOk ( ) ) ;
getWsClient ( ) . waitForUpdate ( true ) ;
checkNotificationAfter ( ( ) - > {
device . setName ( "Updated name" ) ;
doPost ( "/api/device" , device , Device . class ) ;
} , notification - > {
assertThat ( notification . getSubject ( ) ) . isEqualTo ( "updated: Device [" + device . getId ( ) + "]" ) ;
} ) ;
notification = getWsClient ( ) . getLastDataUpdate ( ) . getUpdate ( ) ;
assertThat ( notification . getSubject ( ) ) . isEqualTo ( "deleted: Device [" + device . getId ( ) + "]" ) ;
checkNotificationAfter ( ( ) - > {
doDelete ( "/api/device/" + device . getId ( ) ) . andExpect ( status ( ) . isOk ( ) ) ;
} , notification - > {
assertThat ( notification . getSubject ( ) ) . isEqualTo ( "deleted: Device [" + device . getId ( ) + "]" ) ;
} ) ;
}
@Test
@ -215,7 +205,7 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest {
clients . forEach ( ( expectedDelay , wsClient ) - > {
Notification notification = wsClient . getLastDataUpdate ( ) . getUpdate ( ) ;
double actualDelay = ( double ) ( notification . getCreatedTime ( ) - ts ) / 1000 ;
assertThat ( actualDelay ) . isCloseTo ( expectedDelay , offset ( 0 . 5 ) ) ;
assertThat ( actualDelay ) . isCloseTo ( expectedDelay , offset ( 2 . 0 ) ) ;
assertThat ( notification . getSubject ( ) ) . isEqualTo ( "Alarm type: " + alarmType + ", status: " + AlarmStatus . ACTIVE_UNACK + ", " +
"severity: " + AlarmSeverity . CRITICAL . toString ( ) . toLowerCase ( ) + ", deviceId: " + device . getId ( ) ) ;
@ -312,6 +302,62 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest {
assertThat ( findNotificationRequests ( EntityType . ALARM ) . getData ( ) ) . filteredOn ( NotificationRequest : : isScheduled ) . isEmpty ( ) ;
}
@Test
public void testNotificationRuleProcessing_entitiesLimit ( ) throws Exception {
TenantProfile tenantProfile = tenantProfileService . findDefaultTenantProfile ( TenantId . SYS_TENANT_ID ) ;
TenantProfileData profileData = tenantProfile . getProfileData ( ) ;
DefaultTenantProfileConfiguration profileConfiguration = ( DefaultTenantProfileConfiguration ) profileData . getConfiguration ( ) ;
int limit = 5 ;
profileConfiguration . setMaxDevices ( limit ) ;
profileConfiguration . setMaxAssets ( limit ) ;
profileConfiguration . setMaxCustomers ( limit ) ;
profileConfiguration . setMaxUsers ( limit ) ;
profileConfiguration . setMaxDashboards ( limit ) ;
profileConfiguration . setMaxRuleChains ( limit ) ;
tenantProfile . setProfileData ( profileData ) ;
tbTenantProfileService . save ( TenantId . SYS_TENANT_ID , tenantProfile , null ) ;
EntitiesLimitNotificationRuleTriggerConfig triggerConfig = EntitiesLimitNotificationRuleTriggerConfig . builder ( )
. entityTypes ( null ) . threshold ( 0 . 8f )
. build ( ) ;
loginSysAdmin ( ) ;
NotificationRule rule = createNotificationRule ( triggerConfig , "${entityType}s limit will be reached soon" ,
"${entityType}s usage: ${currentCount}/${limit} (${percents}%)" , createNotificationTarget ( tenantAdminUserId ) . getId ( ) ) ;
int threshold = ( int ) ( limit * 0 . 8 ) ;
loginTenantAdmin ( ) ;
checkNotificationAfter ( ( ) - > {
for ( int i = 1 ; i < = threshold ; i + + ) {
createDevice ( i + "" , i + "" ) ;
}
} , notification - > {
assertThat ( notification . getText ( ) ) . isEqualTo ( "Devices usage: " + threshold + "/" + limit + " (80%)" ) ;
} ) ;
checkNotificationAfter ( ( ) - > {
for ( int i = 1 ; i < = threshold ; i + + ) {
Asset asset = new Asset ( ) ;
asset . setType ( "Test" ) ;
asset . setName ( i + "" ) ;
doPost ( "/api/asset" , asset ) ;
}
} , notification - > {
assertThat ( notification . getText ( ) ) . isEqualTo ( "Assets usage: " + threshold + "/" + limit + " (80%)" ) ;
} ) ;
triggerConfig . setThreshold ( 1 . 0f ) ;
rule . setTriggerConfig ( triggerConfig ) ;
loginSysAdmin ( ) ;
saveNotificationRule ( rule ) ;
loginTenantAdmin ( ) ;
checkNotificationAfter ( ( ) - > {
createDevice ( limit + "" , limit + "" ) ;
} , notification - > {
assertThat ( notification . getText ( ) ) . isEqualTo ( "Devices usage: " + limit + "/" + limit + " (100%)" ) ;
} ) ;
}
@Test
public void testNotificationRuleInfo ( ) throws Exception {
NotificationDeliveryMethod [ ] deliveryMethods = { NotificationDeliveryMethod . WEB , NotificationDeliveryMethod . EMAIL } ;
@ -345,9 +391,7 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest {
DefaultTenantProfileConfiguration profileConfiguration = ( DefaultTenantProfileConfiguration ) profileData . getConfiguration ( ) ;
profileConfiguration . setTenantNotificationRequestsPerRuleRateLimit ( notificationRequestsLimit + ":300" ) ;
tenantProfile . setProfileData ( profileData ) ;
loginSysAdmin ( ) ;
doPost ( "/api/tenantProfile" , tenantProfile ) . andExpect ( status ( ) . isOk ( ) ) ;
loginTenantAdmin ( ) ;
tbTenantProfileService . save ( TenantId . SYS_TENANT_ID , tenantProfile , null ) ;
NotificationRule rule = new NotificationRule ( ) ;
rule . setName ( "Device created" ) ;
@ -386,6 +430,45 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest {
assertThat ( getMyNotifications ( false , 100 ) ) . size ( ) . isEqualTo ( notificationRequestsLimit ) ;
}
private < R > R checkNotificationAfter ( Callable < R > action , BiConsumer < Notification , R > check ) throws Exception {
if ( getWsClient ( ) . getLastDataUpdate ( ) = = null ) {
getWsClient ( ) . subscribeForUnreadNotifications ( 10 ) . waitForReply ( true ) ;
}
getWsClient ( ) . registerWaitForUpdate ( ) ;
R result = action . call ( ) ;
getWsClient ( ) . waitForUpdate ( true ) ;
check . accept ( getWsClient ( ) . getLastDataUpdate ( ) . getUpdate ( ) , result ) ;
return result ;
}
private void checkNotificationAfter ( ThrowingRunnable action , Consumer < Notification > check ) throws Exception {
checkNotificationAfter ( ( ) - > {
try {
action . run ( ) ;
return null ;
} catch ( Throwable e ) {
throw new Exception ( e ) ;
}
} , ( notification , r ) - > check . accept ( notification ) ) ;
}
private NotificationRule createNotificationRule ( NotificationRuleTriggerConfig triggerConfig , String subject , String text , NotificationTargetId . . . targets ) {
NotificationTemplate template = createNotificationTemplate ( NotificationType . valueOf ( triggerConfig . getTriggerType ( ) . toString ( ) ) , subject , text , NotificationDeliveryMethod . WEB ) ;
NotificationRule rule = new NotificationRule ( ) ;
rule . setName ( triggerConfig . getTriggerType ( ) + " [" + Arrays . toString ( targets ) + "]" ) ;
rule . setTemplateId ( template . getId ( ) ) ;
rule . setTriggerType ( triggerConfig . getTriggerType ( ) ) ;
rule . setTriggerConfig ( triggerConfig ) ;
DefaultNotificationRuleRecipientsConfig recipientsConfig = new DefaultNotificationRuleRecipientsConfig ( ) ;
recipientsConfig . setTriggerType ( triggerConfig . getTriggerType ( ) ) ;
recipientsConfig . setTargets ( DaoUtil . toUUIDs ( List . of ( targets ) ) ) ;
rule . setRecipientsConfig ( recipientsConfig ) ;
return saveNotificationRule ( rule ) ;
}
private DeviceProfile createDeviceProfileWithAlarmRules ( String alarmType ) {
DeviceProfile deviceProfile = createDeviceProfile ( "For notification rule test" ) ;
deviceProfile . setTenantId ( tenantId ) ;