Merge branch 'develop/3.5' of github.com:AndriiLandiak/thingsboard into feature/x509-device-cert-impr
This commit is contained in:
commit
e7fc00e53d
@ -6,7 +6,8 @@
|
||||
"firstRuleNodeId": null,
|
||||
"root": true,
|
||||
"debugMode": false,
|
||||
"configuration": null
|
||||
"configuration": null,
|
||||
"externalId": null
|
||||
},
|
||||
"metadata": {
|
||||
"firstNodeIndex": 0,
|
||||
@ -23,7 +24,8 @@
|
||||
"configuration": {
|
||||
"persistAlarmRulesState": false,
|
||||
"fetchAlarmRulesStateOnStart": false
|
||||
}
|
||||
},
|
||||
"externalId": null
|
||||
},
|
||||
{
|
||||
"additionalInfo": {
|
||||
@ -35,7 +37,8 @@
|
||||
"debugMode": false,
|
||||
"configuration": {
|
||||
"defaultTTL": 0
|
||||
}
|
||||
},
|
||||
"externalId": null
|
||||
},
|
||||
{
|
||||
"additionalInfo": {
|
||||
@ -46,8 +49,10 @@
|
||||
"name": "Save Client Attributes",
|
||||
"debugMode": false,
|
||||
"configuration": {
|
||||
"scope": "CLIENT_SCOPE"
|
||||
}
|
||||
"scope": "CLIENT_SCOPE",
|
||||
"notifyDevice": "false"
|
||||
},
|
||||
"externalId": null
|
||||
},
|
||||
{
|
||||
"additionalInfo": {
|
||||
@ -59,7 +64,8 @@
|
||||
"debugMode": false,
|
||||
"configuration": {
|
||||
"version": 0
|
||||
}
|
||||
},
|
||||
"externalId": null
|
||||
},
|
||||
{
|
||||
"additionalInfo": {
|
||||
@ -73,7 +79,8 @@
|
||||
"scriptLang": "TBEL",
|
||||
"jsScript": "return '\\nIncoming message:\\n' + JSON.stringify(msg) + '\\nIncoming metadata:\\n' + JSON.stringify(metadata);",
|
||||
"tbelScript": "return '\\nIncoming message:\\n' + JSON.stringify(msg) + '\\nIncoming metadata:\\n' + JSON.stringify(metadata);"
|
||||
}
|
||||
},
|
||||
"externalId": null
|
||||
},
|
||||
{
|
||||
"additionalInfo": {
|
||||
@ -87,7 +94,8 @@
|
||||
"scriptLang": "TBEL",
|
||||
"jsScript": "return '\\nIncoming message:\\n' + JSON.stringify(msg) + '\\nIncoming metadata:\\n' + JSON.stringify(metadata);",
|
||||
"tbelScript": "return '\\nIncoming message:\\n' + JSON.stringify(msg) + '\\nIncoming metadata:\\n' + JSON.stringify(metadata);"
|
||||
}
|
||||
},
|
||||
"externalId": null
|
||||
},
|
||||
{
|
||||
"additionalInfo": {
|
||||
@ -99,19 +107,34 @@
|
||||
"debugMode": false,
|
||||
"configuration": {
|
||||
"timeoutInSeconds": 60
|
||||
}
|
||||
},
|
||||
"externalId": null
|
||||
},
|
||||
{
|
||||
"additionalInfo": {
|
||||
"layoutX": 1129,
|
||||
"layoutY": 52
|
||||
"layoutX": 1126,
|
||||
"layoutY": 104
|
||||
},
|
||||
"type": "org.thingsboard.rule.engine.edge.TbMsgPushToCloudNode",
|
||||
"name": "Push to cloud",
|
||||
"debugMode": false,
|
||||
"configuration": {
|
||||
"scope": "SERVER_SCOPE"
|
||||
}
|
||||
},
|
||||
"externalId": null
|
||||
},
|
||||
{
|
||||
"additionalInfo": {
|
||||
"layoutX": 826,
|
||||
"layoutY": 601
|
||||
},
|
||||
"type": "org.thingsboard.rule.engine.edge.TbMsgPushToCloudNode",
|
||||
"name": "Push to cloud",
|
||||
"debugMode": false,
|
||||
"configuration": {
|
||||
"scope": "SERVER_SCOPE"
|
||||
},
|
||||
"externalId": null
|
||||
}
|
||||
],
|
||||
"connections": [
|
||||
@ -132,24 +155,14 @@
|
||||
},
|
||||
{
|
||||
"fromIndex": 3,
|
||||
"toIndex": 6,
|
||||
"type": "RPC Request to Device"
|
||||
},
|
||||
{
|
||||
"fromIndex": 3,
|
||||
"toIndex": 5,
|
||||
"type": "Other"
|
||||
"toIndex": 1,
|
||||
"type": "Post telemetry"
|
||||
},
|
||||
{
|
||||
"fromIndex": 3,
|
||||
"toIndex": 2,
|
||||
"type": "Post attributes"
|
||||
},
|
||||
{
|
||||
"fromIndex": 3,
|
||||
"toIndex": 1,
|
||||
"type": "Post telemetry"
|
||||
},
|
||||
{
|
||||
"fromIndex": 3,
|
||||
"toIndex": 4,
|
||||
@ -157,23 +170,23 @@
|
||||
},
|
||||
{
|
||||
"fromIndex": 3,
|
||||
"toIndex": 7,
|
||||
"type": "Attributes Updated"
|
||||
"toIndex": 5,
|
||||
"type": "Other"
|
||||
},
|
||||
{
|
||||
"fromIndex": 3,
|
||||
"toIndex": 7,
|
||||
"toIndex": 6,
|
||||
"type": "RPC Request to Device"
|
||||
},
|
||||
{
|
||||
"fromIndex": 3,
|
||||
"toIndex": 8,
|
||||
"type": "Attributes Deleted"
|
||||
},
|
||||
{
|
||||
"fromIndex": 3,
|
||||
"toIndex": 7,
|
||||
"type": "Timeseries Deleted"
|
||||
},
|
||||
{
|
||||
"fromIndex": 3,
|
||||
"toIndex": 7,
|
||||
"type": "Timeseries Updated"
|
||||
"toIndex": 8,
|
||||
"type": "Attributes Updated"
|
||||
}
|
||||
],
|
||||
"ruleChainConnections": null
|
||||
@ -23,7 +23,7 @@
|
||||
"dataKeySettingsSchema": "",
|
||||
"settingsDirective": "tb-alarms-table-widget-settings",
|
||||
"dataKeySettingsDirective": "tb-alarms-table-key-settings",
|
||||
"defaultConfig": "{\"timewindow\":{\"realtime\":{\"interval\":1000,\"timewindowMs\":86400000},\"aggregation\":{\"type\":\"NONE\",\"limit\":200}},\"showTitle\":true,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"4px\",\"settings\":{\"enableSelection\":true,\"enableSearch\":true,\"displayDetails\":true,\"allowAcknowledgment\":true,\"allowClear\":true,\"allowAssign\":true,\"displayComments\":true,\"displayPagination\":true,\"defaultPageSize\":10,\"defaultSortOrder\":\"-createdTime\",\"enableSelectColumnDisplay\":true,\"enableStickyAction\":false,\"enableFilter\":true},\"title\":\"Alarms table\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400,\"padding\":\"5px 10px 5px 10px\"},\"useDashboardTimewindow\":false,\"showLegend\":false,\"alarmSource\":{\"type\":\"function\",\"dataKeys\":[{\"name\":\"createdTime\",\"type\":\"alarm\",\"label\":\"Created time\",\"color\":\"#2196f3\",\"settings\":{\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.021092237451093787},{\"name\":\"originator\",\"type\":\"alarm\",\"label\":\"Originator\",\"color\":\"#4caf50\",\"settings\":{\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.2780007688856758},{\"name\":\"type\",\"type\":\"alarm\",\"label\":\"Type\",\"color\":\"#f44336\",\"settings\":{\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.7323586880398418},{\"name\":\"severity\",\"type\":\"alarm\",\"label\":\"Severity\",\"color\":\"#ffc107\",\"settings\":{\"useCellStyleFunction\":false,\"useCellContentFunction\":false},\"_hash\":0.09927019860088193},{\"name\":\"status\",\"type\":\"alarm\",\"label\":\"Status\",\"color\":\"#607d8b\",\"settings\":{\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.6588418951443418},{\"name\":\"assignee\",\"type\":\"alarm\",\"label\":\"Assignee\",\"color\":\"#9c27b0\",\"settings\":{},\"_hash\":0.5008441077416634}],\"entityAliasId\":null,\"name\":\"alarms\"},\"alarmSearchStatus\":\"ANY\",\"alarmsPollingInterval\":5,\"showTitleIcon\":false,\"titleIcon\":\"more_horiz\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"displayTimewindow\":true,\"actions\":{},\"alarmStatusList\":[],\"alarmSeverityList\":[],\"alarmTypeList\":[],\"searchPropagatedAlarms\":false}"
|
||||
"defaultConfig": "{\"timewindow\":{\"realtime\":{\"interval\":1000,\"timewindowMs\":86400000},\"aggregation\":{\"type\":\"NONE\",\"limit\":200}},\"showTitle\":true,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"4px\",\"settings\":{\"enableSelection\":true,\"enableSearch\":true,\"displayDetails\":true,\"allowAcknowledgment\":true,\"allowClear\":true,\"allowAssign\":true,\"displayActivity\":true,\"displayPagination\":true,\"defaultPageSize\":10,\"defaultSortOrder\":\"-createdTime\",\"enableSelectColumnDisplay\":true,\"enableStickyAction\":false,\"enableFilter\":true},\"title\":\"Alarms table\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400,\"padding\":\"5px 10px 5px 10px\"},\"useDashboardTimewindow\":false,\"showLegend\":false,\"alarmSource\":{\"type\":\"function\",\"dataKeys\":[{\"name\":\"createdTime\",\"type\":\"alarm\",\"label\":\"Created time\",\"color\":\"#2196f3\",\"settings\":{\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.021092237451093787},{\"name\":\"originator\",\"type\":\"alarm\",\"label\":\"Originator\",\"color\":\"#4caf50\",\"settings\":{\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.2780007688856758},{\"name\":\"type\",\"type\":\"alarm\",\"label\":\"Type\",\"color\":\"#f44336\",\"settings\":{\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.7323586880398418},{\"name\":\"severity\",\"type\":\"alarm\",\"label\":\"Severity\",\"color\":\"#ffc107\",\"settings\":{\"useCellStyleFunction\":false,\"useCellContentFunction\":false},\"_hash\":0.09927019860088193},{\"name\":\"status\",\"type\":\"alarm\",\"label\":\"Status\",\"color\":\"#607d8b\",\"settings\":{\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.6588418951443418},{\"name\":\"assignee\",\"type\":\"alarm\",\"label\":\"Assignee\",\"color\":\"#9c27b0\",\"settings\":{},\"_hash\":0.5008441077416634}],\"entityAliasId\":null,\"name\":\"alarms\"},\"alarmSearchStatus\":\"ANY\",\"alarmsPollingInterval\":5,\"showTitleIcon\":false,\"titleIcon\":\"more_horiz\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"displayTimewindow\":true,\"actions\":{},\"alarmStatusList\":[],\"alarmSeverityList\":[],\"alarmTypeList\":[],\"searchPropagatedAlarms\":false}"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
File diff suppressed because one or more lines are too long
@ -167,11 +167,12 @@ CREATE INDEX IF NOT EXISTS idx_notification_recipient_id_created_time ON notific
|
||||
ALTER TABLE tb_user ADD COLUMN IF NOT EXISTS phone VARCHAR(255);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS user_settings (
|
||||
user_id uuid NOT NULL CONSTRAINT user_settings_pkey PRIMARY KEY,
|
||||
settings varchar(100000),
|
||||
CONSTRAINT fk_user_id FOREIGN KEY (user_id) REFERENCES tb_user(id) ON DELETE CASCADE
|
||||
user_id uuid NOT NULL,
|
||||
type VARCHAR(50) NOT NULL,
|
||||
settings varchar(10000),
|
||||
CONSTRAINT fk_user_id FOREIGN KEY (user_id) REFERENCES tb_user(id) ON DELETE CASCADE,
|
||||
CONSTRAINT user_settings_pkey PRIMARY KEY (user_id, type)
|
||||
);
|
||||
|
||||
-- ALARM INFO VIEW
|
||||
|
||||
DROP VIEW IF EXISTS alarm_info CASCADE;
|
||||
@ -193,15 +194,15 @@ COALESCE(CASE WHEN a.originator_type = 0 THEN (select title from tenant where id
|
||||
WHEN a.originator_type = 18 THEN (select name from edge where id = a.originator_id) END
|
||||
, 'Deleted') originator_name,
|
||||
COALESCE(CASE WHEN a.originator_type = 0 THEN (select title from tenant where id = a.originator_id)
|
||||
WHEN a.originator_type = 1 THEN (select COALESCE(title, email) from customer where id = a.originator_id)
|
||||
WHEN a.originator_type = 1 THEN (select COALESCE(NULLIF(title, ''), email) from customer where id = a.originator_id)
|
||||
WHEN a.originator_type = 2 THEN (select email from tb_user where id = a.originator_id)
|
||||
WHEN a.originator_type = 3 THEN (select title from dashboard where id = a.originator_id)
|
||||
WHEN a.originator_type = 4 THEN (select COALESCE(label, name) from asset where id = a.originator_id)
|
||||
WHEN a.originator_type = 5 THEN (select COALESCE(label, name) from device where id = a.originator_id)
|
||||
WHEN a.originator_type = 4 THEN (select COALESCE(NULLIF(label, ''), name) from asset where id = a.originator_id)
|
||||
WHEN a.originator_type = 5 THEN (select COALESCE(NULLIF(label, ''), name) from device where id = a.originator_id)
|
||||
WHEN a.originator_type = 9 THEN (select name from entity_view where id = a.originator_id)
|
||||
WHEN a.originator_type = 13 THEN (select name from device_profile where id = a.originator_id)
|
||||
WHEN a.originator_type = 14 THEN (select name from asset_profile where id = a.originator_id)
|
||||
WHEN a.originator_type = 18 THEN (select COALESCE(label, name) from edge where id = a.originator_id) END
|
||||
WHEN a.originator_type = 18 THEN (select COALESCE(NULLIF(label, ''), name) from edge where id = a.originator_id) END
|
||||
, 'Deleted') as originator_label,
|
||||
u.first_name as assignee_first_name, u.last_name as assignee_last_name, u.email as assignee_email
|
||||
FROM alarm a
|
||||
|
||||
@ -71,7 +71,6 @@ import org.thingsboard.server.dao.event.EventService;
|
||||
import org.thingsboard.server.dao.nosql.CassandraBufferedRateReadExecutor;
|
||||
import org.thingsboard.server.dao.nosql.CassandraBufferedRateWriteExecutor;
|
||||
import org.thingsboard.server.dao.notification.NotificationRequestService;
|
||||
import org.thingsboard.server.dao.notification.NotificationRuleProcessingService;
|
||||
import org.thingsboard.server.dao.notification.NotificationRuleService;
|
||||
import org.thingsboard.server.dao.notification.NotificationTargetService;
|
||||
import org.thingsboard.server.dao.notification.NotificationTemplateService;
|
||||
@ -90,6 +89,7 @@ import org.thingsboard.server.dao.widget.WidgetTypeService;
|
||||
import org.thingsboard.server.dao.widget.WidgetsBundleService;
|
||||
import org.thingsboard.server.queue.discovery.PartitionService;
|
||||
import org.thingsboard.server.queue.discovery.TbServiceInfoProvider;
|
||||
import org.thingsboard.server.common.msg.notification.NotificationRuleProcessor;
|
||||
import org.thingsboard.server.queue.util.DataDecodingEncodingService;
|
||||
import org.thingsboard.server.service.apiusage.TbApiUsageStateService;
|
||||
import org.thingsboard.server.service.component.ComponentDiscoveryService;
|
||||
@ -341,7 +341,7 @@ public class ActorSystemContext {
|
||||
|
||||
@Autowired
|
||||
@Getter
|
||||
private NotificationRuleProcessingService notificationRuleProcessingService;
|
||||
private NotificationRuleProcessor notificationRuleProcessor;
|
||||
|
||||
@Autowired
|
||||
@Getter
|
||||
|
||||
@ -24,7 +24,7 @@ import org.thingsboard.server.common.data.id.RuleChainId;
|
||||
import org.thingsboard.server.common.data.id.TenantId;
|
||||
import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
|
||||
import org.thingsboard.server.common.msg.TbActorStopReason;
|
||||
import org.thingsboard.server.dao.notification.trigger.RuleEngineComponentLifecycleEventTrigger;
|
||||
import org.thingsboard.server.common.msg.notification.trigger.RuleEngineComponentLifecycleEventTrigger;
|
||||
|
||||
public abstract class RuleEngineComponentActor<T extends EntityId, P extends ComponentMsgProcessor<T>> extends ComponentActor<T, P> {
|
||||
|
||||
@ -50,7 +50,8 @@ public abstract class RuleEngineComponentActor<T extends EntityId, P extends Com
|
||||
}
|
||||
|
||||
private void processNotificationRule(ComponentLifecycleEvent event, Throwable e) {
|
||||
systemContext.getNotificationRuleProcessingService().process(tenantId, RuleEngineComponentLifecycleEventTrigger.builder()
|
||||
systemContext.getNotificationRuleProcessor().process(RuleEngineComponentLifecycleEventTrigger.builder()
|
||||
.tenantId(tenantId)
|
||||
.ruleChainId(getRuleChainId())
|
||||
.ruleChainName(getRuleChainName())
|
||||
.componentId(id)
|
||||
|
||||
@ -49,6 +49,7 @@ import org.thingsboard.server.common.msg.TbMsg;
|
||||
import org.thingsboard.server.common.msg.aware.DeviceAwareMsg;
|
||||
import org.thingsboard.server.common.msg.aware.RuleChainAwareMsg;
|
||||
import org.thingsboard.server.common.msg.edge.EdgeSessionMsg;
|
||||
import org.thingsboard.server.common.msg.notification.trigger.RuleEngineMsgTrigger;
|
||||
import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg;
|
||||
import org.thingsboard.server.common.msg.queue.PartitionChangeMsg;
|
||||
import org.thingsboard.server.common.msg.queue.QueueToRuleEngineMsg;
|
||||
@ -209,7 +210,10 @@ public class TenantActor extends RuleChainManagerActor {
|
||||
log.trace("[{}] Ack message because Rule Engine is disabled", tenantId);
|
||||
tbMsg.getCallback().onSuccess();
|
||||
}
|
||||
systemContext.getNotificationRuleProcessingService().process(tenantId, tbMsg);
|
||||
systemContext.getNotificationRuleProcessor().process(RuleEngineMsgTrigger.builder()
|
||||
.tenantId(tenantId)
|
||||
.msg(tbMsg)
|
||||
.build());
|
||||
}
|
||||
|
||||
private void onRuleChainMsg(RuleChainAwareMsg msg) {
|
||||
|
||||
@ -103,6 +103,7 @@ import org.thingsboard.server.common.data.rpc.Rpc;
|
||||
import org.thingsboard.server.common.data.rule.RuleChain;
|
||||
import org.thingsboard.server.common.data.rule.RuleChainType;
|
||||
import org.thingsboard.server.common.data.rule.RuleNode;
|
||||
import org.thingsboard.server.common.data.settings.UserDashboardAction;
|
||||
import org.thingsboard.server.common.data.util.ThrowingBiFunction;
|
||||
import org.thingsboard.server.common.data.widget.WidgetTypeDetails;
|
||||
import org.thingsboard.server.common.data.widget.WidgetsBundle;
|
||||
@ -146,6 +147,7 @@ import org.thingsboard.server.service.component.ComponentDiscoveryService;
|
||||
import org.thingsboard.server.service.edge.instructions.EdgeInstallService;
|
||||
import org.thingsboard.server.service.edge.rpc.EdgeRpcService;
|
||||
import org.thingsboard.server.service.entitiy.TbNotificationEntityService;
|
||||
import org.thingsboard.server.service.entitiy.user.TbUserSettingsService;
|
||||
import org.thingsboard.server.service.ota.OtaPackageStateService;
|
||||
import org.thingsboard.server.service.profile.TbAssetProfileCache;
|
||||
import org.thingsboard.server.service.profile.TbDeviceProfileCache;
|
||||
@ -168,6 +170,7 @@ import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.thingsboard.server.common.data.StringUtils.isNotEmpty;
|
||||
@ -202,7 +205,7 @@ public abstract class BaseController {
|
||||
protected UserService userService;
|
||||
|
||||
@Autowired
|
||||
protected UserSettingsService userSettingsService;
|
||||
protected TbUserSettingsService userSettingsService;
|
||||
|
||||
@Autowired
|
||||
protected DeviceService deviceService;
|
||||
@ -445,6 +448,14 @@ public abstract class BaseController {
|
||||
}
|
||||
}
|
||||
|
||||
protected <T> T checkEnumParameter(String name, String param, Function<String, T> valueOf) throws ThingsboardException {
|
||||
try {
|
||||
return valueOf.apply(param.toUpperCase());
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new ThingsboardException(name + " \"" + param + "\" is not supported!", ThingsboardErrorCode.BAD_REQUEST_PARAMS);
|
||||
}
|
||||
}
|
||||
|
||||
UUID toUUID(String id) throws ThingsboardException {
|
||||
try {
|
||||
return UUID.fromString(id);
|
||||
|
||||
@ -31,6 +31,7 @@ import org.thingsboard.server.common.data.exception.ThingsboardException;
|
||||
import org.thingsboard.server.common.data.id.TenantId;
|
||||
import org.thingsboard.server.common.data.id.UserId;
|
||||
import org.thingsboard.server.common.data.page.PageData;
|
||||
import org.thingsboard.server.common.data.query.AlarmCountQuery;
|
||||
import org.thingsboard.server.common.data.query.AlarmData;
|
||||
import org.thingsboard.server.common.data.query.AlarmDataQuery;
|
||||
import org.thingsboard.server.common.data.query.EntityCountQuery;
|
||||
@ -93,6 +94,20 @@ public class EntityQueryController extends BaseController {
|
||||
return this.entityQueryService.findAlarmDataByQuery(getCurrentUser(), query);
|
||||
}
|
||||
|
||||
@ApiOperation(value = "Count Alarms by Query (countAlarmsByQuery)", notes = "Returns the number of alarms that match the query definition.")
|
||||
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
|
||||
@RequestMapping(value = "/alarmsQuery/count", method = RequestMethod.POST)
|
||||
@ResponseBody
|
||||
public long countAlarmsByQuery(@ApiParam(value = "A JSON value representing the alarm count query.")
|
||||
@RequestBody AlarmCountQuery query) throws ThingsboardException {
|
||||
checkNotNull(query);
|
||||
UserId assigneeId = query.getAssigneeId();
|
||||
if (assigneeId != null) {
|
||||
checkUserId(assigneeId, Operation.READ);
|
||||
}
|
||||
return this.entityQueryService.countAlarmsByQuery(getCurrentUser(), query);
|
||||
}
|
||||
|
||||
@ApiOperation(value = "Find Entity Keys by Query",
|
||||
notes = "Uses entity data query (see 'Find Entity Data by Query') to find first 100 entities. Then fetch and return all unique time-series and/or attribute keys. Used mostly for UI hints.")
|
||||
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
|
||||
|
||||
@ -42,10 +42,12 @@ import org.thingsboard.server.common.data.notification.NotificationDeliveryMetho
|
||||
import org.thingsboard.server.common.data.notification.NotificationRequest;
|
||||
import org.thingsboard.server.common.data.notification.NotificationRequestInfo;
|
||||
import org.thingsboard.server.common.data.notification.NotificationRequestPreview;
|
||||
import org.thingsboard.server.common.data.notification.info.UserOriginatedNotificationInfo;
|
||||
import org.thingsboard.server.common.data.notification.settings.NotificationSettings;
|
||||
import org.thingsboard.server.common.data.notification.targets.NotificationRecipient;
|
||||
import org.thingsboard.server.common.data.notification.targets.NotificationTarget;
|
||||
import org.thingsboard.server.common.data.notification.targets.NotificationTargetType;
|
||||
import org.thingsboard.server.common.data.notification.targets.platform.PlatformUsersNotificationTargetConfig;
|
||||
import org.thingsboard.server.common.data.notification.targets.slack.SlackNotificationTargetConfig;
|
||||
import org.thingsboard.server.common.data.notification.template.DeliveryMethodNotificationTemplate;
|
||||
import org.thingsboard.server.common.data.notification.template.NotificationTemplate;
|
||||
import org.thingsboard.server.common.data.page.PageData;
|
||||
@ -62,7 +64,6 @@ import org.thingsboard.server.service.security.permission.Operation;
|
||||
import org.thingsboard.server.service.security.permission.Resource;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
@ -187,14 +188,12 @@ public class NotificationController extends BaseController {
|
||||
checkEntity(notificationRequest.getId(), notificationRequest, NOTIFICATION);
|
||||
|
||||
notificationRequest.setOriginatorEntityId(user.getId());
|
||||
if (notificationRequest.getInfo() != null && !(notificationRequest.getInfo() instanceof UserOriginatedNotificationInfo)) {
|
||||
throw new IllegalArgumentException("Unsupported notification info type");
|
||||
}
|
||||
notificationRequest.setInfo(null);
|
||||
notificationRequest.setRuleId(null);
|
||||
notificationRequest.setStatus(null);
|
||||
notificationRequest.setStats(null);
|
||||
|
||||
return doSaveAndLog(EntityType.NOTIFICATION_REQUEST, notificationRequest, notificationCenter::processNotificationRequest);
|
||||
return doSaveAndLog(EntityType.NOTIFICATION_REQUEST, notificationRequest, (tenantId, request) -> notificationCenter.processNotificationRequest(tenantId, request, null));
|
||||
}
|
||||
|
||||
@PostMapping("/notification/request/preview")
|
||||
@ -217,45 +216,49 @@ public class NotificationController extends BaseController {
|
||||
NotificationProcessingContext tmpProcessingCtx = NotificationProcessingContext.builder()
|
||||
.tenantId(user.getTenantId())
|
||||
.request(request)
|
||||
.settings(null)
|
||||
.template(template)
|
||||
.settings(null)
|
||||
.build();
|
||||
|
||||
Map<NotificationDeliveryMethod, DeliveryMethodNotificationTemplate> processedTemplates = tmpProcessingCtx.getDeliveryMethods().stream()
|
||||
.collect(Collectors.toMap(m -> m, deliveryMethod -> {
|
||||
Map<String, String> templateContext;
|
||||
NotificationRecipient recipient = null;
|
||||
if (NotificationTargetType.PLATFORM_USERS.getSupportedDeliveryMethods().contains(deliveryMethod)) {
|
||||
templateContext = tmpProcessingCtx.createTemplateContext(user);
|
||||
} else {
|
||||
templateContext = Collections.emptyMap();
|
||||
recipient = userService.findUserById(user.getTenantId(), user.getId());
|
||||
}
|
||||
return tmpProcessingCtx.getProcessedTemplate(deliveryMethod, templateContext);
|
||||
return tmpProcessingCtx.getProcessedTemplate(deliveryMethod, recipient);
|
||||
}));
|
||||
preview.setProcessedTemplates(processedTemplates);
|
||||
|
||||
// generic permission
|
||||
Set<User> recipientsPreview = new LinkedHashSet<>();
|
||||
Set<String> recipientsPreview = new LinkedHashSet<>();
|
||||
Map<String, Integer> recipientsCountByTarget = new HashMap<>();
|
||||
|
||||
List<NotificationTarget> targets = notificationTargetService.findNotificationTargetsByTenantIdAndIds(user.getTenantId(),
|
||||
request.getTargets().stream().map(NotificationTargetId::new).collect(Collectors.toList()));
|
||||
for (NotificationTarget target : targets) {
|
||||
int recipientsCount;
|
||||
List<NotificationRecipient> recipientsPart;
|
||||
if (target.getConfiguration().getType() == NotificationTargetType.PLATFORM_USERS) {
|
||||
PageData<User> recipients = notificationTargetService.findRecipientsForNotificationTargetConfig(user.getTenantId(), null,
|
||||
target.getConfiguration(), new PageLink(recipientsPreviewSize));
|
||||
PageData<User> recipients = notificationTargetService.findRecipientsForNotificationTargetConfig(user.getTenantId(),
|
||||
(PlatformUsersNotificationTargetConfig) target.getConfiguration(), new PageLink(recipientsPreviewSize));
|
||||
recipientsCount = (int) recipients.getTotalElements();
|
||||
for (User recipient : recipients.getData()) {
|
||||
if (recipientsPreview.size() < recipientsPreviewSize) {
|
||||
recipientsPreview.add(recipient);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
recipientsPart = recipients.getData().stream().map(r -> (NotificationRecipient) r).collect(Collectors.toList());
|
||||
} else {
|
||||
recipientsCount = 1;
|
||||
recipientsPart = List.of(((SlackNotificationTargetConfig) target.getConfiguration()).getConversation());
|
||||
}
|
||||
|
||||
for (NotificationRecipient recipient : recipientsPart) {
|
||||
if (recipientsPreview.size() < recipientsPreviewSize) {
|
||||
recipientsPreview.add(recipient.getTitle());
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
recipientsCountByTarget.put(target.getName(), recipientsCount);
|
||||
}
|
||||
|
||||
preview.setRecipientsPreview(recipientsPreview);
|
||||
preview.setRecipientsCountByTarget(recipientsCountByTarget);
|
||||
preview.setTotalRecipientsCount(recipientsCountByTarget.values().stream().mapToInt(Integer::intValue).sum());
|
||||
|
||||
@ -116,7 +116,7 @@ public class NotificationTargetController extends BaseController {
|
||||
}
|
||||
|
||||
PageLink pageLink = createPageLink(pageSize, page, null, null, null);
|
||||
return notificationTargetService.findRecipientsForNotificationTargetConfig(user.getTenantId(), null, notificationTarget.getConfiguration(), pageLink);
|
||||
return notificationTargetService.findRecipientsForNotificationTargetConfig(user.getTenantId(), (PlatformUsersNotificationTargetConfig) notificationTarget.getConfiguration(), pageLink);
|
||||
}
|
||||
|
||||
@GetMapping(value = "/targets", params = {"ids"})
|
||||
|
||||
@ -17,6 +17,7 @@ package org.thingsboard.server.controller;
|
||||
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.security.core.annotation.AuthenticationPrincipal;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
@ -136,16 +137,20 @@ public class NotificationTemplateController extends BaseController {
|
||||
@GetMapping("/slack/conversations")
|
||||
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
|
||||
public List<SlackConversation> listSlackConversations(@RequestParam SlackConversationType type,
|
||||
@RequestParam(required = false) String token,
|
||||
@AuthenticationPrincipal SecurityUser user) {
|
||||
// generic permission
|
||||
NotificationSettings settings = notificationSettingsService.findNotificationSettings(user.getTenantId());
|
||||
SlackNotificationDeliveryMethodConfig slackConfig = (SlackNotificationDeliveryMethodConfig)
|
||||
settings.getDeliveryMethodsConfigs().get(NotificationDeliveryMethod.SLACK);
|
||||
if (slackConfig == null) {
|
||||
throw new IllegalArgumentException("Slack is not configured");
|
||||
if (StringUtils.isEmpty(token)) {
|
||||
NotificationSettings settings = notificationSettingsService.findNotificationSettings(user.getTenantId());
|
||||
SlackNotificationDeliveryMethodConfig slackConfig = (SlackNotificationDeliveryMethodConfig)
|
||||
settings.getDeliveryMethodsConfigs().get(NotificationDeliveryMethod.SLACK);
|
||||
if (slackConfig == null) {
|
||||
throw new IllegalArgumentException("Slack is not configured");
|
||||
}
|
||||
token = slackConfig.getBotToken();
|
||||
}
|
||||
|
||||
return slackService.listConversations(user.getTenantId(), slackConfig.getBotToken(), type);
|
||||
return slackService.listConversations(user.getTenantId(), token, type);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -0,0 +1,45 @@
|
||||
/**
|
||||
* Copyright © 2016-2023 The Thingsboard Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.thingsboard.server.controller;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.thingsboard.server.common.data.UsageInfo;
|
||||
import org.thingsboard.server.common.data.exception.ThingsboardException;
|
||||
import org.thingsboard.server.dao.usage.UsageInfoService;
|
||||
import org.thingsboard.server.queue.util.TbCoreComponent;
|
||||
|
||||
@RestController
|
||||
@TbCoreComponent
|
||||
@RequestMapping("/api")
|
||||
@Slf4j
|
||||
public class UsageInfoController extends BaseController {
|
||||
|
||||
@Autowired
|
||||
private UsageInfoService usageInfoService;
|
||||
|
||||
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
|
||||
@RequestMapping(value = "/usage", method = RequestMethod.GET)
|
||||
@ResponseBody
|
||||
public UsageInfo getTenantUsageInfo() throws ThingsboardException {
|
||||
return checkNotNull(usageInfoService.getUsageInfo(getCurrentUser().getTenantId()));
|
||||
}
|
||||
}
|
||||
@ -38,12 +38,14 @@ import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.thingsboard.common.util.JacksonUtil;
|
||||
import org.thingsboard.rule.engine.api.MailService;
|
||||
import org.thingsboard.server.common.data.DashboardInfo;
|
||||
import org.thingsboard.server.common.data.EntityType;
|
||||
import org.thingsboard.server.common.data.User;
|
||||
import org.thingsboard.server.common.data.UserEmailInfo;
|
||||
import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
|
||||
import org.thingsboard.server.common.data.exception.ThingsboardException;
|
||||
import org.thingsboard.server.common.data.id.CustomerId;
|
||||
import org.thingsboard.server.common.data.id.DashboardId;
|
||||
import org.thingsboard.server.common.data.id.TenantId;
|
||||
import org.thingsboard.server.common.data.id.UserId;
|
||||
import org.thingsboard.server.common.data.page.PageData;
|
||||
@ -55,9 +57,13 @@ import org.thingsboard.server.common.data.query.EntityTypeFilter;
|
||||
import org.thingsboard.server.common.data.query.TsValue;
|
||||
import org.thingsboard.server.common.data.security.Authority;
|
||||
import org.thingsboard.server.common.data.security.UserCredentials;
|
||||
import org.thingsboard.server.common.data.security.UserSettings;
|
||||
import org.thingsboard.server.common.data.settings.LastVisitedDashboardInfo;
|
||||
import org.thingsboard.server.common.data.settings.UserDashboardAction;
|
||||
import org.thingsboard.server.common.data.settings.UserDashboardsInfo;
|
||||
import org.thingsboard.server.common.data.settings.UserSettings;
|
||||
import org.thingsboard.server.common.data.security.event.UserCredentialsInvalidationEvent;
|
||||
import org.thingsboard.server.common.data.security.model.JwtPair;
|
||||
import org.thingsboard.server.common.data.settings.UserSettingsType;
|
||||
import org.thingsboard.server.queue.util.TbCoreComponent;
|
||||
import org.thingsboard.server.service.entitiy.user.TbUserService;
|
||||
import org.thingsboard.server.service.query.EntityQueryService;
|
||||
@ -76,6 +82,7 @@ import java.util.Map;
|
||||
import static org.thingsboard.server.common.data.query.EntityKeyType.ENTITY_FIELD;
|
||||
import static org.thingsboard.server.controller.ControllerConstants.CUSTOMER_ID;
|
||||
import static org.thingsboard.server.controller.ControllerConstants.CUSTOMER_ID_PARAM_DESCRIPTION;
|
||||
import static org.thingsboard.server.controller.ControllerConstants.DASHBOARD_ID_PARAM_DESCRIPTION;
|
||||
import static org.thingsboard.server.controller.ControllerConstants.DEFAULT_DASHBOARD;
|
||||
import static org.thingsboard.server.controller.ControllerConstants.HOME_DASHBOARD;
|
||||
import static org.thingsboard.server.controller.ControllerConstants.PAGE_DATA_PARAMETERS;
|
||||
@ -438,13 +445,14 @@ public class UserController extends BaseController {
|
||||
}
|
||||
|
||||
@ApiOperation(value = "Save user settings (saveUserSettings)",
|
||||
notes = "Save user settings represented in json format for authorized user. " )
|
||||
notes = "Save user settings represented in json format for authorized user. ")
|
||||
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
|
||||
@PostMapping(value = "/user/settings")
|
||||
public JsonNode saveUserSettings(@RequestBody JsonNode settings) throws ThingsboardException {
|
||||
SecurityUser currentUser = getCurrentUser();
|
||||
|
||||
UserSettings userSettings = new UserSettings();
|
||||
userSettings.setType(UserSettingsType.GENERAL);
|
||||
userSettings.setSettings(settings);
|
||||
userSettings.setUserId(currentUser.getId());
|
||||
return userSettingsService.saveUserSettings(currentUser.getTenantId(), userSettings).getSettings();
|
||||
@ -458,31 +466,108 @@ public class UserController extends BaseController {
|
||||
@PutMapping(value = "/user/settings")
|
||||
public void putUserSettings(@RequestBody JsonNode settings) throws ThingsboardException {
|
||||
SecurityUser currentUser = getCurrentUser();
|
||||
userSettingsService.updateUserSettings(currentUser.getTenantId(), currentUser.getId(), settings);
|
||||
userSettingsService.updateUserSettings(currentUser.getTenantId(), currentUser.getId(), UserSettingsType.GENERAL, settings);
|
||||
}
|
||||
|
||||
@ApiOperation(value = "Get user settings (getUserSettings)",
|
||||
notes = "Fetch the User settings based on authorized user. " )
|
||||
notes = "Fetch the User settings based on authorized user. ")
|
||||
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
|
||||
@GetMapping(value = "/user/settings")
|
||||
public JsonNode getUserSettings() throws ThingsboardException {
|
||||
SecurityUser currentUser = getCurrentUser();
|
||||
|
||||
UserSettings userSettings = userSettingsService.findUserSettings(currentUser.getTenantId(), currentUser.getId());
|
||||
return userSettings == null ? JacksonUtil.newObjectNode(): userSettings.getSettings();
|
||||
UserSettings userSettings = userSettingsService.findUserSettings(currentUser.getTenantId(), currentUser.getId(), UserSettingsType.GENERAL);
|
||||
return userSettings == null ? JacksonUtil.newObjectNode() : userSettings.getSettings();
|
||||
}
|
||||
|
||||
@ApiOperation(value = "Delete user settings (deleteUserSettings)",
|
||||
notes = "Delete user settings by specifying list of json element xpaths. \n " +
|
||||
"Example: to delete B and C element in { \"A\": {\"B\": 5}, \"C\": 15} send A.B,C in jsonPaths request parameter" )
|
||||
"Example: to delete B and C element in { \"A\": {\"B\": 5}, \"C\": 15} send A.B,C in jsonPaths request parameter")
|
||||
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
|
||||
@RequestMapping(value = "/user/settings/{paths}", method = RequestMethod.DELETE)
|
||||
public void deleteUserSettings(@ApiParam(value = PATHS)
|
||||
@PathVariable(PATHS) String paths) throws ThingsboardException {
|
||||
@PathVariable(PATHS) String paths) throws ThingsboardException {
|
||||
checkParameter(USER_ID, paths);
|
||||
|
||||
SecurityUser currentUser = getCurrentUser();
|
||||
userSettingsService.deleteUserSettings(currentUser.getTenantId(), currentUser.getId(), Arrays.asList(paths.split(",")));
|
||||
userSettingsService.deleteUserSettings(currentUser.getTenantId(), currentUser.getId(), UserSettingsType.GENERAL, Arrays.asList(paths.split(",")));
|
||||
}
|
||||
|
||||
@ApiOperation(value = "Update user settings (saveUserSettings)",
|
||||
notes = "Update user settings for authorized user. Only specified json elements will be updated." +
|
||||
"Example: you have such settings: {A:5, B:{C:10, D:20}}. Updating it with {B:{C:10, D:30}} will result in" +
|
||||
"{A:5, B:{C:10, D:30}}. The same could be achieved by putting {B.D:30}")
|
||||
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
|
||||
@PutMapping(value = "/user/settings/{type}")
|
||||
public void putUserSettings(@ApiParam(value = "Settings type, case insensitive, one of: \"general\", \"quick_links\", \"doc_links\" or \"dashboards\".")
|
||||
@PathVariable("type") String strType, @RequestBody JsonNode settings) throws ThingsboardException {
|
||||
SecurityUser currentUser = getCurrentUser();
|
||||
UserSettingsType type = checkEnumParameter("Settings type", strType, UserSettingsType::valueOf);
|
||||
checkNotReserved(strType, type);
|
||||
userSettingsService.updateUserSettings(currentUser.getTenantId(), currentUser.getId(), type, settings);
|
||||
}
|
||||
|
||||
@ApiOperation(value = "Get user settings (getUserSettings)",
|
||||
notes = "Fetch the User settings based on authorized user. ")
|
||||
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
|
||||
@GetMapping(value = "/user/settings/{type}")
|
||||
public JsonNode getUserSettings(@ApiParam(value = "Settings type, case insensitive, one of: \"general\", \"quick_links\", \"doc_links\" or \"dashboards\".")
|
||||
@PathVariable("type") String strType) throws ThingsboardException {
|
||||
SecurityUser currentUser = getCurrentUser();
|
||||
UserSettingsType type = checkEnumParameter("Settings type", strType, UserSettingsType::valueOf);
|
||||
checkNotReserved(strType, type);
|
||||
UserSettings userSettings = userSettingsService.findUserSettings(currentUser.getTenantId(), currentUser.getId(), type);
|
||||
return userSettings == null ? JacksonUtil.newObjectNode() : userSettings.getSettings();
|
||||
}
|
||||
|
||||
@ApiOperation(value = "Delete user settings (deleteUserSettings)",
|
||||
notes = "Delete user settings by specifying list of json element xpaths. \n " +
|
||||
"Example: to delete B and C element in { \"A\": {\"B\": 5}, \"C\": 15} send A.B,C in jsonPaths request parameter")
|
||||
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
|
||||
@RequestMapping(value = "/user/settings/{type}/{paths}", method = RequestMethod.DELETE)
|
||||
public void deleteUserSettings(@ApiParam(value = PATHS)
|
||||
@PathVariable(PATHS) String paths,
|
||||
@ApiParam(value = "Settings type, case insensitive, one of: \"general\", \"quick_links\", \"doc_links\" or \"dashboards\".")
|
||||
@PathVariable("type") String strType) throws ThingsboardException {
|
||||
checkParameter(USER_ID, paths);
|
||||
UserSettingsType type = checkEnumParameter("Settings type", strType, UserSettingsType::valueOf);
|
||||
checkNotReserved(strType, type);
|
||||
SecurityUser currentUser = getCurrentUser();
|
||||
userSettingsService.deleteUserSettings(currentUser.getTenantId(), currentUser.getId(), type, Arrays.asList(paths.split(",")));
|
||||
}
|
||||
|
||||
@ApiOperation(value = "Get information about last visited and starred dashboards (getLastVisitedDashboards)",
|
||||
notes = "Fetch the list of last visited and starred dashboards. Both lists are limited to 10 items." + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH)
|
||||
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
|
||||
@GetMapping(value = "/user/dashboards")
|
||||
public UserDashboardsInfo getUserDashboardsInfo() throws ThingsboardException {
|
||||
SecurityUser currentUser = getCurrentUser();
|
||||
return userSettingsService.findUserDashboardsInfo(currentUser.getTenantId(), currentUser.getId());
|
||||
}
|
||||
|
||||
@ApiOperation(value = "Report action of User over the dashboard (reportUserDashboardAction)",
|
||||
notes = "Report action of User over the dashboard. " + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH)
|
||||
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
|
||||
@RequestMapping(value = "/user/dashboards/{dashboardId}/{action}", method = RequestMethod.GET)
|
||||
@ResponseBody
|
||||
public UserDashboardsInfo reportUserDashboardAction(
|
||||
@ApiParam(value = DASHBOARD_ID_PARAM_DESCRIPTION)
|
||||
@PathVariable(DashboardController.DASHBOARD_ID) String strDashboardId,
|
||||
@ApiParam(value = "Dashboard action, one of: \"visit\", \"star\" or \"unstar\".")
|
||||
@PathVariable("action") String strAction) throws ThingsboardException {
|
||||
checkParameter(DashboardController.DASHBOARD_ID, strDashboardId);
|
||||
checkParameter("action", strAction);
|
||||
UserDashboardAction action = checkEnumParameter("Action", strAction, UserDashboardAction::valueOf);
|
||||
DashboardId dashboardId = new DashboardId(toUUID(strDashboardId));
|
||||
checkDashboardInfoId(dashboardId, Operation.READ);
|
||||
SecurityUser currentUser = getCurrentUser();
|
||||
return userSettingsService.reportUserDashboardAction(currentUser.getTenantId(), currentUser.getId(), dashboardId, action);
|
||||
}
|
||||
|
||||
private void checkNotReserved(String strType, UserSettingsType type) throws ThingsboardException {
|
||||
if (type.isReserved()) {
|
||||
throw new ThingsboardException("Settings with type: " + strType + " are reserved for internal use!", ThingsboardErrorCode.BAD_REQUEST_PARAMS);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -295,6 +295,8 @@ public class ThingsboardInstallService {
|
||||
systemDataLoaderService.loadSystemWidgets();
|
||||
systemDataLoaderService.createOAuth2Templates();
|
||||
systemDataLoaderService.createQueues();
|
||||
systemDataLoaderService.createDefaultNotificationConfigs();
|
||||
|
||||
// systemDataLoaderService.loadSystemPlugins();
|
||||
// systemDataLoaderService.loadSystemRules();
|
||||
installScripts.loadSystemLwm2mResources();
|
||||
|
||||
@ -142,6 +142,13 @@ public class EntityActionService {
|
||||
if (user != null) {
|
||||
metaData.putValue("userId", user.getId().toString());
|
||||
metaData.putValue("userName", user.getName());
|
||||
metaData.putValue("userEmail", user.getEmail());
|
||||
if (user.getFirstName() != null) {
|
||||
metaData.putValue("userFirstName", user.getFirstName());
|
||||
}
|
||||
if (user.getLastName() != null) {
|
||||
metaData.putValue("userLastName", user.getLastName());
|
||||
}
|
||||
}
|
||||
if (customerId != null && !customerId.isNullUid()) {
|
||||
metaData.putValue("customerId", customerId.toString());
|
||||
|
||||
@ -24,13 +24,12 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.thingsboard.common.util.ThingsBoardThreadFactory;
|
||||
import org.thingsboard.rule.engine.api.MailService;
|
||||
import org.thingsboard.server.cluster.TbClusterService;
|
||||
import org.thingsboard.server.common.data.ApiFeature;
|
||||
import org.thingsboard.server.common.data.ApiUsageRecordKey;
|
||||
import org.thingsboard.server.common.data.ApiUsageRecordState;
|
||||
import org.thingsboard.server.common.data.ApiUsageState;
|
||||
import org.thingsboard.server.common.data.ApiUsageStateMailMessage;
|
||||
import org.thingsboard.server.common.data.ApiUsageStateValue;
|
||||
import org.thingsboard.server.common.data.EntityType;
|
||||
import org.thingsboard.server.common.data.StringUtils;
|
||||
@ -53,7 +52,8 @@ import org.thingsboard.server.common.msg.queue.ServiceType;
|
||||
import org.thingsboard.server.common.msg.queue.TbCallback;
|
||||
import org.thingsboard.server.common.msg.queue.TopicPartitionInfo;
|
||||
import org.thingsboard.server.common.msg.tools.SchedulerUtils;
|
||||
import org.thingsboard.server.dao.notification.NotificationRuleProcessingService;
|
||||
import org.thingsboard.server.common.msg.notification.NotificationRuleProcessor;
|
||||
import org.thingsboard.server.common.msg.notification.trigger.ApiUsageLimitTrigger;
|
||||
import org.thingsboard.server.dao.tenant.TbTenantProfileCache;
|
||||
import org.thingsboard.server.dao.tenant.TenantService;
|
||||
import org.thingsboard.server.dao.timeseries.TimeseriesService;
|
||||
@ -63,6 +63,7 @@ import org.thingsboard.server.gen.transport.TransportProtos.UsageStatsKVProto;
|
||||
import org.thingsboard.server.queue.common.TbProtoQueueMsg;
|
||||
import org.thingsboard.server.queue.discovery.PartitionService;
|
||||
import org.thingsboard.server.service.executors.DbCallbackExecutorService;
|
||||
import org.thingsboard.server.service.mail.MailExecutorService;
|
||||
import org.thingsboard.server.service.partition.AbstractPartitionBasedService;
|
||||
import org.thingsboard.server.service.telemetry.InternalTelemetryService;
|
||||
|
||||
@ -79,8 +80,6 @@ import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
@ -108,8 +107,9 @@ public class DefaultTbApiUsageStateService extends AbstractPartitionBasedService
|
||||
private final ApiUsageStateService apiUsageStateService;
|
||||
private final TbTenantProfileCache tenantProfileCache;
|
||||
private final MailService mailService;
|
||||
private final NotificationRuleProcessingService notificationRuleProcessingService;
|
||||
private final NotificationRuleProcessor notificationRuleProcessor;
|
||||
private final DbCallbackExecutorService dbExecutor;
|
||||
private final MailExecutorService mailExecutor;
|
||||
|
||||
@Lazy
|
||||
@Autowired
|
||||
@ -130,8 +130,6 @@ public class DefaultTbApiUsageStateService extends AbstractPartitionBasedService
|
||||
|
||||
private final Lock updateLock = new ReentrantLock();
|
||||
|
||||
private final ExecutorService mailExecutor = Executors.newSingleThreadExecutor(ThingsBoardThreadFactory.forName("api-usage-svc-mail"));
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
super.init();
|
||||
@ -340,32 +338,35 @@ public class DefaultTbApiUsageStateService extends AbstractPartitionBasedService
|
||||
tsWsService.saveAndNotifyInternal(state.getTenantId(), state.getApiUsageState().getId(), stateTelemetry, VOID_CALLBACK);
|
||||
|
||||
if (state.getEntityType() == EntityType.TENANT && !state.getEntityId().equals(TenantId.SYS_TENANT_ID)) {
|
||||
|
||||
String email = tenantService.findTenantById(state.getTenantId()).getEmail();
|
||||
if (StringUtils.isNotEmpty(email)) {
|
||||
result.forEach((apiFeature, stateValue) -> {
|
||||
result.forEach((apiFeature, stateValue) -> {
|
||||
ApiUsageRecordState recordState = createApiUsageRecordState((TenantApiUsageState) state, apiFeature, stateValue);
|
||||
notificationRuleProcessor.process(ApiUsageLimitTrigger.builder()
|
||||
.tenantId(state.getTenantId())
|
||||
.state(recordState)
|
||||
.status(stateValue)
|
||||
.build());
|
||||
if (StringUtils.isNotEmpty(email)) {
|
||||
mailExecutor.submit(() -> {
|
||||
try {
|
||||
mailService.sendApiFeatureStateEmail(apiFeature, stateValue, email, createStateMailMessage((TenantApiUsageState) state, apiFeature, stateValue));
|
||||
mailService.sendApiFeatureStateEmail(apiFeature, stateValue, email, recordState);
|
||||
} catch (ThingsboardException e) {
|
||||
log.warn("[{}] Can't send update of the API state to tenant with provided email [{}]", state.getTenantId(), email, e);
|
||||
}
|
||||
});
|
||||
});
|
||||
} else {
|
||||
log.warn("[{}] Can't send update of the API state to tenant with empty email!", state.getTenantId());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private ApiUsageStateMailMessage createStateMailMessage(TenantApiUsageState state, ApiFeature apiFeature, ApiUsageStateValue stateValue) {
|
||||
private ApiUsageRecordState createApiUsageRecordState(TenantApiUsageState state, ApiFeature apiFeature, ApiUsageStateValue stateValue) {
|
||||
StateChecker checker = getStateChecker(stateValue);
|
||||
for (ApiUsageRecordKey apiUsageRecordKey : ApiUsageRecordKey.getKeys(apiFeature)) {
|
||||
long threshold = state.getProfileThreshold(apiUsageRecordKey);
|
||||
long warnThreshold = state.getProfileWarnThreshold(apiUsageRecordKey);
|
||||
long value = state.get(apiUsageRecordKey);
|
||||
if (checker.check(threshold, warnThreshold, value)) {
|
||||
return new ApiUsageStateMailMessage(apiUsageRecordKey, threshold, value);
|
||||
return new ApiUsageRecordState(apiFeature, apiUsageRecordKey, threshold, value);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
@ -377,7 +378,7 @@ public class DefaultTbApiUsageStateService extends AbstractPartitionBasedService
|
||||
} else if (ApiUsageStateValue.WARNING.equals(stateValue)) {
|
||||
return (t, wt, v) -> v < t && v >= wt;
|
||||
} else {
|
||||
return (t, wt, v) -> v >= t;
|
||||
return (t, wt, v) -> t > 0 && v >= t;
|
||||
}
|
||||
}
|
||||
|
||||
@ -529,8 +530,5 @@ public class DefaultTbApiUsageStateService extends AbstractPartitionBasedService
|
||||
@PreDestroy
|
||||
private void destroy() {
|
||||
super.stop();
|
||||
if (mailExecutor != null) {
|
||||
mailExecutor.shutdownNow();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -423,6 +423,7 @@ public final class EdgeGrpcSession implements Closeable {
|
||||
stopCurrentSendDownlinkMsgsTask(null);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("[{}] Failed to send downlink msgs. Error msg {}", this.sessionId, e.getMessage(), e);
|
||||
stopCurrentSendDownlinkMsgsTask(e);
|
||||
}
|
||||
};
|
||||
@ -681,7 +682,7 @@ public final class EdgeGrpcSession implements Closeable {
|
||||
public void stopCurrentSendDownlinkMsgsTask(Exception e) {
|
||||
if (sessionState.getSendDownlinkMsgsFuture() != null && !sessionState.getSendDownlinkMsgsFuture().isDone()) {
|
||||
if (e != null) {
|
||||
log.warn(e.getMessage(), e);
|
||||
log.debug(e.getMessage());
|
||||
sessionState.getSendDownlinkMsgsFuture().setException(e);
|
||||
} else {
|
||||
sessionState.getSendDownlinkMsgsFuture().set(null);
|
||||
|
||||
@ -485,4 +485,27 @@ public abstract class BaseEdgeProcessor {
|
||||
}
|
||||
return customerId;
|
||||
}
|
||||
|
||||
protected boolean isEntityExists(TenantId tenantId, EntityId entityId) {
|
||||
switch (entityId.getEntityType()) {
|
||||
case TENANT:
|
||||
return tenantService.findTenantById(tenantId) != null;
|
||||
case DEVICE:
|
||||
return deviceService.findDeviceById(tenantId, new DeviceId(entityId.getId())) != null;
|
||||
case ASSET:
|
||||
return assetService.findAssetById(tenantId, new AssetId(entityId.getId())) != null;
|
||||
case ENTITY_VIEW:
|
||||
return entityViewService.findEntityViewById(tenantId, new EntityViewId(entityId.getId())) != null;
|
||||
case CUSTOMER:
|
||||
return customerService.findCustomerById(tenantId, new CustomerId(entityId.getId())) != null;
|
||||
case USER:
|
||||
return userService.findUserById(tenantId, new UserId(entityId.getId())) != null;
|
||||
case DASHBOARD:
|
||||
return dashboardService.findDashboardById(tenantId, new DashboardId(entityId.getId())) != null;
|
||||
case EDGE:
|
||||
return edgeService.findEdgeById(tenantId, new EdgeId(entityId.getId())) != null;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -19,19 +19,19 @@ import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.google.common.util.concurrent.Futures;
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.thingsboard.common.util.JacksonUtil;
|
||||
import org.thingsboard.server.common.data.EntityType;
|
||||
import org.thingsboard.server.common.data.alarm.Alarm;
|
||||
import org.thingsboard.server.common.data.alarm.AlarmCreateOrUpdateActiveRequest;
|
||||
import org.thingsboard.server.common.data.alarm.AlarmSeverity;
|
||||
import org.thingsboard.server.common.data.alarm.AlarmStatus;
|
||||
import org.thingsboard.server.common.data.alarm.AlarmUpdateRequest;
|
||||
import org.thingsboard.server.common.data.edge.EdgeEventActionType;
|
||||
import org.thingsboard.server.common.data.id.AlarmId;
|
||||
import org.thingsboard.server.common.data.id.EntityId;
|
||||
import org.thingsboard.server.common.data.id.TenantId;
|
||||
import org.thingsboard.server.gen.edge.v1.AlarmUpdateMsg;
|
||||
import org.thingsboard.server.gen.edge.v1.UpdateMsgType;
|
||||
import org.thingsboard.server.queue.util.TbCoreComponent;
|
||||
import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessor;
|
||||
|
||||
import java.util.UUID;
|
||||
@ -43,51 +43,59 @@ public abstract class BaseAlarmProcessor extends BaseEdgeProcessor {
|
||||
log.trace("[{}] processAlarmMsg [{}]", tenantId, alarmUpdateMsg);
|
||||
EntityId originatorId = getAlarmOriginator(tenantId, alarmUpdateMsg.getOriginatorName(),
|
||||
EntityType.valueOf(alarmUpdateMsg.getOriginatorType()));
|
||||
AlarmId alarmId = new AlarmId(new UUID(alarmUpdateMsg.getIdMSB(), alarmUpdateMsg.getIdLSB()));
|
||||
if (originatorId == null) {
|
||||
log.warn("Originator not found for the alarm msg {}", alarmUpdateMsg);
|
||||
return Futures.immediateFuture(null);
|
||||
}
|
||||
try {
|
||||
Alarm existentAlarm = alarmService.findLatestActiveByOriginatorAndType(tenantId, originatorId, alarmUpdateMsg.getType());
|
||||
switch (alarmUpdateMsg.getMsgType()) {
|
||||
case ENTITY_CREATED_RPC_MESSAGE:
|
||||
case ENTITY_UPDATED_RPC_MESSAGE:
|
||||
if (existentAlarm == null || existentAlarm.getStatus().isCleared()) {
|
||||
existentAlarm = new Alarm();
|
||||
existentAlarm.setTenantId(tenantId);
|
||||
existentAlarm.setType(alarmUpdateMsg.getName());
|
||||
existentAlarm.setOriginator(originatorId);
|
||||
existentAlarm.setSeverity(AlarmSeverity.valueOf(alarmUpdateMsg.getSeverity()));
|
||||
existentAlarm.setStartTs(alarmUpdateMsg.getStartTs());
|
||||
existentAlarm.setClearTs(alarmUpdateMsg.getClearTs());
|
||||
existentAlarm.setPropagate(alarmUpdateMsg.getPropagate());
|
||||
}
|
||||
Alarm alarm = new Alarm();
|
||||
alarm.setId(alarmId);
|
||||
alarm.setTenantId(tenantId);
|
||||
alarm.setType(alarmUpdateMsg.getName());
|
||||
alarm.setOriginator(originatorId);
|
||||
alarm.setSeverity(AlarmSeverity.valueOf(alarmUpdateMsg.getSeverity()));
|
||||
alarm.setStartTs(alarmUpdateMsg.getStartTs());
|
||||
var alarmStatus = AlarmStatus.valueOf(alarmUpdateMsg.getStatus());
|
||||
existentAlarm.setCleared(alarmStatus.isCleared());
|
||||
existentAlarm.setAcknowledged(alarmStatus.isAck());
|
||||
existentAlarm.setAckTs(alarmUpdateMsg.getAckTs());
|
||||
existentAlarm.setEndTs(alarmUpdateMsg.getEndTs());
|
||||
existentAlarm.setDetails(JacksonUtil.OBJECT_MAPPER.readTree(alarmUpdateMsg.getDetails()));
|
||||
alarmService.createOrUpdateAlarm(existentAlarm);
|
||||
break;
|
||||
alarm.setClearTs(alarmUpdateMsg.getClearTs());
|
||||
alarm.setPropagate(alarmUpdateMsg.getPropagate());
|
||||
alarm.setCleared(alarmStatus.isCleared());
|
||||
alarm.setAcknowledged(alarmStatus.isAck());
|
||||
alarm.setAckTs(alarmUpdateMsg.getAckTs());
|
||||
alarm.setEndTs(alarmUpdateMsg.getEndTs());
|
||||
alarm.setDetails(JacksonUtil.OBJECT_MAPPER.readTree(alarmUpdateMsg.getDetails()));
|
||||
if (UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE.equals(alarmUpdateMsg.getMsgType())) {
|
||||
alarmService.createAlarm(AlarmCreateOrUpdateActiveRequest.fromAlarm(alarm, null, alarmId));
|
||||
} else {
|
||||
alarmService.updateAlarm(AlarmUpdateRequest.fromAlarm(alarm));
|
||||
}
|
||||
return Futures.immediateFuture(null);
|
||||
case ALARM_ACK_RPC_MESSAGE:
|
||||
if (existentAlarm != null) {
|
||||
alarmService.acknowledgeAlarm(tenantId, existentAlarm.getId(), alarmUpdateMsg.getAckTs());
|
||||
Alarm alarmToAck = alarmService.findAlarmById(tenantId, alarmId);
|
||||
if (alarmToAck != null) {
|
||||
alarmService.acknowledgeAlarm(tenantId, alarmId, alarmUpdateMsg.getAckTs());
|
||||
}
|
||||
break;
|
||||
return Futures.immediateFuture(null);
|
||||
case ALARM_CLEAR_RPC_MESSAGE:
|
||||
if (existentAlarm != null) {
|
||||
alarmService.clearAlarm(tenantId, existentAlarm.getId(),
|
||||
alarmUpdateMsg.getAckTs(), JacksonUtil.OBJECT_MAPPER.readTree(alarmUpdateMsg.getDetails()));
|
||||
Alarm alarmToClear = alarmService.findAlarmById(tenantId, alarmId);
|
||||
if (alarmToClear != null) {
|
||||
alarmService.clearAlarm(tenantId, alarmId, alarmUpdateMsg.getClearTs(),
|
||||
JacksonUtil.OBJECT_MAPPER.readTree(alarmUpdateMsg.getDetails()));
|
||||
}
|
||||
break;
|
||||
return Futures.immediateFuture(null);
|
||||
case ENTITY_DELETED_RPC_MESSAGE:
|
||||
if (existentAlarm != null) {
|
||||
alarmService.delAlarm(tenantId, existentAlarm.getId());
|
||||
Alarm alarmToDelete = alarmService.findAlarmById(tenantId, alarmId);
|
||||
if (alarmToDelete != null) {
|
||||
alarmService.delAlarm(tenantId, alarmId);
|
||||
}
|
||||
break;
|
||||
return Futures.immediateFuture(null);
|
||||
case UNRECOGNIZED:
|
||||
default:
|
||||
return handleUnsupportedMsgType(alarmUpdateMsg.getMsgType());
|
||||
}
|
||||
return Futures.immediateFuture(null);
|
||||
} catch (Exception e) {
|
||||
log.error("[{}] Failed to process alarm update msg [{}]", tenantId, alarmUpdateMsg, e);
|
||||
return Futures.immediateFailedFuture(e);
|
||||
|
||||
@ -20,16 +20,9 @@ import com.google.common.util.concurrent.ListenableFuture;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.thingsboard.common.util.JacksonUtil;
|
||||
import org.thingsboard.server.common.data.EntityType;
|
||||
import org.thingsboard.server.common.data.id.AssetId;
|
||||
import org.thingsboard.server.common.data.id.CustomerId;
|
||||
import org.thingsboard.server.common.data.id.DashboardId;
|
||||
import org.thingsboard.server.common.data.id.DeviceId;
|
||||
import org.thingsboard.server.common.data.id.EdgeId;
|
||||
import org.thingsboard.server.common.data.id.EntityId;
|
||||
import org.thingsboard.server.common.data.id.EntityIdFactory;
|
||||
import org.thingsboard.server.common.data.id.EntityViewId;
|
||||
import org.thingsboard.server.common.data.id.TenantId;
|
||||
import org.thingsboard.server.common.data.id.UserId;
|
||||
import org.thingsboard.server.common.data.relation.EntityRelation;
|
||||
import org.thingsboard.server.common.data.relation.RelationTypeGroup;
|
||||
import org.thingsboard.server.gen.edge.v1.RelationUpdateMsg;
|
||||
@ -80,25 +73,4 @@ public abstract class BaseRelationProcessor extends BaseEdgeProcessor {
|
||||
return Futures.immediateFailedFuture(e);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isEntityExists(TenantId tenantId, EntityId entityId) {
|
||||
switch (entityId.getEntityType()) {
|
||||
case DEVICE:
|
||||
return deviceService.findDeviceById(tenantId, new DeviceId(entityId.getId())) != null;
|
||||
case ASSET:
|
||||
return assetService.findAssetById(tenantId, new AssetId(entityId.getId())) != null;
|
||||
case ENTITY_VIEW:
|
||||
return entityViewService.findEntityViewById(tenantId, new EntityViewId(entityId.getId())) != null;
|
||||
case CUSTOMER:
|
||||
return customerService.findCustomerById(tenantId, new CustomerId(entityId.getId())) != null;
|
||||
case USER:
|
||||
return userService.findUserById(tenantId, new UserId(entityId.getId())) != null;
|
||||
case DASHBOARD:
|
||||
return dashboardService.findDashboardById(tenantId, new DashboardId(entityId.getId())) != null;
|
||||
case EDGE:
|
||||
return edgeService.findEdgeById(tenantId, new EdgeId(entityId.getId())) != null;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -18,6 +18,7 @@ package org.thingsboard.server.service.edge.rpc.processor.telemetry;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.google.common.util.concurrent.FutureCallback;
|
||||
import com.google.common.util.concurrent.Futures;
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
import com.google.common.util.concurrent.SettableFuture;
|
||||
import com.google.gson.Gson;
|
||||
@ -90,42 +91,46 @@ public abstract class BaseTelemetryProcessor extends BaseEdgeProcessor {
|
||||
log.trace("[{}] processTelemetryMsg [{}]", tenantId, entityData);
|
||||
List<ListenableFuture<Void>> result = new ArrayList<>();
|
||||
EntityId entityId = constructEntityId(entityData.getEntityType(), entityData.getEntityIdMSB(), entityData.getEntityIdLSB());
|
||||
if ((entityData.hasPostAttributesMsg() || entityData.hasPostTelemetryMsg() || entityData.hasAttributesUpdatedMsg()) && entityId != null) {
|
||||
Pair<TbMsgMetaData, CustomerId> pair = getBaseMsgMetadataAndCustomerId(tenantId, entityId);
|
||||
TbMsgMetaData metaData = pair.getKey();
|
||||
CustomerId customerId = pair.getValue();
|
||||
metaData.putValue(DataConstants.MSG_SOURCE_KEY, getMsgSourceKey());
|
||||
if (entityData.hasPostAttributesMsg()) {
|
||||
result.add(processPostAttributes(tenantId, customerId, entityId, entityData.getPostAttributesMsg(), metaData));
|
||||
}
|
||||
if (entityData.hasAttributesUpdatedMsg()) {
|
||||
metaData.putValue("scope", entityData.getPostAttributeScope());
|
||||
result.add(processAttributesUpdate(tenantId, customerId, entityId, entityData.getAttributesUpdatedMsg(), metaData));
|
||||
}
|
||||
if (entityData.hasPostTelemetryMsg()) {
|
||||
result.add(processPostTelemetry(tenantId, customerId, entityId, entityData.getPostTelemetryMsg(), metaData));
|
||||
}
|
||||
if (EntityType.DEVICE.equals(entityId.getEntityType())) {
|
||||
DeviceId deviceId = new DeviceId(entityId.getId());
|
||||
if (entityId != null && isEntityExists(tenantId, entityId)) {
|
||||
if ((entityData.hasPostAttributesMsg() || entityData.hasPostTelemetryMsg() || entityData.hasAttributesUpdatedMsg())) {
|
||||
Pair<TbMsgMetaData, CustomerId> pair = getBaseMsgMetadataAndCustomerId(tenantId, entityId);
|
||||
TbMsgMetaData metaData = pair.getKey();
|
||||
CustomerId customerId = pair.getValue();
|
||||
metaData.putValue(DataConstants.MSG_SOURCE_KEY, getMsgSourceKey());
|
||||
if (entityData.hasPostAttributesMsg()) {
|
||||
result.add(processPostAttributes(tenantId, customerId, entityId, entityData.getPostAttributesMsg(), metaData));
|
||||
}
|
||||
if (entityData.hasAttributesUpdatedMsg()) {
|
||||
metaData.putValue("scope", entityData.getPostAttributeScope());
|
||||
result.add(processAttributesUpdate(tenantId, customerId, entityId, entityData.getAttributesUpdatedMsg(), metaData));
|
||||
}
|
||||
if (entityData.hasPostTelemetryMsg()) {
|
||||
result.add(processPostTelemetry(tenantId, customerId, entityId, entityData.getPostTelemetryMsg(), metaData));
|
||||
}
|
||||
if (EntityType.DEVICE.equals(entityId.getEntityType())) {
|
||||
DeviceId deviceId = new DeviceId(entityId.getId());
|
||||
|
||||
long currentTs = System.currentTimeMillis();
|
||||
long currentTs = System.currentTimeMillis();
|
||||
|
||||
TransportProtos.DeviceActivityProto deviceActivityMsg = TransportProtos.DeviceActivityProto.newBuilder()
|
||||
.setTenantIdMSB(tenantId.getId().getMostSignificantBits())
|
||||
.setTenantIdLSB(tenantId.getId().getLeastSignificantBits())
|
||||
.setDeviceIdMSB(deviceId.getId().getMostSignificantBits())
|
||||
.setDeviceIdLSB(deviceId.getId().getLeastSignificantBits())
|
||||
.setLastActivityTime(currentTs).build();
|
||||
TransportProtos.DeviceActivityProto deviceActivityMsg = TransportProtos.DeviceActivityProto.newBuilder()
|
||||
.setTenantIdMSB(tenantId.getId().getMostSignificantBits())
|
||||
.setTenantIdLSB(tenantId.getId().getLeastSignificantBits())
|
||||
.setDeviceIdMSB(deviceId.getId().getMostSignificantBits())
|
||||
.setDeviceIdLSB(deviceId.getId().getLeastSignificantBits())
|
||||
.setLastActivityTime(currentTs).build();
|
||||
|
||||
log.trace("[{}][{}] device activity time is going to be updated, ts {}", tenantId, deviceId, currentTs);
|
||||
log.trace("[{}][{}] device activity time is going to be updated, ts {}", tenantId, deviceId, currentTs);
|
||||
|
||||
TopicPartitionInfo tpi = partitionService.resolve(ServiceType.TB_CORE, tenantId, deviceId);
|
||||
tbCoreMsgProducer.send(tpi, new TbProtoQueueMsg<>(deviceId.getId(),
|
||||
TransportProtos.ToCoreMsg.newBuilder().setDeviceActivityMsg(deviceActivityMsg).build()), null);
|
||||
TopicPartitionInfo tpi = partitionService.resolve(ServiceType.TB_CORE, tenantId, deviceId);
|
||||
tbCoreMsgProducer.send(tpi, new TbProtoQueueMsg<>(deviceId.getId(),
|
||||
TransportProtos.ToCoreMsg.newBuilder().setDeviceActivityMsg(deviceActivityMsg).build()), null);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (entityData.hasAttributeDeleteMsg()) {
|
||||
result.add(processAttributeDeleteMsg(tenantId, entityId, entityData.getAttributeDeleteMsg(), entityData.getEntityType()));
|
||||
if (entityData.hasAttributeDeleteMsg()) {
|
||||
result.add(processAttributeDeleteMsg(tenantId, entityId, entityData.getAttributeDeleteMsg(), entityData.getEntityType()));
|
||||
}
|
||||
} else {
|
||||
log.warn("Skipping telemetry update msg because entity doesn't exists on edge, {}", entityData);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@ -279,26 +284,31 @@ public abstract class BaseTelemetryProcessor extends BaseEdgeProcessor {
|
||||
|
||||
private ListenableFuture<Void> processAttributeDeleteMsg(TenantId tenantId, EntityId entityId, AttributeDeleteMsg attributeDeleteMsg,
|
||||
String entityType) {
|
||||
SettableFuture<Void> futureToSet = SettableFuture.create();
|
||||
|
||||
String scope = attributeDeleteMsg.getScope();
|
||||
List<String> attributeKeys = attributeDeleteMsg.getAttributeNamesList();
|
||||
attributesService.removeAll(tenantId, entityId, scope, attributeKeys);
|
||||
if (EntityType.DEVICE.name().equals(entityType)) {
|
||||
tbClusterService.pushMsgToCore(DeviceAttributesEventNotificationMsg.onDelete(
|
||||
tenantId, (DeviceId) entityId, scope, attributeKeys), new TbQueueCallback() {
|
||||
@Override
|
||||
public void onSuccess(TbQueueMsgMetadata metadata) {
|
||||
futureToSet.set(null);
|
||||
}
|
||||
ListenableFuture<List<String>> removeAllFuture = attributesService.removeAll(tenantId, entityId, scope, attributeKeys);
|
||||
return Futures.transformAsync(removeAllFuture, removeAttributes -> {
|
||||
if (EntityType.DEVICE.name().equals(entityType)) {
|
||||
SettableFuture<Void> futureToSet = SettableFuture.create();
|
||||
tbClusterService.pushMsgToCore(DeviceAttributesEventNotificationMsg.onDelete(
|
||||
tenantId, (DeviceId) entityId, scope, attributeKeys), new TbQueueCallback() {
|
||||
@Override
|
||||
public void onSuccess(TbQueueMsgMetadata metadata) {
|
||||
futureToSet.set(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable t) {
|
||||
log.error("Can't process attribute delete msg [{}]", attributeDeleteMsg, t);
|
||||
futureToSet.setException(t);
|
||||
}
|
||||
});
|
||||
}
|
||||
return futureToSet;
|
||||
@Override
|
||||
public void onFailure(Throwable t) {
|
||||
log.error("Can't process attribute delete msg [{}]", attributeDeleteMsg, t);
|
||||
futureToSet.setException(t);
|
||||
}
|
||||
});
|
||||
return futureToSet;
|
||||
} else {
|
||||
return Futures.immediateFuture(null);
|
||||
}
|
||||
}, dbCallbackExecutorService);
|
||||
}
|
||||
|
||||
public EntityDataProto convertTelemetryEventToEntityDataProto(EntityType entityType,
|
||||
|
||||
@ -35,7 +35,7 @@ import org.thingsboard.server.common.data.exception.ThingsboardException;
|
||||
import org.thingsboard.server.common.data.id.EdgeId;
|
||||
import org.thingsboard.server.common.data.id.TenantId;
|
||||
import org.thingsboard.server.common.data.id.UserId;
|
||||
import org.thingsboard.server.dao.alarm.AlarmApiCallResult;
|
||||
import org.thingsboard.server.common.data.alarm.AlarmApiCallResult;
|
||||
import org.thingsboard.server.service.entitiy.AbstractTbEntityService;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@ -0,0 +1,202 @@
|
||||
/**
|
||||
* Copyright © 2016-2023 The Thingsboard Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.thingsboard.server.service.entitiy.user;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.thingsboard.common.util.JacksonUtil;
|
||||
import org.thingsboard.server.common.data.HasTitle;
|
||||
import org.thingsboard.server.common.data.StringUtils;
|
||||
import org.thingsboard.server.common.data.id.DashboardId;
|
||||
import org.thingsboard.server.common.data.id.TenantId;
|
||||
import org.thingsboard.server.common.data.id.UserId;
|
||||
import org.thingsboard.server.common.data.settings.AbstractUserDashboardInfo;
|
||||
import org.thingsboard.server.common.data.settings.LastVisitedDashboardInfo;
|
||||
import org.thingsboard.server.common.data.settings.StarredDashboardInfo;
|
||||
import org.thingsboard.server.common.data.settings.UserDashboardAction;
|
||||
import org.thingsboard.server.common.data.settings.UserDashboardsInfo;
|
||||
import org.thingsboard.server.common.data.settings.UserSettings;
|
||||
import org.thingsboard.server.common.data.settings.UserSettingsType;
|
||||
import org.thingsboard.server.dao.dashboard.DashboardService;
|
||||
import org.thingsboard.server.dao.user.UserSettingsService;
|
||||
import org.thingsboard.server.queue.util.TbCoreComponent;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Service
|
||||
@TbCoreComponent
|
||||
@AllArgsConstructor
|
||||
@Slf4j
|
||||
public class DefaultTbUserSettingsService implements TbUserSettingsService {
|
||||
|
||||
private static final int MAX_DASHBOARD_INFO_LIST_SIZE = 10;
|
||||
private static final Predicate<HasTitle> EMPTY_TITLE = i -> StringUtils.isEmpty(i.getTitle());
|
||||
|
||||
private final UserSettingsService settingsService;
|
||||
private final DashboardService dashboardService;
|
||||
|
||||
@Override
|
||||
public UserSettings saveUserSettings(TenantId tenantId, UserSettings userSettings) {
|
||||
return settingsService.saveUserSettings(tenantId, userSettings);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateUserSettings(TenantId tenantId, UserId userId, UserSettingsType type, JsonNode settings) {
|
||||
settingsService.updateUserSettings(tenantId, userId, type, settings);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserSettings findUserSettings(TenantId tenantId, UserId userId, UserSettingsType type) {
|
||||
return settingsService.findUserSettings(tenantId, userId, type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteUserSettings(TenantId tenantId, UserId userId, UserSettingsType type, List<String> jsonPaths) {
|
||||
settingsService.deleteUserSettings(tenantId, userId, type, jsonPaths);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserDashboardsInfo findUserDashboardsInfo(TenantId tenantId, UserId id) {
|
||||
UserSettings us = findUserSettings(tenantId, id, UserSettingsType.VISITED_DASHBOARDS);
|
||||
if (us == null) {
|
||||
return UserDashboardsInfo.EMPTY;
|
||||
}
|
||||
UserDashboardsInfo stored = JacksonUtil.convertValue(us.getSettings(), UserDashboardsInfo.class);
|
||||
return refreshDashboardTitles(tenantId, stored);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserDashboardsInfo reportUserDashboardAction(TenantId tenantId, UserId id, DashboardId dashboardId, UserDashboardAction action) {
|
||||
UserSettings us = findUserSettings(tenantId, id, UserSettingsType.VISITED_DASHBOARDS);
|
||||
UserDashboardsInfo stored = null;
|
||||
if (us != null) {
|
||||
stored = JacksonUtil.convertValue(us.getSettings(), UserDashboardsInfo.class);
|
||||
}
|
||||
if (stored == null) {
|
||||
stored = new UserDashboardsInfo();
|
||||
}
|
||||
|
||||
switch (action) {
|
||||
case STAR:
|
||||
addToStarred(stored, dashboardId);
|
||||
break;
|
||||
case UNSTAR:
|
||||
removeFromStarred(stored, dashboardId);
|
||||
break;
|
||||
case VISIT:
|
||||
addToVisited(stored, dashboardId);
|
||||
break;
|
||||
}
|
||||
|
||||
stored = refreshDashboardTitles(tenantId, stored);
|
||||
|
||||
us = new UserSettings();
|
||||
us.setUserId(id);
|
||||
us.setType(UserSettingsType.VISITED_DASHBOARDS);
|
||||
us.setSettings(JacksonUtil.valueToTree(stored));
|
||||
saveUserSettings(tenantId, us);
|
||||
return stored;
|
||||
}
|
||||
|
||||
private void addToVisited(UserDashboardsInfo stored, DashboardId dashboardId) {
|
||||
UUID id = dashboardId.getId();
|
||||
long ts = System.currentTimeMillis();
|
||||
var opt = stored.getLast().stream().filter(filterById(id)).findFirst();
|
||||
if (opt.isPresent()) {
|
||||
opt.get().setLastVisited(ts);
|
||||
} else {
|
||||
var newInfo = new LastVisitedDashboardInfo();
|
||||
newInfo.setId(id);
|
||||
newInfo.setStarred(stored.getStarred().stream().anyMatch(filterById(id)));
|
||||
newInfo.setLastVisited(System.currentTimeMillis());
|
||||
stored.getLast().add(newInfo);
|
||||
}
|
||||
stored.getLast().sort(Comparator.comparing(LastVisitedDashboardInfo::getLastVisited).reversed());
|
||||
if (stored.getLast().size() > MAX_DASHBOARD_INFO_LIST_SIZE) {
|
||||
stored.setLast(stored.getLast().stream().limit(MAX_DASHBOARD_INFO_LIST_SIZE).collect(Collectors.toList()));
|
||||
}
|
||||
}
|
||||
|
||||
private void removeFromStarred(UserDashboardsInfo stored, DashboardId dashboardId) {
|
||||
UUID id = dashboardId.getId();
|
||||
stored.getStarred().removeIf(filterById(id));
|
||||
stored.getLast().stream().filter(d -> id.equals(d.getId())).findFirst().ifPresent(d -> d.setStarred(false));
|
||||
}
|
||||
|
||||
private void addToStarred(UserDashboardsInfo stored, DashboardId dashboardId) {
|
||||
UUID id = dashboardId.getId();
|
||||
long ts = System.currentTimeMillis();
|
||||
var opt = stored.getStarred().stream().filter(filterById(id)).findFirst();
|
||||
if (opt.isPresent()) {
|
||||
opt.get().setStarredAt(ts);
|
||||
} else {
|
||||
var newInfo = new StarredDashboardInfo();
|
||||
newInfo.setId(id);
|
||||
newInfo.setStarredAt(System.currentTimeMillis());
|
||||
stored.getStarred().add(newInfo);
|
||||
}
|
||||
stored.getStarred().sort(Comparator.comparing(StarredDashboardInfo::getStarredAt).reversed());
|
||||
if (stored.getStarred().size() > MAX_DASHBOARD_INFO_LIST_SIZE) {
|
||||
stored.setStarred(stored.getStarred().stream().limit(MAX_DASHBOARD_INFO_LIST_SIZE).collect(Collectors.toList()));
|
||||
}
|
||||
Set<UUID> starredMap =
|
||||
stored.getStarred().stream().map(AbstractUserDashboardInfo::getId).collect(Collectors.toSet());
|
||||
stored.getLast().forEach(d -> d.setStarred(starredMap.contains(d.getId())));
|
||||
}
|
||||
|
||||
private Predicate<AbstractUserDashboardInfo> filterById(UUID id) {
|
||||
return d -> id.equals(d.getId());
|
||||
}
|
||||
|
||||
private UserDashboardsInfo refreshDashboardTitles(TenantId tenantId, UserDashboardsInfo stored) {
|
||||
if (stored == null) {
|
||||
return UserDashboardsInfo.EMPTY;
|
||||
}
|
||||
stored.getLast().forEach(i -> i.setTitle(null));
|
||||
stored.getStarred().forEach(i -> i.setTitle(null));
|
||||
|
||||
Set<UUID> uniqueIds = new HashSet<>();
|
||||
stored.getLast().stream().map(AbstractUserDashboardInfo::getId).forEach(uniqueIds::add);
|
||||
stored.getStarred().stream().map(AbstractUserDashboardInfo::getId).forEach(uniqueIds::add);
|
||||
|
||||
Map<UUID, String> dashboardTitles = new HashMap<>();
|
||||
uniqueIds.forEach(id -> {
|
||||
var title = dashboardService.findDashboardTitleById(tenantId, new DashboardId(id));
|
||||
if (StringUtils.isNotEmpty(title)) {
|
||||
dashboardTitles.put(id, title);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
stored.getLast().forEach(i -> i.setTitle(dashboardTitles.get(i.getId())));
|
||||
stored.getLast().removeIf(EMPTY_TITLE);
|
||||
stored.getStarred().forEach(i -> i.setTitle(dashboardTitles.get(i.getId())));
|
||||
stored.getStarred().removeIf(EMPTY_TITLE);
|
||||
return stored;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,42 @@
|
||||
/**
|
||||
* Copyright © 2016-2023 The Thingsboard Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.thingsboard.server.service.entitiy.user;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import org.thingsboard.server.common.data.id.DashboardId;
|
||||
import org.thingsboard.server.common.data.id.TenantId;
|
||||
import org.thingsboard.server.common.data.id.UserId;
|
||||
import org.thingsboard.server.common.data.settings.UserDashboardAction;
|
||||
import org.thingsboard.server.common.data.settings.UserDashboardsInfo;
|
||||
import org.thingsboard.server.common.data.settings.UserSettings;
|
||||
import org.thingsboard.server.common.data.settings.UserSettingsType;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface TbUserSettingsService {
|
||||
|
||||
void updateUserSettings(TenantId tenantId, UserId userId, UserSettingsType type, JsonNode settings);
|
||||
|
||||
UserSettings saveUserSettings(TenantId tenantId, UserSettings userSettings);
|
||||
|
||||
UserSettings findUserSettings(TenantId tenantId, UserId userId, UserSettingsType type);
|
||||
|
||||
void deleteUserSettings(TenantId tenantId, UserId userId, UserSettingsType type, List<String> jsonPaths);
|
||||
|
||||
UserDashboardsInfo findUserDashboardsInfo(TenantId tenantId, UserId id);
|
||||
|
||||
UserDashboardsInfo reportUserDashboardAction(TenantId tenantId, UserId id, DashboardId dashboardId, UserDashboardAction action);
|
||||
}
|
||||
@ -22,7 +22,6 @@ import com.google.common.util.concurrent.Futures;
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
import lombok.Getter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
@ -91,6 +90,7 @@ import org.thingsboard.server.dao.device.DeviceProfileService;
|
||||
import org.thingsboard.server.dao.device.DeviceService;
|
||||
import org.thingsboard.server.dao.exception.DataValidationException;
|
||||
import org.thingsboard.server.dao.notification.NotificationSettingsService;
|
||||
import org.thingsboard.server.dao.notification.NotificationTargetService;
|
||||
import org.thingsboard.server.dao.queue.QueueService;
|
||||
import org.thingsboard.server.dao.rule.RuleChainService;
|
||||
import org.thingsboard.server.dao.settings.AdminSettingsService;
|
||||
@ -177,6 +177,9 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
|
||||
@Autowired
|
||||
private NotificationSettingsService notificationSettingsService;
|
||||
|
||||
@Autowired
|
||||
private NotificationTargetService notificationTargetService;
|
||||
|
||||
@Bean
|
||||
protected BCryptPasswordEncoder passwordEncoder() {
|
||||
return new BCryptPasswordEncoder();
|
||||
@ -679,27 +682,15 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
|
||||
|
||||
@Override
|
||||
public void createDefaultNotificationConfigs() {
|
||||
try {
|
||||
log.info("Creating default notification configs for system admin");
|
||||
log.info("Creating default notification configs for system admin");
|
||||
if (notificationTargetService.findNotificationTargetsByTenantId(TenantId.SYS_TENANT_ID, new PageLink(1)).getTotalElements() == 0) {
|
||||
notificationSettingsService.createDefaultNotificationConfigs(TenantId.SYS_TENANT_ID);
|
||||
} catch (Exception e) {
|
||||
if (StringUtils.contains(e.getMessage(), "already exists")) {
|
||||
log.info("Default notification configs are already present for system admin, skipping");
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
PageDataIterable<TenantId> tenants = new PageDataIterable<>(tenantService::findTenantsIds, 500);
|
||||
log.info("Creating default notification configs for all tenants");
|
||||
for (TenantId tenantId : tenants) {
|
||||
try {
|
||||
if (notificationTargetService.findNotificationTargetsByTenantId(tenantId, new PageLink(1)).getTotalElements() == 0) {
|
||||
notificationSettingsService.createDefaultNotificationConfigs(tenantId);
|
||||
} catch (Exception e) {
|
||||
if (StringUtils.contains(e.getMessage(), "already exists")) {
|
||||
log.info("Default notification configs are already present for tenant {}, skipping", tenantId);
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -65,6 +65,7 @@ public class InstallScripts {
|
||||
public static final String JSON_DIR = "json";
|
||||
public static final String SYSTEM_DIR = "system";
|
||||
public static final String TENANT_DIR = "tenant";
|
||||
public static final String EDGE_DIR = "edge";
|
||||
public static final String DEVICE_PROFILE_DIR = "device_profile";
|
||||
public static final String DEMO_DIR = "demo";
|
||||
public static final String RULE_CHAINS_DIR = "rule_chains";
|
||||
@ -74,8 +75,6 @@ public class InstallScripts {
|
||||
public static final String MODELS_LWM2M_DIR = "lwm2m-registry";
|
||||
public static final String CREDENTIALS_DIR = "credentials";
|
||||
|
||||
public static final String EDGE_MANAGEMENT = "edge_management";
|
||||
|
||||
public static final String JSON_EXT = ".json";
|
||||
public static final String XML_EXT = ".xml";
|
||||
|
||||
@ -109,7 +108,7 @@ public class InstallScripts {
|
||||
}
|
||||
|
||||
private Path getEdgeRuleChainsDir() {
|
||||
return Paths.get(getDataDir(), JSON_DIR, TENANT_DIR, EDGE_MANAGEMENT, RULE_CHAINS_DIR);
|
||||
return Paths.get(getDataDir(), JSON_DIR, EDGE_DIR, RULE_CHAINS_DIR);
|
||||
}
|
||||
|
||||
public String getDataDir() {
|
||||
@ -293,17 +292,15 @@ public class InstallScripts {
|
||||
}
|
||||
|
||||
private void doSaveLwm2mResource(TbResource resource) throws ThingsboardException {
|
||||
try {
|
||||
log.trace("Executing saveResource [{}]", resource);
|
||||
if (StringUtils.isEmpty(resource.getData())) {
|
||||
throw new DataValidationException("Resource data should be specified!");
|
||||
}
|
||||
toLwm2mResource(resource);
|
||||
log.trace("Executing saveResource [{}]", resource);
|
||||
if (StringUtils.isEmpty(resource.getData())) {
|
||||
throw new DataValidationException("Resource data should be specified!");
|
||||
}
|
||||
toLwm2mResource(resource);
|
||||
TbResource foundResource =
|
||||
resourceService.getResource(TenantId.SYS_TENANT_ID, ResourceType.LWM2M_MODEL, resource.getResourceKey());
|
||||
if (foundResource == null) {
|
||||
resourceService.saveResource(resource);
|
||||
} catch (DataValidationException e) {
|
||||
log.debug("[{}] {}", resource.getFileName(), e.getMessage());
|
||||
} catch (Exception ex) {
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -35,7 +35,7 @@ import org.thingsboard.rule.engine.api.TbEmail;
|
||||
import org.thingsboard.server.common.data.AdminSettings;
|
||||
import org.thingsboard.server.common.data.ApiFeature;
|
||||
import org.thingsboard.server.common.data.ApiUsageRecordKey;
|
||||
import org.thingsboard.server.common.data.ApiUsageStateMailMessage;
|
||||
import org.thingsboard.server.common.data.ApiUsageRecordState;
|
||||
import org.thingsboard.server.common.data.ApiUsageStateValue;
|
||||
import org.thingsboard.server.common.data.StringUtils;
|
||||
import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
|
||||
@ -64,8 +64,6 @@ public class DefaultMailService implements MailService {
|
||||
public static final String MAIL_PROP = "mail.";
|
||||
public static final String TARGET_EMAIL = "targetEmail";
|
||||
public static final String UTF_8 = "UTF-8";
|
||||
public static final int _10K = 10000;
|
||||
public static final int _1M = 1000000;
|
||||
|
||||
private final MessageSource messages;
|
||||
private final Configuration freemarkerConfig;
|
||||
@ -335,7 +333,7 @@ public class DefaultMailService implements MailService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendApiFeatureStateEmail(ApiFeature apiFeature, ApiUsageStateValue stateValue, String email, ApiUsageStateMailMessage msg) throws ThingsboardException {
|
||||
public void sendApiFeatureStateEmail(ApiFeature apiFeature, ApiUsageStateValue stateValue, String email, ApiUsageRecordState recordState) throws ThingsboardException {
|
||||
String subject = messages.getMessage("api.usage.state", null, Locale.US);
|
||||
|
||||
Map<String, Object> model = new HashMap<>();
|
||||
@ -350,11 +348,11 @@ public class DefaultMailService implements MailService {
|
||||
message = mergeTemplateIntoString("state.enabled.ftl", model);
|
||||
break;
|
||||
case WARNING:
|
||||
model.put("apiValueLabel", toDisabledValueLabel(apiFeature) + " " + toWarningValueLabel(msg.getKey(), msg.getValue(), msg.getThreshold()));
|
||||
model.put("apiValueLabel", toDisabledValueLabel(apiFeature) + " " + toWarningValueLabel(recordState));
|
||||
message = mergeTemplateIntoString("state.warning.ftl", model);
|
||||
break;
|
||||
case DISABLED:
|
||||
model.put("apiLimitValueLabel", toDisabledValueLabel(apiFeature) + " " + toDisabledValueLabel(msg.getKey(), msg.getThreshold()));
|
||||
model.put("apiLimitValueLabel", toDisabledValueLabel(apiFeature) + " " + toDisabledValueLabel(recordState));
|
||||
message = mergeTemplateIntoString("state.disabled.ftl", model);
|
||||
break;
|
||||
}
|
||||
@ -406,10 +404,10 @@ public class DefaultMailService implements MailService {
|
||||
}
|
||||
}
|
||||
|
||||
private String toWarningValueLabel(ApiUsageRecordKey key, long value, long threshold) {
|
||||
String valueInM = getValueAsString(value);
|
||||
String thresholdInM = getValueAsString(threshold);
|
||||
switch (key) {
|
||||
private String toWarningValueLabel(ApiUsageRecordState recordState) {
|
||||
String valueInM = recordState.getValueAsString();
|
||||
String thresholdInM = recordState.getThresholdAsString();
|
||||
switch (recordState.getKey()) {
|
||||
case STORAGE_DP_COUNT:
|
||||
case TRANSPORT_DP_COUNT:
|
||||
return valueInM + " out of " + thresholdInM + " allowed data points";
|
||||
@ -428,36 +426,26 @@ public class DefaultMailService implements MailService {
|
||||
}
|
||||
}
|
||||
|
||||
private String toDisabledValueLabel(ApiUsageRecordKey key, long value) {
|
||||
switch (key) {
|
||||
private String toDisabledValueLabel(ApiUsageRecordState recordState) {
|
||||
switch (recordState.getKey()) {
|
||||
case STORAGE_DP_COUNT:
|
||||
case TRANSPORT_DP_COUNT:
|
||||
return getValueAsString(value) + " data points";
|
||||
return recordState.getValueAsString() + " data points";
|
||||
case TRANSPORT_MSG_COUNT:
|
||||
return getValueAsString(value) + " messages";
|
||||
return recordState.getValueAsString() + " messages";
|
||||
case JS_EXEC_COUNT:
|
||||
return "JavaScript functions " + getValueAsString(value) + " times";
|
||||
return "JavaScript functions " + recordState.getValueAsString() + " times";
|
||||
case RE_EXEC_COUNT:
|
||||
return getValueAsString(value) + " Rule Engine messages";
|
||||
return recordState.getValueAsString() + " Rule Engine messages";
|
||||
case EMAIL_EXEC_COUNT:
|
||||
return getValueAsString(value) + " Email messages";
|
||||
return recordState.getValueAsString() + " Email messages";
|
||||
case SMS_EXEC_COUNT:
|
||||
return getValueAsString(value) + " SMS messages";
|
||||
return recordState.getValueAsString() + " SMS messages";
|
||||
default:
|
||||
throw new RuntimeException("Not implemented!");
|
||||
}
|
||||
}
|
||||
|
||||
private String getValueAsString(long value) {
|
||||
if (value > _1M && value % _1M < _10K) {
|
||||
return value / _1M + "M";
|
||||
} else if (value > _10K) {
|
||||
return String.format("%.2fM", ((double) value) / 1000000);
|
||||
} else {
|
||||
return value + "";
|
||||
}
|
||||
}
|
||||
|
||||
private void sendMail(JavaMailSenderImpl mailSender, String mailFrom, String email,
|
||||
String subject, String message, long timeout) throws ThingsboardException {
|
||||
try {
|
||||
|
||||
@ -22,13 +22,12 @@ import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.thingsboard.common.util.DonAsynchron;
|
||||
import org.thingsboard.rule.engine.api.MailService;
|
||||
import org.thingsboard.rule.engine.api.NotificationCenter;
|
||||
import org.thingsboard.rule.engine.api.SmsService;
|
||||
import org.thingsboard.server.common.data.EntityType;
|
||||
import org.thingsboard.server.common.data.User;
|
||||
import org.thingsboard.server.common.data.id.NotificationId;
|
||||
import org.thingsboard.server.common.data.id.NotificationRequestId;
|
||||
import org.thingsboard.server.common.data.id.NotificationRuleId;
|
||||
import org.thingsboard.server.common.data.id.NotificationTargetId;
|
||||
import org.thingsboard.server.common.data.id.TenantId;
|
||||
import org.thingsboard.server.common.data.id.UserId;
|
||||
@ -46,7 +45,6 @@ import org.thingsboard.server.common.data.notification.settings.NotificationSett
|
||||
import org.thingsboard.server.common.data.notification.targets.NotificationRecipient;
|
||||
import org.thingsboard.server.common.data.notification.targets.NotificationTarget;
|
||||
import org.thingsboard.server.common.data.notification.targets.platform.PlatformUsersNotificationTargetConfig;
|
||||
import org.thingsboard.server.common.data.notification.targets.platform.UsersFilterType;
|
||||
import org.thingsboard.server.common.data.notification.targets.slack.SlackNotificationTargetConfig;
|
||||
import org.thingsboard.server.common.data.notification.template.DeliveryMethodNotificationTemplate;
|
||||
import org.thingsboard.server.common.data.notification.template.NotificationTemplate;
|
||||
@ -84,6 +82,7 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Service
|
||||
@ -103,61 +102,59 @@ public class DefaultNotificationCenter extends AbstractSubscriptionService imple
|
||||
private final NotificationsTopicService notificationsTopicService;
|
||||
private final TbQueueProducerProvider producerProvider;
|
||||
private final RateLimitService rateLimitService;
|
||||
private final MailService mailService;
|
||||
private final SmsService smsService;
|
||||
|
||||
private Map<NotificationDeliveryMethod, NotificationChannel> channels;
|
||||
|
||||
|
||||
@Override
|
||||
public NotificationRequest processNotificationRequest(TenantId tenantId, NotificationRequest notificationRequest) {
|
||||
public NotificationRequest processNotificationRequest(TenantId tenantId, NotificationRequest request, Consumer<NotificationRequestStats> callback) {
|
||||
if (!rateLimitService.checkRateLimit(tenantId, LimitedApi.NOTIFICATION_REQUEST)) {
|
||||
throw new TbRateLimitsException(EntityType.TENANT);
|
||||
}
|
||||
NotificationSettings settings = notificationSettingsService.findNotificationSettings(tenantId);
|
||||
|
||||
NotificationTemplate notificationTemplate;
|
||||
if (notificationRequest.getTemplateId() != null) {
|
||||
notificationTemplate = notificationTemplateService.findNotificationTemplateById(tenantId, notificationRequest.getTemplateId());
|
||||
if (request.getTemplateId() != null) {
|
||||
notificationTemplate = notificationTemplateService.findNotificationTemplateById(tenantId, request.getTemplateId());
|
||||
} else {
|
||||
notificationTemplate = notificationRequest.getTemplate();
|
||||
notificationTemplate = request.getTemplate();
|
||||
}
|
||||
if (notificationTemplate == null) throw new IllegalArgumentException("Template is missing");
|
||||
|
||||
List<NotificationTarget> targets = notificationRequest.getTargets().stream().map(NotificationTargetId::new)
|
||||
List<NotificationTarget> targets = request.getTargets().stream().map(NotificationTargetId::new)
|
||||
.map(id -> notificationTargetService.findNotificationTargetById(tenantId, id)).collect(Collectors.toList());
|
||||
Set<NotificationDeliveryMethod> availableDeliveryMethods = getAvailableDeliveryMethods(tenantId);
|
||||
|
||||
NotificationRuleId ruleId = request.getRuleId();
|
||||
notificationTemplate.getConfiguration().getDeliveryMethodsTemplates().forEach((deliveryMethod, template) -> {
|
||||
if (!template.isEnabled()) return;
|
||||
if (!availableDeliveryMethods.contains(deliveryMethod)) {
|
||||
throw new IllegalArgumentException("Settings for " + deliveryMethod.getName() + " are missing");
|
||||
if (!channels.get(deliveryMethod).check(tenantId)) {
|
||||
throw new IllegalArgumentException("Unable to send notification via " + deliveryMethod.getName() + ": not configured or not working");
|
||||
}
|
||||
if (notificationRequest.getRuleId() == null) {
|
||||
if (ruleId == null) {
|
||||
if (targets.stream().noneMatch(target -> target.getConfiguration().getType().getSupportedDeliveryMethods().contains(deliveryMethod))) {
|
||||
throw new IllegalArgumentException("Target for " + deliveryMethod.getName() + " delivery method is missing");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (notificationRequest.getAdditionalConfig() != null) {
|
||||
NotificationRequestConfig config = notificationRequest.getAdditionalConfig();
|
||||
if (config.getSendingDelayInSec() > 0 && notificationRequest.getId() == null) {
|
||||
notificationRequest.setStatus(NotificationRequestStatus.SCHEDULED);
|
||||
NotificationRequest savedNotificationRequest = notificationRequestService.saveNotificationRequest(tenantId, notificationRequest);
|
||||
forwardToNotificationSchedulerService(tenantId, savedNotificationRequest.getId());
|
||||
return savedNotificationRequest;
|
||||
if (request.getAdditionalConfig() != null) {
|
||||
NotificationRequestConfig config = request.getAdditionalConfig();
|
||||
if (config.getSendingDelayInSec() > 0 && request.getId() == null) {
|
||||
request.setStatus(NotificationRequestStatus.SCHEDULED);
|
||||
request = notificationRequestService.saveNotificationRequest(tenantId, request);
|
||||
forwardToNotificationSchedulerService(tenantId, request.getId());
|
||||
return request;
|
||||
}
|
||||
}
|
||||
NotificationSettings settings = notificationSettingsService.findNotificationSettings(tenantId);
|
||||
|
||||
log.debug("Processing notification request (tenantId: {}, targets: {})", tenantId, notificationRequest.getTargets());
|
||||
notificationRequest.setStatus(NotificationRequestStatus.PROCESSING);
|
||||
NotificationRequest savedNotificationRequest = notificationRequestService.saveNotificationRequest(tenantId, notificationRequest);
|
||||
log.debug("Processing notification request (tenantId: {}, targets: {})", tenantId, request.getTargets());
|
||||
request.setStatus(NotificationRequestStatus.PROCESSING);
|
||||
request = notificationRequestService.saveNotificationRequest(tenantId, request);
|
||||
|
||||
NotificationProcessingContext ctx = NotificationProcessingContext.builder()
|
||||
.tenantId(tenantId)
|
||||
.request(savedNotificationRequest)
|
||||
.settings(settings)
|
||||
.request(request)
|
||||
.template(notificationTemplate)
|
||||
.settings(settings)
|
||||
.build();
|
||||
|
||||
notificationExecutor.submit(() -> {
|
||||
@ -169,7 +166,7 @@ public class DefaultNotificationCenter extends AbstractSubscriptionService imple
|
||||
}
|
||||
|
||||
Futures.whenAllComplete(results).run(() -> {
|
||||
NotificationRequestId requestId = savedNotificationRequest.getId();
|
||||
NotificationRequestId requestId = ctx.getRequest().getId();
|
||||
log.debug("[{}] Notification request processing is finished", requestId);
|
||||
NotificationRequestStats stats = ctx.getStats();
|
||||
try {
|
||||
@ -177,36 +174,39 @@ public class DefaultNotificationCenter extends AbstractSubscriptionService imple
|
||||
} catch (Exception e) {
|
||||
log.error("[{}] Failed to update stats for notification request", requestId, e);
|
||||
}
|
||||
|
||||
if (callback != null) {
|
||||
try {
|
||||
callback.accept(stats);
|
||||
} catch (Exception e) {
|
||||
log.error("Failed to process callback for notification request {}", requestId, e);
|
||||
}
|
||||
}
|
||||
}, dbCallbackExecutorService);
|
||||
});
|
||||
|
||||
return savedNotificationRequest;
|
||||
return request;
|
||||
}
|
||||
|
||||
private List<ListenableFuture<Void>> processForTarget(NotificationTarget target, NotificationProcessingContext ctx) {
|
||||
Iterable<? extends NotificationRecipient> recipients;
|
||||
switch (target.getConfiguration().getType()) {
|
||||
case PLATFORM_USERS: {
|
||||
PlatformUsersNotificationTargetConfig platformUsersTargetConfig = (PlatformUsersNotificationTargetConfig) target.getConfiguration();
|
||||
if (platformUsersTargetConfig.getUsersFilter().getType() == UsersFilterType.AFFECTED_USER) {
|
||||
if (ctx.getRequest().getInfo() instanceof RuleOriginatedNotificationInfo) {
|
||||
UserId targetUserId = ((RuleOriginatedNotificationInfo) ctx.getRequest().getInfo()).getTargetUserId();
|
||||
if (targetUserId != null) {
|
||||
recipients = List.of(userService.findUserById(ctx.getTenantId(), targetUserId));
|
||||
break;
|
||||
}
|
||||
}
|
||||
recipients = Collections.emptyList();
|
||||
PlatformUsersNotificationTargetConfig targetConfig = (PlatformUsersNotificationTargetConfig) target.getConfiguration();
|
||||
if (targetConfig.getUsersFilter().getType().isForRules()) {
|
||||
recipients = new PageDataIterable<>(pageLink -> {
|
||||
return notificationTargetService.findRecipientsForRuleNotificationTargetConfig(ctx.getTenantId(), targetConfig, (RuleOriginatedNotificationInfo) ctx.getRequest().getInfo(), pageLink);
|
||||
}, 500);
|
||||
} else {
|
||||
recipients = new PageDataIterable<>(pageLink -> {
|
||||
return notificationTargetService.findRecipientsForNotificationTargetConfig(ctx.getTenantId(), ctx.getCustomerId(), platformUsersTargetConfig, pageLink);
|
||||
return notificationTargetService.findRecipientsForNotificationTargetConfig(ctx.getTenantId(), targetConfig, pageLink);
|
||||
}, 500);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SLACK: {
|
||||
SlackNotificationTargetConfig slackTargetConfig = (SlackNotificationTargetConfig) target.getConfiguration();
|
||||
recipients = List.of(slackTargetConfig.getConversation());
|
||||
SlackNotificationTargetConfig targetConfig = (SlackNotificationTargetConfig) target.getConfiguration();
|
||||
recipients = List.of(targetConfig.getConversation());
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
@ -239,15 +239,10 @@ public class DefaultNotificationCenter extends AbstractSubscriptionService imple
|
||||
if (ctx.getStats().contains(deliveryMethod, recipient.getId())) {
|
||||
return Futures.immediateFailedFuture(new AlreadySentException());
|
||||
}
|
||||
Map<String, String> templateContext;
|
||||
if (recipient instanceof User) {
|
||||
templateContext = ctx.createTemplateContext(((User) recipient));
|
||||
} else {
|
||||
templateContext = Collections.emptyMap();
|
||||
}
|
||||
|
||||
DeliveryMethodNotificationTemplate processedTemplate;
|
||||
try {
|
||||
processedTemplate = ctx.getProcessedTemplate(deliveryMethod, templateContext);
|
||||
processedTemplate = ctx.getProcessedTemplate(deliveryMethod, recipient);
|
||||
} catch (Exception e) {
|
||||
return Futures.immediateFailedFuture(e);
|
||||
}
|
||||
@ -309,7 +304,7 @@ public class DefaultNotificationCenter extends AbstractSubscriptionService imple
|
||||
log.trace("Marked notification {} as read (recipient id: {}, tenant id: {})", notificationId, recipientId, tenantId);
|
||||
NotificationUpdate update = NotificationUpdate.builder()
|
||||
.updated(true)
|
||||
.notificationId(notificationId)
|
||||
.notificationId(notificationId.getId())
|
||||
.newStatus(NotificationStatus.READ)
|
||||
.build();
|
||||
onNotificationUpdate(tenantId, recipientId, update);
|
||||
@ -345,20 +340,15 @@ public class DefaultNotificationCenter extends AbstractSubscriptionService imple
|
||||
|
||||
@Override
|
||||
public Set<NotificationDeliveryMethod> getAvailableDeliveryMethods(TenantId tenantId) {
|
||||
Set<NotificationDeliveryMethod> deliveryMethods = new HashSet<>();
|
||||
deliveryMethods.add(NotificationDeliveryMethod.WEB);
|
||||
NotificationSettings notificationSettings = notificationSettingsService.findNotificationSettings(tenantId);
|
||||
if (notificationSettings.getDeliveryMethodsConfigs().containsKey(NotificationDeliveryMethod.SLACK)) {
|
||||
deliveryMethods.add(NotificationDeliveryMethod.SLACK);
|
||||
}
|
||||
try {
|
||||
mailService.testConnection(tenantId);
|
||||
deliveryMethods.add(NotificationDeliveryMethod.EMAIL);
|
||||
} catch (Exception e) {}
|
||||
if (smsService.isConfigured(tenantId)) {
|
||||
deliveryMethods.add(NotificationDeliveryMethod.SMS);
|
||||
}
|
||||
return deliveryMethods;
|
||||
return channels.values().stream()
|
||||
.filter(channel -> channel.check(tenantId))
|
||||
.map(NotificationChannel::getDeliveryMethod)
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean check(TenantId tenantId) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -374,6 +364,7 @@ public class DefaultNotificationCenter extends AbstractSubscriptionService imple
|
||||
.deleted(true)
|
||||
.build());
|
||||
} else if (notificationRequest.isScheduled()) {
|
||||
// TODO: just forward to scheduler service
|
||||
clusterService.broadcastEntityStateChangeEvent(tenantId, notificationRequestId, ComponentLifecycleEvent.DELETED);
|
||||
}
|
||||
}
|
||||
|
||||
@ -25,9 +25,10 @@ import org.thingsboard.rule.engine.api.NotificationCenter;
|
||||
import org.thingsboard.server.common.data.id.EntityId;
|
||||
import org.thingsboard.server.common.data.id.NotificationRequestId;
|
||||
import org.thingsboard.server.common.data.id.TenantId;
|
||||
import org.thingsboard.server.common.data.id.UserId;
|
||||
import org.thingsboard.server.common.data.notification.NotificationRequest;
|
||||
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.page.PageDataIterable;
|
||||
import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
|
||||
import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg;
|
||||
@ -109,14 +110,12 @@ public class DefaultNotificationSchedulerService extends AbstractPartitionBasedS
|
||||
|
||||
notificationExecutor.executeAsync(() -> {
|
||||
try {
|
||||
notificationCenter.processNotificationRequest(tenantId, notificationRequest);
|
||||
notificationCenter.processNotificationRequest(tenantId, notificationRequest, null);
|
||||
} catch (Exception e) {
|
||||
log.error("Failed to process scheduled notification request {}", notificationRequest.getId(), e);
|
||||
UserId senderId = notificationRequest.getSenderId();
|
||||
if (senderId != null) {
|
||||
notificationCenter.sendBasicNotification(tenantId, senderId, "Notification failure",
|
||||
"Failed to process scheduled notification (request " + notificationRequest.getId() + "): " + e.getMessage());
|
||||
}
|
||||
NotificationRequestStats stats = new NotificationRequestStats();
|
||||
stats.setError(e.getMessage());
|
||||
notificationRequestService.updateNotificationRequest(tenantId, request.getId(), NotificationRequestStatus.SENT, stats);
|
||||
}
|
||||
});
|
||||
scheduledNotificationRequests.remove(notificationRequest.getId());
|
||||
|
||||
@ -15,35 +15,30 @@
|
||||
*/
|
||||
package org.thingsboard.server.service.notification;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
import com.fasterxml.jackson.databind.node.TextNode;
|
||||
import com.google.common.base.Strings;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.thingsboard.server.common.data.User;
|
||||
import org.thingsboard.server.common.data.id.CustomerId;
|
||||
import org.apache.commons.collections4.MapUtils;
|
||||
import org.thingsboard.server.common.data.id.TenantId;
|
||||
import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod;
|
||||
import org.thingsboard.server.common.data.notification.NotificationRequest;
|
||||
import org.thingsboard.server.common.data.notification.NotificationRequestStats;
|
||||
import org.thingsboard.server.common.data.notification.info.NotificationInfo;
|
||||
import org.thingsboard.server.common.data.notification.info.RuleOriginatedNotificationInfo;
|
||||
import org.thingsboard.server.common.data.notification.settings.NotificationDeliveryMethodConfig;
|
||||
import org.thingsboard.server.common.data.notification.settings.NotificationSettings;
|
||||
import org.thingsboard.server.common.data.notification.targets.NotificationRecipient;
|
||||
import org.thingsboard.server.common.data.notification.template.DeliveryMethodNotificationTemplate;
|
||||
import org.thingsboard.server.common.data.notification.template.HasSubject;
|
||||
import org.thingsboard.server.common.data.notification.template.NotificationTemplate;
|
||||
import org.thingsboard.server.common.data.notification.template.NotificationTemplateConfig;
|
||||
import org.thingsboard.server.common.data.notification.template.WebDeliveryMethodNotificationTemplate;
|
||||
import org.thingsboard.server.common.data.util.TemplateUtils;
|
||||
|
||||
import java.util.EnumMap;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.isNotEmpty;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public class NotificationProcessingContext {
|
||||
@ -62,11 +57,9 @@ public class NotificationProcessingContext {
|
||||
@Getter
|
||||
private final NotificationRequestStats stats;
|
||||
|
||||
private static final Pattern TEMPLATE_PARAM_PATTERN = Pattern.compile("\\$\\{([a-zA-Z]+)(:[a-zA-Z]+)?}");
|
||||
|
||||
@Builder
|
||||
public NotificationProcessingContext(TenantId tenantId, NotificationRequest request, NotificationSettings settings,
|
||||
NotificationTemplate template) {
|
||||
public NotificationProcessingContext(TenantId tenantId, NotificationRequest request, NotificationTemplate template, NotificationSettings settings) {
|
||||
this.tenantId = tenantId;
|
||||
this.request = request;
|
||||
this.settings = settings;
|
||||
@ -80,6 +73,7 @@ public class NotificationProcessingContext {
|
||||
NotificationTemplateConfig templateConfig = notificationTemplate.getConfiguration();
|
||||
templateConfig.getDeliveryMethodsTemplates().forEach((deliveryMethod, template) -> {
|
||||
if (template.isEnabled()) {
|
||||
template = processTemplate(template, null); // processing template with immutable params
|
||||
templates.put(deliveryMethod, template);
|
||||
}
|
||||
});
|
||||
@ -90,74 +84,54 @@ public class NotificationProcessingContext {
|
||||
return (C) settings.getDeliveryMethodsConfigs().get(deliveryMethod);
|
||||
}
|
||||
|
||||
public <T extends DeliveryMethodNotificationTemplate> T getProcessedTemplate(NotificationDeliveryMethod deliveryMethod, Map<String, String> templateContext) {
|
||||
NotificationInfo info = request.getInfo();
|
||||
if (info != null) {
|
||||
templateContext = new HashMap<>(templateContext);
|
||||
templateContext.putAll(info.getTemplateData());
|
||||
public <T extends DeliveryMethodNotificationTemplate> T getProcessedTemplate(NotificationDeliveryMethod deliveryMethod, NotificationRecipient recipient) {
|
||||
T template = (T) templates.get(deliveryMethod);
|
||||
Map<String, String> additionalTemplateContext = null;
|
||||
if (recipient != null) {
|
||||
additionalTemplateContext = createTemplateContextForRecipient(recipient);
|
||||
}
|
||||
if (MapUtils.isNotEmpty(additionalTemplateContext) && template.containsAny(additionalTemplateContext.keySet().toArray(String[]::new))) {
|
||||
template = processTemplate(template, additionalTemplateContext);
|
||||
}
|
||||
return template;
|
||||
}
|
||||
|
||||
T template = (T) templates.get(deliveryMethod).copy();
|
||||
template.setBody(processTemplate(template.getBody(), templateContext));
|
||||
private <T extends DeliveryMethodNotificationTemplate> T processTemplate(T template, Map<String, String> additionalTemplateContext) {
|
||||
Map<String, String> templateContext = new HashMap<>();
|
||||
if (request.getInfo() != null) {
|
||||
templateContext.putAll(request.getInfo().getTemplateData());
|
||||
}
|
||||
if (additionalTemplateContext != null) {
|
||||
templateContext.putAll(additionalTemplateContext);
|
||||
}
|
||||
if (templateContext.isEmpty()) return template;
|
||||
|
||||
template = (T) template.copy();
|
||||
template.setBody(TemplateUtils.processTemplate(template.getBody(), templateContext));
|
||||
if (template instanceof HasSubject) {
|
||||
String subject = ((HasSubject) template).getSubject();
|
||||
((HasSubject) template).setSubject(processTemplate(subject, templateContext));
|
||||
((HasSubject) template).setSubject(TemplateUtils.processTemplate(subject, templateContext));
|
||||
}
|
||||
|
||||
if (deliveryMethod == NotificationDeliveryMethod.WEB) {
|
||||
if (template instanceof WebDeliveryMethodNotificationTemplate) {
|
||||
WebDeliveryMethodNotificationTemplate webNotificationTemplate = (WebDeliveryMethodNotificationTemplate) template;
|
||||
Optional<ObjectNode> buttonConfig = Optional.ofNullable(webNotificationTemplate.getAdditionalConfig())
|
||||
.map(config -> config.get("actionButtonConfig")).filter(JsonNode::isObject)
|
||||
.map(config -> (ObjectNode) config);
|
||||
if (buttonConfig.isPresent()) {
|
||||
JsonNode text = buttonConfig.get().get("text");
|
||||
if (text != null && text.isTextual()) {
|
||||
text = new TextNode(processTemplate(text.asText(), templateContext));
|
||||
buttonConfig.get().set("text", text);
|
||||
}
|
||||
JsonNode link = buttonConfig.get().get("link");
|
||||
if (link != null && link.isTextual()) {
|
||||
link = new TextNode(processTemplate(link.asText(), templateContext));
|
||||
buttonConfig.get().set("link", link);
|
||||
}
|
||||
String buttonText = webNotificationTemplate.getButtonText();
|
||||
if (isNotEmpty(buttonText)) {
|
||||
webNotificationTemplate.setButtonText(TemplateUtils.processTemplate(buttonText, templateContext));
|
||||
}
|
||||
String buttonLink = webNotificationTemplate.getButtonLink();
|
||||
if (isNotEmpty(buttonLink)) {
|
||||
webNotificationTemplate.setButtonLink(TemplateUtils.processTemplate(buttonLink, templateContext));
|
||||
}
|
||||
}
|
||||
return template;
|
||||
}
|
||||
|
||||
private static String processTemplate(String template, Map<String, String> context) {
|
||||
return TEMPLATE_PARAM_PATTERN.matcher(template).replaceAll(matchResult -> {
|
||||
String key = matchResult.group(1);
|
||||
String value = Strings.nullToEmpty(context.get(key));
|
||||
String function = matchResult.group(2);
|
||||
if (function != null) {
|
||||
switch (function) {
|
||||
case ":upperCase":
|
||||
return value.toUpperCase();
|
||||
case ":lowerCase":
|
||||
return value.toLowerCase();
|
||||
case ":capitalize":
|
||||
return StringUtils.capitalize(value.toLowerCase());
|
||||
}
|
||||
}
|
||||
return value;
|
||||
});
|
||||
}
|
||||
|
||||
public Map<String, String> createTemplateContext(User recipient) {
|
||||
Map<String, String> templateContext = new HashMap<>();
|
||||
templateContext.put("recipientEmail", recipient.getEmail());
|
||||
templateContext.put("recipientFirstName", Strings.nullToEmpty(recipient.getFirstName()));
|
||||
templateContext.put("recipientLastName", Strings.nullToEmpty(recipient.getLastName()));
|
||||
return templateContext;
|
||||
}
|
||||
|
||||
public CustomerId getCustomerId() {
|
||||
if (request.getInfo() instanceof RuleOriginatedNotificationInfo) {
|
||||
return ((RuleOriginatedNotificationInfo) request.getInfo()).getOriginatorEntityCustomerId();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
private Map<String, String> createTemplateContextForRecipient(NotificationRecipient recipient) {
|
||||
return Map.of(
|
||||
"recipientEmail", Strings.nullToEmpty(recipient.getEmail()),
|
||||
"recipientFirstName", Strings.nullToEmpty(recipient.getFirstName()),
|
||||
"recipientLastName", Strings.nullToEmpty(recipient.getLastName())
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -19,7 +19,9 @@ import com.google.common.util.concurrent.ListenableFuture;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.thingsboard.rule.engine.api.MailService;
|
||||
import org.thingsboard.rule.engine.api.TbEmail;
|
||||
import org.thingsboard.server.common.data.User;
|
||||
import org.thingsboard.server.common.data.id.TenantId;
|
||||
import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod;
|
||||
import org.thingsboard.server.common.data.notification.template.EmailDeliveryMethodNotificationTemplate;
|
||||
import org.thingsboard.server.service.mail.MailExecutorService;
|
||||
@ -35,11 +37,26 @@ public class EmailNotificationChannel implements NotificationChannel<User, Email
|
||||
@Override
|
||||
public ListenableFuture<Void> sendNotification(User recipient, EmailDeliveryMethodNotificationTemplate processedTemplate, NotificationProcessingContext ctx) {
|
||||
return executor.submit(() -> {
|
||||
mailService.sendEmail(recipient.getTenantId(), recipient.getEmail(), processedTemplate.getSubject(), processedTemplate.getBody());
|
||||
mailService.send(recipient.getTenantId(), null, TbEmail.builder()
|
||||
.to(recipient.getEmail())
|
||||
.subject(processedTemplate.getSubject())
|
||||
.body(processedTemplate.getBody())
|
||||
.html(true)
|
||||
.build());
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean check(TenantId tenantId) {
|
||||
try {
|
||||
mailService.testConnection(tenantId);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public NotificationDeliveryMethod getDeliveryMethod() {
|
||||
return NotificationDeliveryMethod.EMAIL;
|
||||
|
||||
@ -16,15 +16,18 @@
|
||||
package org.thingsboard.server.service.notification.channels;
|
||||
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
import org.thingsboard.server.common.data.id.TenantId;
|
||||
import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod;
|
||||
import org.thingsboard.server.service.notification.NotificationProcessingContext;
|
||||
import org.thingsboard.server.common.data.notification.targets.NotificationRecipient;
|
||||
import org.thingsboard.server.common.data.notification.template.DeliveryMethodNotificationTemplate;
|
||||
import org.thingsboard.server.service.notification.NotificationProcessingContext;
|
||||
|
||||
public interface NotificationChannel<R extends NotificationRecipient, T extends DeliveryMethodNotificationTemplate> {
|
||||
|
||||
ListenableFuture<Void> sendNotification(R recipient, T processedTemplate, NotificationProcessingContext ctx);
|
||||
|
||||
boolean check(TenantId tenantId);
|
||||
|
||||
NotificationDeliveryMethod getDeliveryMethod();
|
||||
|
||||
}
|
||||
|
||||
@ -19,7 +19,10 @@ import com.google.common.util.concurrent.ListenableFuture;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.thingsboard.rule.engine.api.slack.SlackService;
|
||||
import org.thingsboard.server.common.data.id.TenantId;
|
||||
import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod;
|
||||
import org.thingsboard.server.common.data.notification.settings.NotificationSettings;
|
||||
import org.thingsboard.server.dao.notification.NotificationSettingsService;
|
||||
import org.thingsboard.server.service.notification.NotificationProcessingContext;
|
||||
import org.thingsboard.server.common.data.notification.settings.SlackNotificationDeliveryMethodConfig;
|
||||
import org.thingsboard.server.common.data.notification.targets.slack.SlackConversation;
|
||||
@ -31,6 +34,7 @@ import org.thingsboard.server.service.executors.ExternalCallExecutorService;
|
||||
public class SlackNotificationChannel implements NotificationChannel<SlackConversation, SlackDeliveryMethodNotificationTemplate> {
|
||||
|
||||
private final SlackService slackService;
|
||||
private final NotificationSettingsService notificationSettingsService;
|
||||
private final ExternalCallExecutorService executor;
|
||||
|
||||
@Override
|
||||
@ -42,6 +46,12 @@ public class SlackNotificationChannel implements NotificationChannel<SlackConver
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean check(TenantId tenantId) {
|
||||
NotificationSettings notificationSettings = notificationSettingsService.findNotificationSettings(tenantId);
|
||||
return notificationSettings.getDeliveryMethodsConfigs().containsKey(NotificationDeliveryMethod.SLACK);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NotificationDeliveryMethod getDeliveryMethod() {
|
||||
return NotificationDeliveryMethod.SLACK;
|
||||
|
||||
@ -22,6 +22,7 @@ import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.thingsboard.rule.engine.api.SmsService;
|
||||
import org.thingsboard.server.common.data.User;
|
||||
import org.thingsboard.server.common.data.id.TenantId;
|
||||
import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod;
|
||||
import org.thingsboard.server.common.data.notification.template.SmsDeliveryMethodNotificationTemplate;
|
||||
import org.thingsboard.server.service.notification.NotificationProcessingContext;
|
||||
@ -47,6 +48,11 @@ public class SmsNotificationChannel implements NotificationChannel<User, SmsDeli
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean check(TenantId tenantId) {
|
||||
return smsService.isConfigured(tenantId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NotificationDeliveryMethod getDeliveryMethod() {
|
||||
return NotificationDeliveryMethod.SMS;
|
||||
|
||||
@ -32,16 +32,15 @@ import org.thingsboard.server.common.data.notification.NotificationRequestConfig
|
||||
import org.thingsboard.server.common.data.notification.NotificationRequestStatus;
|
||||
import org.thingsboard.server.common.data.notification.info.NotificationInfo;
|
||||
import org.thingsboard.server.common.data.notification.rule.NotificationRule;
|
||||
import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTrigger;
|
||||
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.plugin.ComponentLifecycleEvent;
|
||||
import org.thingsboard.server.common.msg.TbMsg;
|
||||
import org.thingsboard.server.common.msg.notification.trigger.NotificationRuleTrigger;
|
||||
import org.thingsboard.server.common.msg.notification.trigger.RuleEngineMsgTrigger;
|
||||
import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg;
|
||||
import org.thingsboard.server.dao.notification.NotificationRequestService;
|
||||
import org.thingsboard.server.dao.notification.NotificationRuleProcessingService;
|
||||
import org.thingsboard.server.dao.notification.NotificationRuleService;
|
||||
import org.thingsboard.server.dao.notification.trigger.RuleEngineMsgTrigger;
|
||||
import org.thingsboard.server.common.msg.notification.NotificationRuleProcessor;
|
||||
import org.thingsboard.server.service.executors.NotificationExecutorService;
|
||||
import org.thingsboard.server.service.notification.rule.trigger.NotificationRuleTriggerProcessor;
|
||||
import org.thingsboard.server.service.notification.rule.trigger.RuleEngineMsgNotificationRuleTriggerProcessor;
|
||||
@ -59,7 +58,7 @@ import java.util.stream.Collectors;
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||
public class DefaultNotificationRuleProcessingService implements NotificationRuleProcessingService {
|
||||
public class DefaultNotificationRuleProcessor implements NotificationRuleProcessor {
|
||||
|
||||
private final NotificationRuleService notificationRuleService;
|
||||
private final NotificationRequestService notificationRequestService;
|
||||
@ -69,16 +68,17 @@ public class DefaultNotificationRuleProcessingService implements NotificationRul
|
||||
|
||||
private final Map<NotificationRuleTriggerType, NotificationRuleTriggerProcessor> triggerProcessors = new EnumMap<>(NotificationRuleTriggerType.class);
|
||||
|
||||
private final Map<String, NotificationRuleTriggerType> ruleEngineMsgTypeToTriggerType = new HashMap<>();
|
||||
|
||||
@Override
|
||||
public void process(TenantId tenantId, NotificationRuleTrigger trigger) {
|
||||
List<NotificationRule> rules = notificationRuleService.findNotificationRulesByTenantIdAndTriggerType(
|
||||
trigger.getType().isTenantLevel() ? tenantId : TenantId.SYS_TENANT_ID, trigger.getType());
|
||||
public void process(NotificationRuleTrigger trigger) {
|
||||
NotificationRuleTriggerType triggerType = trigger.getType();
|
||||
if (triggerType == null) return;
|
||||
TenantId tenantId = triggerType.isTenantLevel() ? trigger.getTenantId() : TenantId.SYS_TENANT_ID;
|
||||
|
||||
List<NotificationRule> rules = notificationRuleService.findNotificationRulesByTenantIdAndTriggerType(tenantId, triggerType);
|
||||
for (NotificationRule rule : rules) {
|
||||
notificationExecutor.submit(() -> {
|
||||
try {
|
||||
processNotificationRule(tenantId, rule, trigger);
|
||||
processNotificationRule(rule, trigger);
|
||||
} catch (Throwable e) {
|
||||
log.error("Failed to process notification rule {} for trigger type {} with trigger object {}", rule.getId(), rule.getTriggerType(), trigger, e);
|
||||
}
|
||||
@ -86,24 +86,12 @@ public class DefaultNotificationRuleProcessingService implements NotificationRul
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void process(TenantId tenantId, TbMsg ruleEngineMsg) {
|
||||
NotificationRuleTriggerType triggerType = ruleEngineMsgTypeToTriggerType.get(ruleEngineMsg.getType());
|
||||
if (triggerType == null) {
|
||||
return;
|
||||
}
|
||||
process(tenantId, RuleEngineMsgTrigger.builder()
|
||||
.msg(ruleEngineMsg)
|
||||
.triggerType(triggerType)
|
||||
.build());
|
||||
}
|
||||
|
||||
private void processNotificationRule(TenantId tenantId, NotificationRule rule, NotificationRuleTrigger trigger) {
|
||||
private void processNotificationRule(NotificationRule rule, NotificationRuleTrigger trigger) {
|
||||
NotificationRuleTriggerConfig triggerConfig = rule.getTriggerConfig();
|
||||
log.debug("Processing notification rule '{}' for trigger type {}", rule.getName(), rule.getTriggerType());
|
||||
|
||||
if (matchesClearRule(trigger, triggerConfig)) {
|
||||
List<NotificationRequest> notificationRequests = notificationRequestService.findNotificationRequestsByRuleIdAndOriginatorEntityId(tenantId, rule.getId(), trigger.getOriginatorEntityId());
|
||||
List<NotificationRequest> notificationRequests = findAlreadySentNotificationRequests(rule, trigger);
|
||||
if (notificationRequests.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
@ -113,11 +101,11 @@ public class DefaultNotificationRuleProcessingService implements NotificationRul
|
||||
.flatMap(notificationRequest -> notificationRequest.getTargets().stream())
|
||||
.distinct().collect(Collectors.toList());
|
||||
NotificationInfo notificationInfo = constructNotificationInfo(trigger, triggerConfig);
|
||||
submitNotificationRequest(tenantId, targets, rule, trigger.getOriginatorEntityId(), notificationInfo, 0);
|
||||
submitNotificationRequest(targets, rule, trigger.getOriginatorEntityId(), notificationInfo, 0);
|
||||
|
||||
notificationRequests.forEach(notificationRequest -> {
|
||||
if (notificationRequest.isScheduled()) {
|
||||
notificationCenter.deleteNotificationRequest(tenantId, notificationRequest.getId());
|
||||
notificationCenter.deleteNotificationRequest(rule.getTenantId(), notificationRequest.getId());
|
||||
}
|
||||
});
|
||||
return;
|
||||
@ -126,11 +114,40 @@ public class DefaultNotificationRuleProcessingService implements NotificationRul
|
||||
if (matchesFilter(trigger, triggerConfig)) {
|
||||
NotificationInfo notificationInfo = constructNotificationInfo(trigger, triggerConfig);
|
||||
rule.getRecipientsConfig().getTargetsTable().forEach((delay, targets) -> {
|
||||
submitNotificationRequest(tenantId, targets, rule, trigger.getOriginatorEntityId(), notificationInfo, delay);
|
||||
submitNotificationRequest(targets, rule, trigger.getOriginatorEntityId(), notificationInfo, delay);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private List<NotificationRequest> findAlreadySentNotificationRequests(NotificationRule rule, NotificationRuleTrigger trigger) {
|
||||
return notificationRequestService.findNotificationRequestsByRuleIdAndOriginatorEntityId(rule.getTenantId(), rule.getId(), trigger.getOriginatorEntityId());
|
||||
}
|
||||
|
||||
private void submitNotificationRequest(List<UUID> targets, NotificationRule rule,
|
||||
EntityId originatorEntityId, NotificationInfo notificationInfo, int delayInSec) {
|
||||
NotificationRequestConfig config = new NotificationRequestConfig();
|
||||
if (delayInSec > 0) {
|
||||
config.setSendingDelayInSec(delayInSec);
|
||||
}
|
||||
NotificationRequest notificationRequest = NotificationRequest.builder()
|
||||
.tenantId(rule.getTenantId())
|
||||
.targets(targets)
|
||||
.templateId(rule.getTemplateId())
|
||||
.additionalConfig(config)
|
||||
.info(notificationInfo)
|
||||
.ruleId(rule.getId())
|
||||
.originatorEntityId(originatorEntityId)
|
||||
.build();
|
||||
notificationExecutor.submit(() -> {
|
||||
try {
|
||||
log.debug("Submitting notification request for rule '{}' with delay of {} sec to targets {}", rule.getName(), delayInSec, targets);
|
||||
notificationCenter.processNotificationRequest(rule.getTenantId(), notificationRequest, null);
|
||||
} catch (Exception e) {
|
||||
log.error("Failed to process notification request for tenant {} for rule {}", rule.getTenantId(), rule.getId(), e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private boolean matchesFilter(NotificationRuleTrigger trigger, NotificationRuleTriggerConfig triggerConfig) {
|
||||
return triggerProcessors.get(triggerConfig.getTriggerType()).matchesFilter(trigger, triggerConfig);
|
||||
}
|
||||
@ -140,32 +157,7 @@ public class DefaultNotificationRuleProcessingService implements NotificationRul
|
||||
}
|
||||
|
||||
private NotificationInfo constructNotificationInfo(NotificationRuleTrigger trigger, NotificationRuleTriggerConfig triggerConfig) {
|
||||
return triggerProcessors.get(triggerConfig.getTriggerType()).constructNotificationInfo(trigger, triggerConfig);
|
||||
}
|
||||
|
||||
private void submitNotificationRequest(TenantId tenantId, List<UUID> targets, NotificationRule rule,
|
||||
EntityId originatorEntityId, NotificationInfo notificationInfo, int delayInSec) {
|
||||
NotificationRequestConfig config = new NotificationRequestConfig();
|
||||
if (delayInSec > 0) {
|
||||
config.setSendingDelayInSec(delayInSec);
|
||||
}
|
||||
NotificationRequest notificationRequest = NotificationRequest.builder()
|
||||
.tenantId(tenantId)
|
||||
.targets(targets)
|
||||
.templateId(rule.getTemplateId())
|
||||
.additionalConfig(config)
|
||||
.info(notificationInfo)
|
||||
.ruleId(rule.getId())
|
||||
.originatorEntityId(originatorEntityId)
|
||||
.build();
|
||||
notificationExecutor.submit(() -> {
|
||||
try {
|
||||
log.debug("Submitting notification request for rule '{}' with delay of {} sec to targets {}", rule.getName(), delayInSec, targets);
|
||||
notificationCenter.processNotificationRequest(tenantId, notificationRequest);
|
||||
} catch (Exception e) {
|
||||
log.error("Failed to process notification request for rule {}", rule.getId(), e);
|
||||
}
|
||||
});
|
||||
return triggerProcessors.get(triggerConfig.getTriggerType()).constructNotificationInfo(trigger);
|
||||
}
|
||||
|
||||
@EventListener(ComponentLifecycleMsg.class)
|
||||
@ -187,6 +179,7 @@ public class DefaultNotificationRuleProcessingService implements NotificationRul
|
||||
|
||||
@Autowired
|
||||
public void setTriggerProcessors(Collection<NotificationRuleTriggerProcessor> processors) {
|
||||
Map<String, NotificationRuleTriggerType> ruleEngineMsgTypeToTriggerType = new HashMap<>();
|
||||
processors.forEach(processor -> {
|
||||
triggerProcessors.put(processor.getTriggerType(), processor);
|
||||
if (processor instanceof RuleEngineMsgNotificationRuleTriggerProcessor) {
|
||||
@ -196,6 +189,7 @@ public class DefaultNotificationRuleProcessingService implements NotificationRul
|
||||
});
|
||||
}
|
||||
});
|
||||
RuleEngineMsgTrigger.msgTypeToTriggerType = ruleEngineMsgTypeToTriggerType;
|
||||
}
|
||||
|
||||
}
|
||||
@ -23,11 +23,11 @@ import org.thingsboard.server.common.data.alarm.AlarmAssignee;
|
||||
import org.thingsboard.server.common.data.alarm.AlarmInfo;
|
||||
import org.thingsboard.server.common.data.alarm.AlarmStatusFilter;
|
||||
import org.thingsboard.server.common.data.notification.info.AlarmAssignmentNotificationInfo;
|
||||
import org.thingsboard.server.common.data.notification.info.NotificationInfo;
|
||||
import org.thingsboard.server.common.data.notification.info.RuleOriginatedNotificationInfo;
|
||||
import org.thingsboard.server.common.data.notification.rule.trigger.AlarmAssignmentNotificationRuleTriggerConfig;
|
||||
import org.thingsboard.server.common.data.notification.rule.trigger.AlarmAssignmentNotificationRuleTriggerConfig.Action;
|
||||
import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTriggerType;
|
||||
import org.thingsboard.server.dao.notification.trigger.RuleEngineMsgTrigger;
|
||||
import org.thingsboard.server.common.msg.notification.trigger.RuleEngineMsgTrigger;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
@ -49,7 +49,7 @@ public class AlarmAssignmentTriggerProcessor implements RuleEngineMsgNotificatio
|
||||
}
|
||||
|
||||
@Override
|
||||
public NotificationInfo constructNotificationInfo(RuleEngineMsgTrigger trigger, AlarmAssignmentNotificationRuleTriggerConfig triggerConfig) {
|
||||
public RuleOriginatedNotificationInfo constructNotificationInfo(RuleEngineMsgTrigger trigger) {
|
||||
AlarmInfo alarmInfo = JacksonUtil.fromString(trigger.getMsg().getData(), AlarmInfo.class);
|
||||
AlarmAssignee assignee = alarmInfo.getAssignee();
|
||||
return AlarmAssignmentNotificationInfo.builder()
|
||||
@ -58,7 +58,9 @@ public class AlarmAssignmentTriggerProcessor implements RuleEngineMsgNotificatio
|
||||
.assigneeLastName(assignee != null ? assignee.getLastName() : null)
|
||||
.assigneeEmail(assignee != null ? assignee.getEmail() : null)
|
||||
.assigneeId(assignee != null ? assignee.getId() : null)
|
||||
.userName(trigger.getMsg().getMetaData().getValue("userName"))
|
||||
.userEmail(trigger.getMsg().getMetaData().getValue("userEmail"))
|
||||
.userFirstName(trigger.getMsg().getMetaData().getValue("userFirstName"))
|
||||
.userLastName(trigger.getMsg().getMetaData().getValue("userLastName"))
|
||||
.alarmId(alarmInfo.getUuidId())
|
||||
.alarmType(alarmInfo.getType())
|
||||
.alarmOriginator(alarmInfo.getOriginator())
|
||||
|
||||
@ -24,11 +24,11 @@ import org.thingsboard.server.common.data.alarm.AlarmCommentType;
|
||||
import org.thingsboard.server.common.data.alarm.AlarmInfo;
|
||||
import org.thingsboard.server.common.data.alarm.AlarmStatusFilter;
|
||||
import org.thingsboard.server.common.data.notification.info.AlarmCommentNotificationInfo;
|
||||
import org.thingsboard.server.common.data.notification.info.NotificationInfo;
|
||||
import org.thingsboard.server.common.data.notification.info.RuleOriginatedNotificationInfo;
|
||||
import org.thingsboard.server.common.data.notification.rule.trigger.AlarmCommentNotificationRuleTriggerConfig;
|
||||
import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTriggerType;
|
||||
import org.thingsboard.server.common.msg.TbMsg;
|
||||
import org.thingsboard.server.dao.notification.trigger.RuleEngineMsgTrigger;
|
||||
import org.thingsboard.server.common.msg.notification.trigger.RuleEngineMsgTrigger;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
@ -59,14 +59,16 @@ public class AlarmCommentTriggerProcessor implements RuleEngineMsgNotificationRu
|
||||
}
|
||||
|
||||
@Override
|
||||
public NotificationInfo constructNotificationInfo(RuleEngineMsgTrigger trigger, AlarmCommentNotificationRuleTriggerConfig triggerConfig) {
|
||||
public RuleOriginatedNotificationInfo constructNotificationInfo(RuleEngineMsgTrigger trigger) {
|
||||
TbMsg msg = trigger.getMsg();
|
||||
AlarmComment comment = JacksonUtil.fromString(msg.getMetaData().getValue("comment"), AlarmComment.class);
|
||||
AlarmInfo alarmInfo = JacksonUtil.fromString(msg.getData(), AlarmInfo.class);
|
||||
return AlarmCommentNotificationInfo.builder()
|
||||
.comment(comment.getComment().get("text").asText())
|
||||
.action(msg.getType().equals(DataConstants.COMMENT_CREATED) ? "added" : "updated")
|
||||
.userName(msg.getMetaData().getValue("userName"))
|
||||
.userEmail(trigger.getMsg().getMetaData().getValue("userEmail"))
|
||||
.userFirstName(trigger.getMsg().getMetaData().getValue("userFirstName"))
|
||||
.userLastName(trigger.getMsg().getMetaData().getValue("userLastName"))
|
||||
.alarmId(alarmInfo.getUuidId())
|
||||
.alarmType(alarmInfo.getType())
|
||||
.alarmOriginator(alarmInfo.getOriginator())
|
||||
|
||||
@ -20,13 +20,13 @@ import org.thingsboard.server.common.data.alarm.Alarm;
|
||||
import org.thingsboard.server.common.data.alarm.AlarmInfo;
|
||||
import org.thingsboard.server.common.data.alarm.AlarmStatusFilter;
|
||||
import org.thingsboard.server.common.data.notification.info.AlarmNotificationInfo;
|
||||
import org.thingsboard.server.common.data.notification.info.NotificationInfo;
|
||||
import org.thingsboard.server.common.data.notification.info.RuleOriginatedNotificationInfo;
|
||||
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.AlarmNotificationRuleTriggerConfig.ClearRule;
|
||||
import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTriggerType;
|
||||
import org.thingsboard.server.dao.alarm.AlarmApiCallResult;
|
||||
import org.thingsboard.server.dao.notification.trigger.AlarmTrigger;
|
||||
import org.thingsboard.server.common.data.alarm.AlarmApiCallResult;
|
||||
import org.thingsboard.server.common.msg.notification.trigger.AlarmTrigger;
|
||||
|
||||
import static org.apache.commons.collections.CollectionUtils.isEmpty;
|
||||
import static org.apache.commons.collections.CollectionUtils.isNotEmpty;
|
||||
@ -93,7 +93,7 @@ public class AlarmTriggerProcessor implements NotificationRuleTriggerProcessor<A
|
||||
}
|
||||
|
||||
@Override
|
||||
public NotificationInfo constructNotificationInfo(AlarmTrigger trigger, AlarmNotificationRuleTriggerConfig triggerConfig) {
|
||||
public RuleOriginatedNotificationInfo constructNotificationInfo(AlarmTrigger trigger) {
|
||||
AlarmApiCallResult alarmUpdate = trigger.getAlarmUpdate();
|
||||
AlarmInfo alarmInfo = alarmUpdate.getAlarm();
|
||||
return AlarmNotificationInfo.builder()
|
||||
|
||||
@ -0,0 +1,59 @@
|
||||
/**
|
||||
* Copyright © 2016-2023 The Thingsboard Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.thingsboard.server.service.notification.rule.trigger;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.thingsboard.server.common.data.notification.info.ApiUsageLimitNotificationInfo;
|
||||
import org.thingsboard.server.common.data.notification.info.RuleOriginatedNotificationInfo;
|
||||
import org.thingsboard.server.common.data.notification.rule.trigger.ApiUsageLimitNotificationRuleTriggerConfig;
|
||||
import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTriggerType;
|
||||
import org.thingsboard.server.common.msg.notification.trigger.ApiUsageLimitTrigger;
|
||||
import org.thingsboard.server.dao.tenant.TenantService;
|
||||
|
||||
import static org.apache.commons.collections.CollectionUtils.isEmpty;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class ApiUsageLimitTriggerProcessor implements NotificationRuleTriggerProcessor<ApiUsageLimitTrigger, ApiUsageLimitNotificationRuleTriggerConfig> {
|
||||
|
||||
private final TenantService tenantService;
|
||||
|
||||
@Override
|
||||
public boolean matchesFilter(ApiUsageLimitTrigger trigger, ApiUsageLimitNotificationRuleTriggerConfig triggerConfig) {
|
||||
return (isEmpty(triggerConfig.getApiFeatures()) || triggerConfig.getApiFeatures().contains(trigger.getState().getApiFeature())) &&
|
||||
(isEmpty(triggerConfig.getNotifyOn()) || triggerConfig.getNotifyOn().contains(trigger.getStatus()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public RuleOriginatedNotificationInfo constructNotificationInfo(ApiUsageLimitTrigger trigger) {
|
||||
return ApiUsageLimitNotificationInfo.builder()
|
||||
.feature(trigger.getState().getApiFeature())
|
||||
.recordKey(trigger.getState().getKey())
|
||||
.status(trigger.getStatus())
|
||||
.limit(trigger.getState().getThresholdAsString())
|
||||
.currentValue(trigger.getState().getValueAsString())
|
||||
.tenantId(trigger.getTenantId())
|
||||
.tenantName(tenantService.findTenantById(trigger.getTenantId()).getName())
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public NotificationRuleTriggerType getTriggerType() {
|
||||
return NotificationRuleTriggerType.API_USAGE_LIMIT;
|
||||
}
|
||||
|
||||
}
|
||||
@ -22,24 +22,29 @@ import org.thingsboard.server.common.data.DataConstants;
|
||||
import org.thingsboard.server.common.data.DeviceProfile;
|
||||
import org.thingsboard.server.common.data.id.DeviceId;
|
||||
import org.thingsboard.server.common.data.id.TenantId;
|
||||
import org.thingsboard.server.common.data.notification.info.DeviceInactivityNotificationInfo;
|
||||
import org.thingsboard.server.common.data.notification.info.NotificationInfo;
|
||||
import org.thingsboard.server.common.data.notification.rule.trigger.DeviceInactivityNotificationRuleTriggerConfig;
|
||||
import org.thingsboard.server.common.data.notification.info.DeviceActivityNotificationInfo;
|
||||
import org.thingsboard.server.common.data.notification.info.RuleOriginatedNotificationInfo;
|
||||
import org.thingsboard.server.common.data.notification.rule.trigger.DeviceActivityNotificationRuleTriggerConfig;
|
||||
import org.thingsboard.server.common.data.notification.rule.trigger.DeviceActivityNotificationRuleTriggerConfig.DeviceEvent;
|
||||
import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTriggerType;
|
||||
import org.thingsboard.server.common.msg.TbMsg;
|
||||
import org.thingsboard.server.dao.notification.trigger.RuleEngineMsgTrigger;
|
||||
import org.thingsboard.server.common.msg.notification.trigger.RuleEngineMsgTrigger;
|
||||
import org.thingsboard.server.service.profile.TbDeviceProfileCache;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class DeviceInactivityTriggerProcessor implements RuleEngineMsgNotificationRuleTriggerProcessor<DeviceInactivityNotificationRuleTriggerConfig> {
|
||||
public class DeviceActivityTriggerProcessor implements RuleEngineMsgNotificationRuleTriggerProcessor<DeviceActivityNotificationRuleTriggerConfig> {
|
||||
|
||||
private final TbDeviceProfileCache deviceProfileCache;
|
||||
|
||||
@Override
|
||||
public boolean matchesFilter(RuleEngineMsgTrigger trigger, DeviceInactivityNotificationRuleTriggerConfig triggerConfig) {
|
||||
public boolean matchesFilter(RuleEngineMsgTrigger trigger, DeviceActivityNotificationRuleTriggerConfig triggerConfig) {
|
||||
DeviceEvent event = trigger.getMsg().getType().equals(DataConstants.ACTIVITY_EVENT) ? DeviceEvent.ACTIVE : DeviceEvent.INACTIVE;
|
||||
if (!triggerConfig.getNotifyOn().contains(event)) {
|
||||
return false;
|
||||
}
|
||||
DeviceId deviceId = (DeviceId) trigger.getMsg().getOriginator();
|
||||
if (CollectionUtils.isNotEmpty(triggerConfig.getDevices())) {
|
||||
return triggerConfig.getDevices().contains(deviceId.getId());
|
||||
@ -52,9 +57,10 @@ public class DeviceInactivityTriggerProcessor implements RuleEngineMsgNotificati
|
||||
}
|
||||
|
||||
@Override
|
||||
public NotificationInfo constructNotificationInfo(RuleEngineMsgTrigger trigger, DeviceInactivityNotificationRuleTriggerConfig triggerConfig) {
|
||||
public RuleOriginatedNotificationInfo constructNotificationInfo(RuleEngineMsgTrigger trigger) {
|
||||
TbMsg msg = trigger.getMsg();
|
||||
return DeviceInactivityNotificationInfo.builder()
|
||||
return DeviceActivityNotificationInfo.builder()
|
||||
.eventType(trigger.getMsg().getType().equals(DataConstants.ACTIVITY_EVENT) ? "active" : "inactive")
|
||||
.deviceId(msg.getOriginator().getId())
|
||||
.deviceName(msg.getMetaData().getValue("deviceName"))
|
||||
.deviceType(msg.getMetaData().getValue("deviceType"))
|
||||
@ -65,12 +71,12 @@ public class DeviceInactivityTriggerProcessor implements RuleEngineMsgNotificati
|
||||
|
||||
@Override
|
||||
public NotificationRuleTriggerType getTriggerType() {
|
||||
return NotificationRuleTriggerType.DEVICE_INACTIVITY;
|
||||
return NotificationRuleTriggerType.DEVICE_ACTIVITY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getSupportedMsgTypes() {
|
||||
return Set.of(DataConstants.INACTIVITY_EVENT);
|
||||
return Set.of(DataConstants.ACTIVITY_EVENT, DataConstants.INACTIVITY_EVENT);
|
||||
}
|
||||
|
||||
}
|
||||
@ -18,10 +18,10 @@ package org.thingsboard.server.service.notification.rule.trigger;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.thingsboard.server.common.data.notification.info.EntitiesLimitNotificationInfo;
|
||||
import org.thingsboard.server.common.data.notification.info.NotificationInfo;
|
||||
import org.thingsboard.server.common.data.notification.info.RuleOriginatedNotificationInfo;
|
||||
import org.thingsboard.server.common.data.notification.rule.trigger.EntitiesLimitNotificationRuleTriggerConfig;
|
||||
import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTriggerType;
|
||||
import org.thingsboard.server.dao.notification.trigger.EntitiesLimitTrigger;
|
||||
import org.thingsboard.server.common.msg.notification.trigger.EntitiesLimitTrigger;
|
||||
import org.thingsboard.server.dao.tenant.TenantService;
|
||||
|
||||
import static org.apache.commons.collections.CollectionUtils.isNotEmpty;
|
||||
@ -41,7 +41,7 @@ public class EntitiesLimitTriggerProcessor implements NotificationRuleTriggerPro
|
||||
}
|
||||
|
||||
@Override
|
||||
public NotificationInfo constructNotificationInfo(EntitiesLimitTrigger trigger, EntitiesLimitNotificationRuleTriggerConfig triggerConfig) {
|
||||
public RuleOriginatedNotificationInfo constructNotificationInfo(EntitiesLimitTrigger trigger) {
|
||||
return EntitiesLimitNotificationInfo.builder()
|
||||
.entityType(trigger.getEntityType())
|
||||
.currentCount(trigger.getCurrentCount())
|
||||
|
||||
@ -20,16 +20,18 @@ import org.thingsboard.server.common.data.DataConstants;
|
||||
import org.thingsboard.server.common.data.EntityType;
|
||||
import org.thingsboard.server.common.data.audit.ActionType;
|
||||
import org.thingsboard.server.common.data.notification.info.EntityActionNotificationInfo;
|
||||
import org.thingsboard.server.common.data.notification.info.NotificationInfo;
|
||||
import org.thingsboard.server.common.data.notification.info.RuleOriginatedNotificationInfo;
|
||||
import org.thingsboard.server.common.data.notification.rule.trigger.EntityActionNotificationRuleTriggerConfig;
|
||||
import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTriggerType;
|
||||
import org.thingsboard.server.common.msg.TbMsg;
|
||||
import org.thingsboard.server.dao.notification.trigger.RuleEngineMsgTrigger;
|
||||
import org.thingsboard.server.common.msg.notification.trigger.RuleEngineMsgTrigger;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
import static org.apache.commons.collections.CollectionUtils.isEmpty;
|
||||
|
||||
@Service
|
||||
public class EntityActionTriggerProcessor implements RuleEngineMsgNotificationRuleTriggerProcessor<EntityActionNotificationRuleTriggerConfig> {
|
||||
|
||||
@ -51,11 +53,11 @@ public class EntityActionTriggerProcessor implements RuleEngineMsgNotificationRu
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return triggerConfig.getEntityType() == null || getEntityType(trigger.getMsg()) == triggerConfig.getEntityType();
|
||||
return isEmpty(triggerConfig.getEntityTypes()) || triggerConfig.getEntityTypes().contains(getEntityType(trigger.getMsg()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public NotificationInfo constructNotificationInfo(RuleEngineMsgTrigger trigger, EntityActionNotificationRuleTriggerConfig triggerConfig) {
|
||||
public RuleOriginatedNotificationInfo constructNotificationInfo(RuleEngineMsgTrigger trigger) {
|
||||
TbMsg msg = trigger.getMsg();
|
||||
String msgType = msg.getType();
|
||||
ActionType actionType = msgType.equals(DataConstants.ENTITY_CREATED) ? ActionType.ADDED :
|
||||
@ -65,8 +67,10 @@ public class EntityActionTriggerProcessor implements RuleEngineMsgNotificationRu
|
||||
.entityId(msg.getOriginator())
|
||||
.entityName(msg.getMetaData().getValue("entityName"))
|
||||
.actionType(actionType)
|
||||
.originatorUserId(UUID.fromString(msg.getMetaData().getValue("userId")))
|
||||
.originatorUserName(msg.getMetaData().getValue("userName"))
|
||||
.userId(UUID.fromString(msg.getMetaData().getValue("userId")))
|
||||
.userEmail(trigger.getMsg().getMetaData().getValue("userEmail"))
|
||||
.userFirstName(trigger.getMsg().getMetaData().getValue("userFirstName"))
|
||||
.userLastName(trigger.getMsg().getMetaData().getValue("userLastName"))
|
||||
.entityCustomerId(msg.getCustomerId())
|
||||
.build();
|
||||
}
|
||||
|
||||
@ -15,13 +15,15 @@
|
||||
*/
|
||||
package org.thingsboard.server.service.notification.rule.trigger;
|
||||
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.thingsboard.common.util.JacksonUtil;
|
||||
import org.thingsboard.server.common.data.id.TenantId;
|
||||
import org.thingsboard.server.common.data.notification.info.NewPlatformVersionNotificationInfo;
|
||||
import org.thingsboard.server.common.data.notification.info.NotificationInfo;
|
||||
import org.thingsboard.server.common.data.notification.info.RuleOriginatedNotificationInfo;
|
||||
import org.thingsboard.server.common.data.notification.rule.trigger.NewPlatformVersionNotificationRuleTriggerConfig;
|
||||
import org.thingsboard.server.common.data.notification.rule.trigger.NewPlatformVersionTrigger;
|
||||
import org.thingsboard.server.common.msg.notification.trigger.NewPlatformVersionTrigger;
|
||||
import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTriggerType;
|
||||
import org.thingsboard.server.common.msg.queue.ServiceType;
|
||||
import org.thingsboard.server.queue.discovery.PartitionService;
|
||||
@ -42,9 +44,9 @@ public class NewPlatformVersionTriggerProcessor implements NotificationRuleTrigg
|
||||
}
|
||||
|
||||
@Override
|
||||
public NotificationInfo constructNotificationInfo(NewPlatformVersionTrigger trigger, NewPlatformVersionNotificationRuleTriggerConfig triggerConfig) {
|
||||
public RuleOriginatedNotificationInfo constructNotificationInfo(NewPlatformVersionTrigger trigger) {
|
||||
return NewPlatformVersionNotificationInfo.builder()
|
||||
.message(trigger.getMessage().getMessage())
|
||||
.message(JacksonUtil.convertValue(trigger.getMessage(), new TypeReference<>() {}))
|
||||
.build();
|
||||
}
|
||||
|
||||
|
||||
@ -15,8 +15,8 @@
|
||||
*/
|
||||
package org.thingsboard.server.service.notification.rule.trigger;
|
||||
|
||||
import org.thingsboard.server.common.data.notification.info.NotificationInfo;
|
||||
import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTrigger;
|
||||
import org.thingsboard.server.common.data.notification.info.RuleOriginatedNotificationInfo;
|
||||
import org.thingsboard.server.common.msg.notification.trigger.NotificationRuleTrigger;
|
||||
import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTriggerConfig;
|
||||
import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTriggerType;
|
||||
|
||||
@ -28,7 +28,7 @@ public interface NotificationRuleTriggerProcessor<T extends NotificationRuleTrig
|
||||
return false;
|
||||
}
|
||||
|
||||
NotificationInfo constructNotificationInfo(T trigger, C triggerConfig);
|
||||
RuleOriginatedNotificationInfo constructNotificationInfo(T trigger);
|
||||
|
||||
NotificationRuleTriggerType getTriggerType();
|
||||
|
||||
|
||||
@ -15,25 +15,31 @@
|
||||
*/
|
||||
package org.thingsboard.server.service.notification.rule.trigger;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.exception.ExceptionUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.thingsboard.server.common.data.EntityType;
|
||||
import org.thingsboard.server.common.data.notification.info.NotificationInfo;
|
||||
import org.thingsboard.server.common.data.notification.info.RuleEngineComponentLifecycleEventNotificationInfo;
|
||||
import org.thingsboard.server.common.data.notification.info.RuleOriginatedNotificationInfo;
|
||||
import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTriggerType;
|
||||
import org.thingsboard.server.common.data.notification.rule.trigger.RuleEngineComponentLifecycleEventNotificationRuleTriggerConfig;
|
||||
import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
|
||||
import org.thingsboard.server.dao.notification.trigger.RuleEngineComponentLifecycleEventTrigger;
|
||||
import org.thingsboard.server.common.msg.queue.ServiceType;
|
||||
import org.thingsboard.server.common.msg.notification.trigger.RuleEngineComponentLifecycleEventTrigger;
|
||||
import org.thingsboard.server.queue.discovery.PartitionService;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.util.Set;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class RuleEngineComponentLifecycleEventTriggerProcessor implements NotificationRuleTriggerProcessor<RuleEngineComponentLifecycleEventTrigger, RuleEngineComponentLifecycleEventNotificationRuleTriggerConfig> {
|
||||
|
||||
private final PartitionService partitionService;
|
||||
|
||||
@Override
|
||||
public boolean matchesFilter(RuleEngineComponentLifecycleEventTrigger trigger, RuleEngineComponentLifecycleEventNotificationRuleTriggerConfig triggerConfig) {
|
||||
if (CollectionUtils.isNotEmpty(triggerConfig.getRuleChains())) {
|
||||
@ -41,6 +47,9 @@ public class RuleEngineComponentLifecycleEventTriggerProcessor implements Notifi
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (!partitionService.resolve(ServiceType.TB_RULE_ENGINE, trigger.getTenantId(), trigger.getComponentId()).isMyPartition()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
EntityType componentType = trigger.getComponentId().getEntityType();
|
||||
Set<ComponentLifecycleEvent> trackedEvents;
|
||||
@ -68,7 +77,7 @@ public class RuleEngineComponentLifecycleEventTriggerProcessor implements Notifi
|
||||
}
|
||||
|
||||
@Override
|
||||
public NotificationInfo constructNotificationInfo(RuleEngineComponentLifecycleEventTrigger trigger, RuleEngineComponentLifecycleEventNotificationRuleTriggerConfig triggerConfig) {
|
||||
public RuleOriginatedNotificationInfo constructNotificationInfo(RuleEngineComponentLifecycleEventTrigger trigger) {
|
||||
return RuleEngineComponentLifecycleEventNotificationInfo.builder()
|
||||
.ruleChainId(trigger.getRuleChainId())
|
||||
.ruleChainName(trigger.getRuleChainName())
|
||||
|
||||
@ -16,7 +16,7 @@
|
||||
package org.thingsboard.server.service.notification.rule.trigger;
|
||||
|
||||
import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTriggerConfig;
|
||||
import org.thingsboard.server.dao.notification.trigger.RuleEngineMsgTrigger;
|
||||
import org.thingsboard.server.common.msg.notification.trigger.RuleEngineMsgTrigger;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
|
||||
@ -35,6 +35,7 @@ import org.thingsboard.server.common.data.id.EntityId;
|
||||
import org.thingsboard.server.common.data.id.TenantId;
|
||||
import org.thingsboard.server.common.data.kv.AttributeKvEntry;
|
||||
import org.thingsboard.server.common.data.page.PageData;
|
||||
import org.thingsboard.server.common.data.query.AlarmCountQuery;
|
||||
import org.thingsboard.server.common.data.query.AlarmData;
|
||||
import org.thingsboard.server.common.data.query.AlarmDataQuery;
|
||||
import org.thingsboard.server.common.data.query.ComplexFilterPredicate;
|
||||
@ -205,6 +206,11 @@ public class DefaultEntityQueryService implements EntityQueryService {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public long countAlarmsByQuery(SecurityUser securityUser, AlarmCountQuery query) {
|
||||
return alarmService.countAlarmsByQuery(securityUser.getTenantId(), securityUser.getCustomerId(), query);
|
||||
}
|
||||
|
||||
private EntityDataQuery buildEntityDataQuery(AlarmDataQuery query) {
|
||||
EntityDataSortOrder sortOrder = query.getPageLink().getSortOrder();
|
||||
EntityDataSortOrder entitiesSortOrder;
|
||||
|
||||
@ -19,6 +19,7 @@ import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.context.request.async.DeferredResult;
|
||||
import org.thingsboard.server.common.data.id.TenantId;
|
||||
import org.thingsboard.server.common.data.page.PageData;
|
||||
import org.thingsboard.server.common.data.query.AlarmCountQuery;
|
||||
import org.thingsboard.server.common.data.query.AlarmData;
|
||||
import org.thingsboard.server.common.data.query.AlarmDataQuery;
|
||||
import org.thingsboard.server.common.data.query.EntityCountQuery;
|
||||
@ -34,6 +35,8 @@ public interface EntityQueryService {
|
||||
|
||||
PageData<AlarmData> findAlarmDataByQuery(SecurityUser securityUser, AlarmDataQuery query);
|
||||
|
||||
long countAlarmsByQuery(SecurityUser securityUser, AlarmCountQuery query);
|
||||
|
||||
DeferredResult<ResponseEntity> getKeysByQuery(SecurityUser securityUser, TenantId tenantId, EntityDataQuery query,
|
||||
boolean isTimeseries, boolean isAttributes);
|
||||
|
||||
|
||||
@ -35,10 +35,12 @@ import org.thingsboard.server.common.data.id.UserId;
|
||||
import org.thingsboard.server.common.data.rpc.RpcError;
|
||||
import org.thingsboard.server.common.msg.MsgType;
|
||||
import org.thingsboard.server.common.msg.TbActorMsg;
|
||||
import org.thingsboard.server.common.msg.notification.trigger.NotificationRuleTrigger;
|
||||
import org.thingsboard.server.common.msg.queue.ServiceType;
|
||||
import org.thingsboard.server.common.msg.queue.TbCallback;
|
||||
import org.thingsboard.server.common.msg.rpc.FromDeviceRpcResponse;
|
||||
import org.thingsboard.server.common.stats.StatsFactory;
|
||||
import org.thingsboard.server.common.msg.notification.NotificationRuleProcessor;
|
||||
import org.thingsboard.server.dao.tenant.TbTenantProfileCache;
|
||||
import org.thingsboard.server.gen.transport.TransportProtos;
|
||||
import org.thingsboard.server.gen.transport.TransportProtos.DeviceStateServiceMsgProto;
|
||||
@ -129,6 +131,7 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService<ToCore
|
||||
private final OtaPackageStateService firmwareStateService;
|
||||
private final GitVersionControlQueueService vcQueueService;
|
||||
private final NotificationSchedulerService notificationSchedulerService;
|
||||
private final NotificationRuleProcessor notificationRuleProcessor;
|
||||
private final TbCoreConsumerStats stats;
|
||||
protected final TbQueueConsumer<TbProtoQueueMsg<ToUsageStatsServiceMsg>> usageStatsConsumer;
|
||||
private final TbQueueConsumer<TbProtoQueueMsg<ToOtaPackageStateServiceMsg>> firmwareStatesConsumer;
|
||||
@ -156,7 +159,8 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService<ToCore
|
||||
PartitionService partitionService,
|
||||
ApplicationEventPublisher eventPublisher,
|
||||
Optional<JwtSettingsService> jwtSettingsService,
|
||||
NotificationSchedulerService notificationSchedulerService) {
|
||||
NotificationSchedulerService notificationSchedulerService,
|
||||
NotificationRuleProcessor notificationRuleProcessor) {
|
||||
super(actorContext, encodingService, tenantProfileCache, deviceProfileCache, assetProfileCache, apiUsageStateService, partitionService, eventPublisher, tbCoreQueueFactory.createToCoreNotificationsMsgConsumer(), jwtSettingsService);
|
||||
this.mainConsumer = tbCoreQueueFactory.createToCoreMsgConsumer();
|
||||
this.usageStatsConsumer = tbCoreQueueFactory.createToUsageStatsServiceMsgConsumer();
|
||||
@ -171,6 +175,7 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService<ToCore
|
||||
this.firmwareStateService = firmwareStateService;
|
||||
this.vcQueueService = vcQueueService;
|
||||
this.notificationSchedulerService = notificationSchedulerService;
|
||||
this.notificationRuleProcessor = notificationRuleProcessor;
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
@ -269,6 +274,9 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService<ToCore
|
||||
TransportProtos.NotificationSchedulerServiceMsg notificationSchedulerServiceMsg = toCoreMsg.getNotificationSchedulerServiceMsg();
|
||||
log.trace("[{}] Forwarding message to notification scheduler service {}", id, toCoreMsg.getNotificationSchedulerServiceMsg());
|
||||
forwardToNotificationSchedulerService(notificationSchedulerServiceMsg, callback);
|
||||
} else if (toCoreMsg.hasNotificationRuleProcessorMsg()) {
|
||||
Optional<NotificationRuleTrigger> notificationRuleTrigger = encodingService.decode(toCoreMsg.getNotificationRuleProcessorMsg().getTrigger().toByteArray());
|
||||
notificationRuleTrigger.ifPresent(notificationRuleProcessor::process);
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
log.warn("[{}] Failed to process message: {}", id, msg, e);
|
||||
|
||||
@ -44,6 +44,8 @@ import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.isNotEmpty;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class DefaultSlackService implements SlackService {
|
||||
@ -80,7 +82,14 @@ public class DefaultSlackService implements SlackService {
|
||||
.map(user -> {
|
||||
SlackConversation conversation = new SlackConversation();
|
||||
conversation.setId(user.getId());
|
||||
conversation.setName(String.format("@%s (%s)", user.getName(), user.getRealName()));
|
||||
conversation.setShortName(user.getName());
|
||||
conversation.setWholeName(user.getProfile() != null ? user.getProfile().getRealNameNormalized() : user.getRealName());
|
||||
conversation.setEmail(user.getProfile() != null ? user.getProfile().getEmail() : null);
|
||||
String title = "@" + conversation.getShortName();
|
||||
if (isNotEmpty(conversation.getWholeName()) && !conversation.getWholeName().equals(conversation.getShortName())) {
|
||||
title += " (" + conversation.getWholeName() + ")";
|
||||
}
|
||||
conversation.setTitle(title);
|
||||
return conversation;
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
@ -99,7 +108,9 @@ public class DefaultSlackService implements SlackService {
|
||||
.map(channel -> {
|
||||
SlackConversation conversation = new SlackConversation();
|
||||
conversation.setId(channel.getId());
|
||||
conversation.setName("#" + channel.getName());
|
||||
conversation.setShortName(channel.getName());
|
||||
conversation.setWholeName(channel.getNameNormalized());
|
||||
conversation.setTitle("#" + channel.getName());
|
||||
return conversation;
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
@ -111,7 +122,7 @@ public class DefaultSlackService implements SlackService {
|
||||
public SlackConversation findConversation(TenantId tenantId, String token, SlackConversationType conversationType, String namePattern) {
|
||||
List<SlackConversation> conversations = listConversations(tenantId, token, conversationType);
|
||||
return conversations.stream()
|
||||
.filter(conversation -> StringUtils.containsIgnoreCase(conversation.getName(), namePattern))
|
||||
.filter(conversation -> StringUtils.containsIgnoreCase(conversation.getTitle(), namePattern))
|
||||
.findFirst().orElse(null);
|
||||
}
|
||||
|
||||
@ -144,7 +155,7 @@ public class DefaultSlackService implements SlackService {
|
||||
String neededScope = response.getNeeded();
|
||||
error = "bot token scope '" + neededScope + "' is needed";
|
||||
}
|
||||
throw new RuntimeException("Failed to send message via Slack: " + error);
|
||||
throw new RuntimeException("Slack API error: " + error);
|
||||
}
|
||||
|
||||
return response;
|
||||
|
||||
@ -316,20 +316,11 @@ public class DefaultSubscriptionManagerService extends TbApplicationEventListene
|
||||
Set<TbSubscription> subscriptions = subscriptionsByEntityId.get(recipientId);
|
||||
if (subscriptions != null) {
|
||||
NotificationsSubscriptionUpdate subscriptionUpdate = new NotificationsSubscriptionUpdate(notificationUpdate);
|
||||
log.trace("Handling notificationUpdate for user {}: {}", recipientId, notificationUpdate);
|
||||
subscriptions.stream()
|
||||
.filter(subscription -> subscription.getType() == TbSubscriptionType.NOTIFICATIONS
|
||||
|| subscription.getType() == TbSubscriptionType.NOTIFICATIONS_COUNT)
|
||||
.forEach(subscription -> {
|
||||
if (serviceId.equals(subscription.getServiceId())) {
|
||||
localSubscriptionService.onSubscriptionUpdate(subscription.getSessionId(),
|
||||
subscription.getSubscriptionId(), subscriptionUpdate, TbCallback.EMPTY);
|
||||
} else {
|
||||
TopicPartitionInfo tpi = notificationsTopicService.getNotificationsTopic(ServiceType.TB_CORE, subscription.getServiceId());
|
||||
ToCoreNotificationMsg updateProto = TbSubscriptionUtils.notificationsSubUpdateToProto(subscription, subscriptionUpdate);
|
||||
TbProtoQueueMsg<ToCoreNotificationMsg> queueMsg = new TbProtoQueueMsg<>(subscription.getEntityId().getId(), updateProto);
|
||||
toCoreNotificationsProducer.send(tpi, queueMsg, null);
|
||||
}
|
||||
});
|
||||
.forEach(subscription -> onNotificationsSubUpdate(subscriptionUpdate, subscription));
|
||||
}
|
||||
callback.onSuccess();
|
||||
}
|
||||
@ -341,21 +332,37 @@ public class DefaultSubscriptionManagerService extends TbApplicationEventListene
|
||||
if (entityId.getEntityType() != EntityType.USER) {
|
||||
return;
|
||||
}
|
||||
log.trace("Handling notificationRequestUpdate for user {}: {}", entityId, notificationRequestUpdate);
|
||||
subscriptions.forEach(subscription -> {
|
||||
if (subscription.getType() != TbSubscriptionType.NOTIFICATIONS &&
|
||||
subscription.getType() != TbSubscriptionType.NOTIFICATIONS_COUNT) {
|
||||
return;
|
||||
}
|
||||
if (!subscription.getTenantId().equals(tenantId) || !subscription.getServiceId().equals(serviceId)) {
|
||||
if (!subscription.getTenantId().equals(tenantId)) {
|
||||
return;
|
||||
}
|
||||
localSubscriptionService.onSubscriptionUpdate(subscription.getSessionId(), subscription.getSubscriptionId(),
|
||||
subscriptionUpdate, TbCallback.EMPTY);
|
||||
onNotificationsSubUpdate(subscriptionUpdate, subscription);
|
||||
});
|
||||
});
|
||||
callback.onSuccess();
|
||||
}
|
||||
|
||||
private void onNotificationsSubUpdate(NotificationsSubscriptionUpdate subscriptionUpdate, TbSubscription subscription) {
|
||||
if (serviceId.equals(subscription.getServiceId())) {
|
||||
log.trace("[{}][{}][{}] Subscription session is managed by current service, forwarding to localSubscriptionService (update: {})",
|
||||
subscription.getServiceId(), subscription.getEntityId(), subscription.getSessionId(), subscriptionUpdate);
|
||||
localSubscriptionService.onSubscriptionUpdate(subscription.getSessionId(),
|
||||
subscription.getSubscriptionId(), subscriptionUpdate, TbCallback.EMPTY);
|
||||
} else {
|
||||
log.trace("[{}][{}][{}] Subscription session is not managed by current service (update: {})",
|
||||
subscription.getServiceId(), subscription.getEntityId(), subscription.getSessionId(), subscriptionUpdate);
|
||||
TopicPartitionInfo tpi = notificationsTopicService.getNotificationsTopic(ServiceType.TB_CORE, subscription.getServiceId());
|
||||
ToCoreNotificationMsg updateProto = TbSubscriptionUtils.notificationsSubUpdateToProto(subscription, subscriptionUpdate);
|
||||
TbProtoQueueMsg<ToCoreNotificationMsg> queueMsg = new TbProtoQueueMsg<>(subscription.getEntityId().getId(), updateProto);
|
||||
toCoreNotificationsProducer.send(tpi, queueMsg, null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttributesDelete(TenantId tenantId, EntityId entityId, String scope, List<String> keys, boolean notifyDevice, TbCallback callback) {
|
||||
onLocalTelemetrySubUpdate(entityId,
|
||||
|
||||
@ -54,6 +54,7 @@ import org.thingsboard.server.service.ws.WebSocketSessionRef;
|
||||
import org.thingsboard.server.service.ws.telemetry.cmd.v2.AggHistoryCmd;
|
||||
import org.thingsboard.server.service.ws.telemetry.cmd.v2.AggKey;
|
||||
import org.thingsboard.server.service.ws.telemetry.cmd.v2.AggTimeSeriesCmd;
|
||||
import org.thingsboard.server.service.ws.telemetry.cmd.v2.AlarmCountCmd;
|
||||
import org.thingsboard.server.service.ws.telemetry.cmd.v2.AlarmDataCmd;
|
||||
import org.thingsboard.server.service.ws.telemetry.cmd.v2.AlarmDataUpdate;
|
||||
import org.thingsboard.server.service.ws.telemetry.cmd.v2.EntityCountCmd;
|
||||
@ -94,7 +95,7 @@ public class DefaultTbEntityDataSubscriptionService implements TbEntityDataSubsc
|
||||
private static final int DEFAULT_LIMIT = 100;
|
||||
private final Map<String, Map<Integer, TbAbstractSubCtx>> subscriptionsBySessionId = new ConcurrentHashMap<>();
|
||||
|
||||
@Autowired
|
||||
@Autowired @Lazy
|
||||
private WebSocketService wsService;
|
||||
|
||||
@Autowired
|
||||
@ -408,6 +409,26 @@ public class DefaultTbEntityDataSubscriptionService implements TbEntityDataSubsc
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCmd(WebSocketSessionRef session, AlarmCountCmd cmd) {
|
||||
TbAlarmCountSubCtx ctx = getSubCtx(session.getSessionId(), cmd.getCmdId());
|
||||
if (ctx == null) {
|
||||
ctx = createSubCtx(session, cmd);
|
||||
long start = System.currentTimeMillis();
|
||||
ctx.fetchData();
|
||||
long end = System.currentTimeMillis();
|
||||
stats.getAlarmQueryInvocationCnt().incrementAndGet();
|
||||
stats.getAlarmQueryTimeSpent().addAndGet(end - start);
|
||||
TbAlarmCountSubCtx finalCtx = ctx;
|
||||
ScheduledFuture<?> task = scheduler.scheduleWithFixedDelay(
|
||||
() -> refreshDynamicQuery(finalCtx),
|
||||
dynamicPageLinkRefreshInterval, dynamicPageLinkRefreshInterval, TimeUnit.SECONDS);
|
||||
finalCtx.setRefreshTask(task);
|
||||
} else {
|
||||
log.debug("[{}][{}] Received duplicate command: {}", session.getSessionId(), cmd.getCmdId(), cmd);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean validate(TbAbstractSubCtx<?> finalCtx) {
|
||||
if (finalCtx.isStopped()) {
|
||||
log.warn("[{}][{}][{}] Received validation task for already stopped context.", finalCtx.getTenantId(), finalCtx.getSessionId(), finalCtx.getCmdId());
|
||||
@ -501,6 +522,17 @@ public class DefaultTbEntityDataSubscriptionService implements TbEntityDataSubsc
|
||||
return ctx;
|
||||
}
|
||||
|
||||
private TbAlarmCountSubCtx createSubCtx(WebSocketSessionRef sessionRef, AlarmCountCmd cmd) {
|
||||
Map<Integer, TbAbstractSubCtx> sessionSubs = subscriptionsBySessionId.computeIfAbsent(sessionRef.getSessionId(), k -> new HashMap<>());
|
||||
TbAlarmCountSubCtx ctx = new TbAlarmCountSubCtx(serviceId, wsService, entityService, localSubscriptionService,
|
||||
attributesService, stats, alarmService, sessionRef, cmd.getCmdId());
|
||||
if (cmd.getQuery() != null) {
|
||||
ctx.setAndResolveQuery(cmd.getQuery());
|
||||
}
|
||||
sessionSubs.put(cmd.getCmdId(), ctx);
|
||||
return ctx;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <T extends TbAbstractSubCtx> T getSubCtx(String sessionId, int cmdId) {
|
||||
Map<Integer, TbAbstractSubCtx> sessionSubs = subscriptionsBySessionId.get(sessionId);
|
||||
|
||||
@ -0,0 +1,67 @@
|
||||
/**
|
||||
* Copyright © 2016-2023 The Thingsboard Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.thingsboard.server.service.subscription;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.thingsboard.server.common.data.query.AlarmCountQuery;
|
||||
import org.thingsboard.server.dao.alarm.AlarmService;
|
||||
import org.thingsboard.server.dao.attributes.AttributesService;
|
||||
import org.thingsboard.server.dao.entity.EntityService;
|
||||
import org.thingsboard.server.service.ws.WebSocketService;
|
||||
import org.thingsboard.server.service.ws.WebSocketSessionRef;
|
||||
import org.thingsboard.server.service.ws.telemetry.cmd.v2.AlarmCountUpdate;
|
||||
|
||||
@Slf4j
|
||||
@ToString(callSuper = true)
|
||||
public class TbAlarmCountSubCtx extends TbAbstractSubCtx<AlarmCountQuery> {
|
||||
|
||||
private final AlarmService alarmService;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
private volatile int result;
|
||||
|
||||
public TbAlarmCountSubCtx(String serviceId, WebSocketService wsService,
|
||||
EntityService entityService, TbLocalSubscriptionService localSubscriptionService,
|
||||
AttributesService attributesService, SubscriptionServiceStatistics stats, AlarmService alarmService,
|
||||
WebSocketSessionRef sessionRef, int cmdId) {
|
||||
super(serviceId, wsService, entityService, localSubscriptionService, attributesService, stats, sessionRef, cmdId);
|
||||
this.alarmService = alarmService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fetchData() {
|
||||
result = (int) alarmService.countAlarmsByQuery(getTenantId(), getCustomerId(), query);
|
||||
sendWsMsg(new AlarmCountUpdate(cmdId, result));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void update() {
|
||||
int newCount = (int) alarmService.countAlarmsByQuery(getTenantId(), getCustomerId(), query);
|
||||
if (newCount != result) {
|
||||
result = newCount;
|
||||
sendWsMsg(new AlarmCountUpdate(cmdId, result));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDynamic() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -16,6 +16,7 @@
|
||||
package org.thingsboard.server.service.subscription;
|
||||
|
||||
import org.thingsboard.server.service.ws.WebSocketSessionRef;
|
||||
import org.thingsboard.server.service.ws.telemetry.cmd.v2.AlarmCountCmd;
|
||||
import org.thingsboard.server.service.ws.telemetry.cmd.v2.AlarmDataCmd;
|
||||
import org.thingsboard.server.service.ws.telemetry.cmd.v2.EntityCountCmd;
|
||||
import org.thingsboard.server.service.ws.telemetry.cmd.v2.EntityDataCmd;
|
||||
@ -29,6 +30,8 @@ public interface TbEntityDataSubscriptionService {
|
||||
|
||||
void handleCmd(WebSocketSessionRef sessionId, AlarmDataCmd cmd);
|
||||
|
||||
void handleCmd(WebSocketSessionRef sessionId, AlarmCountCmd cmd);
|
||||
|
||||
void cancelSubscription(String sessionId, UnsubscribeCmd subscriptionId);
|
||||
|
||||
void cancelAllSessionSubscriptions(String sessionId);
|
||||
|
||||
@ -30,7 +30,6 @@ import org.thingsboard.server.common.data.SystemInfo;
|
||||
import org.thingsboard.server.common.data.SystemInfoData;
|
||||
import org.thingsboard.server.common.data.id.TenantId;
|
||||
import org.thingsboard.server.common.data.kv.BasicTsKvEntry;
|
||||
import org.thingsboard.server.common.data.kv.DoubleDataEntry;
|
||||
import org.thingsboard.server.common.data.kv.JsonDataEntry;
|
||||
import org.thingsboard.server.common.data.kv.LongDataEntry;
|
||||
import org.thingsboard.server.common.data.kv.TsKvEntry;
|
||||
@ -58,10 +57,9 @@ import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static org.thingsboard.common.util.SystemUtil.getCpuUsage;
|
||||
import static org.thingsboard.common.util.SystemUtil.getFreeDiscSpace;
|
||||
import static org.thingsboard.common.util.SystemUtil.getFreeMemory;
|
||||
import static org.thingsboard.common.util.SystemUtil.getMemoryUsage;
|
||||
import static org.thingsboard.common.util.SystemUtil.getTotalCpuUsage;
|
||||
import static org.thingsboard.common.util.SystemUtil.getDiscSpaceUsage;
|
||||
import static org.thingsboard.common.util.SystemUtil.getCpuCount;
|
||||
import static org.thingsboard.common.util.SystemUtil.getTotalDiscSpace;
|
||||
import static org.thingsboard.common.util.SystemUtil.getTotalMemory;
|
||||
|
||||
@ -112,13 +110,11 @@ public class DefaultSystemInfoService extends TbApplicationEventListener<Partiti
|
||||
public SystemInfo getSystemInfo() {
|
||||
SystemInfo systemInfo = new SystemInfo();
|
||||
|
||||
ServiceInfo serviceInfo = serviceInfoProvider.getServiceInfoWithCurrentSystemInfo();
|
||||
|
||||
if (discoveryService.isMonolith()) {
|
||||
systemInfo.setMonolith(true);
|
||||
systemInfo.setSystemData(Collections.singletonList(createSystemInfoData(serviceInfo)));
|
||||
systemInfo.setSystemData(Collections.singletonList(createSystemInfoData(serviceInfoProvider.generateNewServiceInfoWithCurrentSystemInfo())));
|
||||
} else {
|
||||
systemInfo.setSystemData(getSystemData(serviceInfo));
|
||||
systemInfo.setSystemData(getSystemData(serviceInfoProvider.getServiceInfo()));
|
||||
}
|
||||
|
||||
return systemInfo;
|
||||
@ -156,7 +152,7 @@ public class DefaultSystemInfoService extends TbApplicationEventListener<Partiti
|
||||
|
||||
private void saveCurrentClusterSystemInfo() {
|
||||
long ts = System.currentTimeMillis();
|
||||
List<SystemInfoData> clusterSystemData = getSystemData(serviceInfoProvider.getServiceInfoWithCurrentSystemInfo());
|
||||
List<SystemInfoData> clusterSystemData = getSystemData(serviceInfoProvider.getServiceInfo());
|
||||
BasicTsKvEntry clusterDataKv = new BasicTsKvEntry(ts, new JsonDataEntry("clusterSystemData", JacksonUtil.toString(clusterSystemData)));
|
||||
doSave(Collections.singletonList(clusterDataKv));
|
||||
}
|
||||
@ -165,12 +161,12 @@ public class DefaultSystemInfoService extends TbApplicationEventListener<Partiti
|
||||
long ts = System.currentTimeMillis();
|
||||
List<TsKvEntry> tsList = new ArrayList<>();
|
||||
|
||||
getMemoryUsage().ifPresent(v -> tsList.add(new BasicTsKvEntry(ts, new LongDataEntry("memoryUsage", v))));
|
||||
getCpuUsage().ifPresent(v -> tsList.add(new BasicTsKvEntry(ts, new LongDataEntry("cpuUsage", (long) v))));
|
||||
getMemoryUsage().ifPresent(v -> tsList.add(new BasicTsKvEntry(ts, new LongDataEntry("memoryUsage", (long) v))));
|
||||
getDiscSpaceUsage().ifPresent(v -> tsList.add(new BasicTsKvEntry(ts, new LongDataEntry("discUsage", (long) v))));
|
||||
|
||||
getCpuCount().ifPresent(v -> tsList.add(new BasicTsKvEntry(ts, new LongDataEntry("cpuCount", (long) v))));
|
||||
getTotalMemory().ifPresent(v -> tsList.add(new BasicTsKvEntry(ts, new LongDataEntry("totalMemory", v))));
|
||||
getFreeMemory().ifPresent(v -> tsList.add(new BasicTsKvEntry(ts, new LongDataEntry("freeMemory", v))));
|
||||
getCpuUsage().ifPresent(v -> tsList.add(new BasicTsKvEntry(ts, new DoubleDataEntry("cpuUsage", v))));
|
||||
getTotalCpuUsage().ifPresent(v -> tsList.add(new BasicTsKvEntry(ts, new DoubleDataEntry("totalCpuUsage", v))));
|
||||
getFreeDiscSpace().ifPresent(v -> tsList.add(new BasicTsKvEntry(ts, new LongDataEntry("freeDiscSpace", v))));
|
||||
getTotalDiscSpace().ifPresent(v -> tsList.add(new BasicTsKvEntry(ts, new LongDataEntry("totalDiscSpace", v))));
|
||||
|
||||
doSave(tsList);
|
||||
@ -196,13 +192,15 @@ public class DefaultSystemInfoService extends TbApplicationEventListener<Partiti
|
||||
SystemInfoData infoData = new SystemInfoData();
|
||||
infoData.setServiceId(serviceInfo.getServiceId());
|
||||
infoData.setServiceType(serviceTypes.size() > 1 ? "MONOLITH" : serviceTypes.get(0));
|
||||
infoData.setMemoryUsage(serviceInfo.getSystemInfo().getMemoryUsage());
|
||||
infoData.setTotalMemory(serviceInfo.getSystemInfo().getTotalMemory());
|
||||
infoData.setFreeMemory(serviceInfo.getSystemInfo().getFreeMemory());
|
||||
|
||||
infoData.setCpuUsage(serviceInfo.getSystemInfo().getCpuUsage());
|
||||
infoData.setTotalCpuUsage(serviceInfo.getSystemInfo().getTotalCpuUsage());
|
||||
infoData.setFreeDiscSpace(serviceInfo.getSystemInfo().getFreeDiscSpace());
|
||||
infoData.setMemoryUsage(serviceInfo.getSystemInfo().getMemoryUsage());
|
||||
infoData.setDiscUsage(serviceInfo.getSystemInfo().getDiskUsage());
|
||||
|
||||
infoData.setCpuCount(serviceInfo.getSystemInfo().getCpuCount());
|
||||
infoData.setTotalMemory(serviceInfo.getSystemInfo().getTotalMemory());
|
||||
infoData.setTotalDiscSpace(serviceInfo.getSystemInfo().getTotalDiscSpace());
|
||||
|
||||
return infoData;
|
||||
}
|
||||
|
||||
|
||||
@ -26,6 +26,7 @@ import org.springframework.stereotype.Service;
|
||||
import org.thingsboard.common.util.JacksonUtil;
|
||||
import org.thingsboard.server.common.data.ApiUsageRecordKey;
|
||||
import org.thingsboard.server.common.data.alarm.Alarm;
|
||||
import org.thingsboard.server.common.data.alarm.AlarmApiCallResult;
|
||||
import org.thingsboard.server.common.data.alarm.AlarmComment;
|
||||
import org.thingsboard.server.common.data.alarm.AlarmCommentType;
|
||||
import org.thingsboard.server.common.data.alarm.AlarmCreateOrUpdateActiveRequest;
|
||||
@ -45,15 +46,14 @@ import org.thingsboard.server.common.data.id.UserId;
|
||||
import org.thingsboard.server.common.data.page.PageData;
|
||||
import org.thingsboard.server.common.data.query.AlarmData;
|
||||
import org.thingsboard.server.common.data.query.AlarmDataQuery;
|
||||
import org.thingsboard.server.common.msg.notification.trigger.AlarmTrigger;
|
||||
import org.thingsboard.server.common.msg.queue.TbCallback;
|
||||
import org.thingsboard.server.common.stats.TbApiUsageReportClient;
|
||||
import org.thingsboard.server.dao.alarm.AlarmApiCallResult;
|
||||
import org.thingsboard.server.dao.alarm.AlarmOperationResult;
|
||||
import org.thingsboard.server.dao.alarm.AlarmService;
|
||||
import org.thingsboard.server.dao.notification.NotificationRuleProcessingService;
|
||||
import org.thingsboard.server.common.msg.notification.NotificationRuleProcessor;
|
||||
import org.thingsboard.server.service.apiusage.TbApiUsageStateService;
|
||||
import org.thingsboard.server.service.entitiy.alarm.TbAlarmCommentService;
|
||||
import org.thingsboard.server.dao.notification.trigger.AlarmTrigger;
|
||||
import org.thingsboard.server.service.subscription.TbSubscriptionUtils;
|
||||
|
||||
import java.util.Collection;
|
||||
@ -70,7 +70,7 @@ public class DefaultAlarmSubscriptionService extends AbstractSubscriptionService
|
||||
private final TbAlarmCommentService alarmCommentService;
|
||||
private final TbApiUsageReportClient apiUsageClient;
|
||||
private final TbApiUsageStateService apiUsageStateService;
|
||||
private final NotificationRuleProcessingService notificationRuleProcessingService;
|
||||
private final NotificationRuleProcessor notificationRuleProcessor;
|
||||
|
||||
@Override
|
||||
protected String getExecutorPrefix() {
|
||||
@ -235,7 +235,8 @@ public class DefaultAlarmSubscriptionService extends AbstractSubscriptionService
|
||||
return TbSubscriptionUtils.toAlarmUpdateProto(tenantId, entityId, alarm);
|
||||
});
|
||||
}
|
||||
notificationRuleProcessingService.process(tenantId, AlarmTrigger.builder()
|
||||
notificationRuleProcessor.process(AlarmTrigger.builder()
|
||||
.tenantId(tenantId)
|
||||
.alarmUpdate(result)
|
||||
.build());
|
||||
});
|
||||
@ -252,7 +253,8 @@ public class DefaultAlarmSubscriptionService extends AbstractSubscriptionService
|
||||
return TbSubscriptionUtils.toAlarmDeletedProto(tenantId, entityId, alarm);
|
||||
});
|
||||
}
|
||||
notificationRuleProcessingService.process(tenantId, AlarmTrigger.builder()
|
||||
notificationRuleProcessor.process(AlarmTrigger.builder()
|
||||
.tenantId(tenantId)
|
||||
.alarmUpdate(result)
|
||||
.build());
|
||||
});
|
||||
|
||||
@ -21,13 +21,13 @@ import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.info.BuildProperties;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
import org.thingsboard.common.util.ThingsBoardThreadFactory;
|
||||
import org.thingsboard.server.common.data.UpdateMessage;
|
||||
import org.thingsboard.server.common.data.id.TenantId;
|
||||
import org.thingsboard.server.common.data.notification.rule.trigger.NewPlatformVersionTrigger;
|
||||
import org.thingsboard.server.dao.notification.NotificationRuleProcessingService;
|
||||
import org.thingsboard.server.common.msg.notification.trigger.NewPlatformVersionTrigger;
|
||||
import org.thingsboard.server.common.msg.notification.NotificationRuleProcessor;
|
||||
import org.thingsboard.server.queue.util.TbCoreComponent;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
@ -57,8 +57,11 @@ public class DefaultUpdateService implements UpdateService {
|
||||
@Value("${updates.enabled}")
|
||||
private boolean updatesEnabled;
|
||||
|
||||
@Autowired(required = false)
|
||||
private BuildProperties buildProperties;
|
||||
|
||||
@Autowired
|
||||
private NotificationRuleProcessingService notificationRuleProcessingService;
|
||||
private NotificationRuleProcessor notificationRuleProcessor;
|
||||
|
||||
private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1, ThingsBoardThreadFactory.forName("tb-update-service"));
|
||||
|
||||
@ -73,14 +76,11 @@ public class DefaultUpdateService implements UpdateService {
|
||||
|
||||
@PostConstruct
|
||||
private void init() {
|
||||
updateMessage = new UpdateMessage("", false, "");
|
||||
version = buildProperties != null ? buildProperties.getVersion() : "unknown";
|
||||
updateMessage = new UpdateMessage(false, version, "", "", "", "");
|
||||
if (updatesEnabled) {
|
||||
try {
|
||||
platform = System.getProperty("platform", "unknown");
|
||||
version = getClass().getPackage().getImplementationVersion();
|
||||
if (version == null) {
|
||||
version = "unknown";
|
||||
}
|
||||
instanceId = parseInstanceId();
|
||||
checkUpdatesFuture = scheduler.scheduleAtFixedRate(checkUpdatesRunnable, 0, 1, TimeUnit.HOURS);
|
||||
} catch (Exception e) {
|
||||
@ -128,15 +128,10 @@ public class DefaultUpdateService implements UpdateService {
|
||||
request.put(PLATFORM_PARAM, platform);
|
||||
request.put(VERSION_PARAM, version);
|
||||
request.put(INSTANCE_ID_PARAM, instanceId.toString());
|
||||
JsonNode response = restClient.postForObject(UPDATE_SERVER_BASE_URL + "/api/thingsboard/updates", request, JsonNode.class);
|
||||
UpdateMessage prevUpdateMessage = updateMessage;
|
||||
updateMessage = new UpdateMessage(
|
||||
response.get("message").asText(),
|
||||
response.get("updateAvailable").asBoolean(),
|
||||
version
|
||||
);
|
||||
updateMessage = restClient.postForObject(UPDATE_SERVER_BASE_URL + "/api/v2/thingsboard/updates", request, UpdateMessage.class);
|
||||
if (updateMessage.isUpdateAvailable() && !updateMessage.equals(prevUpdateMessage)) {
|
||||
notificationRuleProcessingService.process(TenantId.SYS_TENANT_ID, NewPlatformVersionTrigger.builder()
|
||||
notificationRuleProcessor.process(NewPlatformVersionTrigger.builder()
|
||||
.message(updateMessage)
|
||||
.build());
|
||||
}
|
||||
|
||||
@ -72,6 +72,7 @@ import org.thingsboard.server.service.ws.telemetry.cmd.v1.GetHistoryCmd;
|
||||
import org.thingsboard.server.service.ws.telemetry.cmd.v1.SubscriptionCmd;
|
||||
import org.thingsboard.server.service.ws.telemetry.cmd.v1.TelemetryPluginCmd;
|
||||
import org.thingsboard.server.service.ws.telemetry.cmd.v1.TimeseriesSubscriptionCmd;
|
||||
import org.thingsboard.server.service.ws.telemetry.cmd.v2.AlarmCountCmd;
|
||||
import org.thingsboard.server.service.ws.telemetry.cmd.v2.AlarmDataCmd;
|
||||
import org.thingsboard.server.service.ws.telemetry.cmd.v2.CmdUpdate;
|
||||
import org.thingsboard.server.service.ws.telemetry.cmd.v2.EntityCountCmd;
|
||||
@ -166,10 +167,11 @@ public class DefaultWebSocketService implements WebSocketService {
|
||||
newCmdsHandler(TelemetryPluginCmdsWrapper::getEntityDataCmds, this::handleWsEntityDataCmd),
|
||||
newCmdsHandler(TelemetryPluginCmdsWrapper::getAlarmDataCmds, this::handleWsAlarmDataCmd),
|
||||
newCmdsHandler(TelemetryPluginCmdsWrapper::getEntityCountCmds, this::handleWsEntityCountCmd),
|
||||
newCmdsHandler(TelemetryPluginCmdsWrapper::getAlarmCountCmds, this::handleWsAlarmCountCmd),
|
||||
newCmdsHandler(TelemetryPluginCmdsWrapper::getEntityDataUnsubscribeCmds, this::handleWsDataUnsubscribeCmd),
|
||||
newCmdsHandler(TelemetryPluginCmdsWrapper::getAlarmDataUnsubscribeCmds, this::handleWsDataUnsubscribeCmd),
|
||||
newCmdsHandler(TelemetryPluginCmdsWrapper::getAlarmDataUnsubscribeCmds, this::handleWsDataUnsubscribeCmd),
|
||||
newCmdsHandler(TelemetryPluginCmdsWrapper::getEntityCountUnsubscribeCmds, this::handleWsDataUnsubscribeCmd)
|
||||
newCmdsHandler(TelemetryPluginCmdsWrapper::getEntityCountUnsubscribeCmds, this::handleWsDataUnsubscribeCmd),
|
||||
newCmdsHandler(TelemetryPluginCmdsWrapper::getAlarmCountUnsubscribeCmds, this::handleWsDataUnsubscribeCmd)
|
||||
);
|
||||
notificationCmdsHandlers = List.of(
|
||||
newCmdHandler(NotificationCmdsWrapper::getUnreadSubCmd, notificationCmdsHandler::handleUnreadNotificationsSubCmd),
|
||||
@ -302,6 +304,16 @@ public class DefaultWebSocketService implements WebSocketService {
|
||||
}
|
||||
}
|
||||
|
||||
private void handleWsAlarmCountCmd(WebSocketSessionRef sessionRef, AlarmCountCmd cmd) {
|
||||
String sessionId = sessionRef.getSessionId();
|
||||
log.debug("[{}] Processing: {}", sessionId, cmd);
|
||||
|
||||
if (validateSessionMetadata(sessionRef, cmd.getCmdId(), sessionId)
|
||||
&& validateSubscriptionCmd(sessionRef, cmd)) {
|
||||
entityDataSubService.handleCmd(sessionRef, cmd);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendWsMsg(String sessionId, TelemetrySubscriptionUpdate update) {
|
||||
sendWsMsg(sessionId, update.getSubscriptionId(), update);
|
||||
@ -866,6 +878,16 @@ public class DefaultWebSocketService implements WebSocketService {
|
||||
}
|
||||
}
|
||||
|
||||
private boolean validateSubscriptionCmd(WebSocketSessionRef sessionRef, AlarmCountCmd cmd) {
|
||||
if (cmd.getCmdId() < 0) {
|
||||
TelemetrySubscriptionUpdate update = new TelemetrySubscriptionUpdate(cmd.getCmdId(), SubscriptionErrorCode.BAD_REQUEST,
|
||||
"Cmd id is negative value!");
|
||||
sendWsMsg(sessionRef, update);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void sendWsMsg(WebSocketSessionRef sessionRef, EntityDataUpdate update) {
|
||||
sendWsMsg(sessionRef, update.getCmdId(), update);
|
||||
}
|
||||
|
||||
@ -120,17 +120,21 @@ public class DefaultNotificationCommandsHandler implements NotificationCommandsH
|
||||
|
||||
/* Notifications subscription update handling */
|
||||
private void handleNotificationsSubscriptionUpdate(NotificationsSubscription subscription, NotificationsSubscriptionUpdate subscriptionUpdate) {
|
||||
if (subscriptionUpdate.getNotificationUpdate() != null) {
|
||||
handleNotificationUpdate(subscription, subscriptionUpdate.getNotificationUpdate());
|
||||
} else if (subscriptionUpdate.getNotificationRequestUpdate() != null) {
|
||||
handleNotificationRequestUpdate(subscription, subscriptionUpdate.getNotificationRequestUpdate());
|
||||
try {
|
||||
if (subscriptionUpdate.getNotificationUpdate() != null) {
|
||||
handleNotificationUpdate(subscription, subscriptionUpdate.getNotificationUpdate());
|
||||
} else if (subscriptionUpdate.getNotificationRequestUpdate() != null) {
|
||||
handleNotificationRequestUpdate(subscription, subscriptionUpdate.getNotificationRequestUpdate());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("[{}, subId: {}] Failed to handle update for notifications subscription: {}", subscription.getSessionId(), subscription.getSubscriptionId(), subscriptionUpdate, e);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleNotificationUpdate(NotificationsSubscription subscription, NotificationUpdate update) {
|
||||
log.trace("[{}, subId: {}] Handling notification update: {}", subscription.getSessionId(), subscription.getSubscriptionId(), update);
|
||||
Notification notification = update.getNotification();
|
||||
UUID notificationId = update.getNotificationId();
|
||||
UUID notificationId = notification != null ? notification.getUuidId() : update.getNotificationId();
|
||||
if (update.isCreated()) {
|
||||
subscription.getLatestUnreadNotifications().put(notificationId, notification);
|
||||
subscription.getTotalUnreadCounter().incrementAndGet();
|
||||
@ -175,10 +179,14 @@ public class DefaultNotificationCommandsHandler implements NotificationCommandsH
|
||||
|
||||
/* Notifications count subscription update handling */
|
||||
private void handleNotificationsCountSubscriptionUpdate(NotificationsCountSubscription subscription, NotificationsSubscriptionUpdate subscriptionUpdate) {
|
||||
if (subscriptionUpdate.getNotificationUpdate() != null) {
|
||||
handleNotificationUpdate(subscription, subscriptionUpdate.getNotificationUpdate());
|
||||
} else if (subscriptionUpdate.getNotificationRequestUpdate() != null) {
|
||||
handleNotificationRequestUpdate(subscription, subscriptionUpdate.getNotificationRequestUpdate());
|
||||
try {
|
||||
if (subscriptionUpdate.getNotificationUpdate() != null) {
|
||||
handleNotificationUpdate(subscription, subscriptionUpdate.getNotificationUpdate());
|
||||
} else if (subscriptionUpdate.getNotificationRequestUpdate() != null) {
|
||||
handleNotificationRequestUpdate(subscription, subscriptionUpdate.getNotificationRequestUpdate());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("[{}, subId: {}] Failed to handle update for notifications count subscription: {}", subscription.getSessionId(), subscription.getSubscriptionId(), subscriptionUpdate, e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -15,12 +15,10 @@
|
||||
*/
|
||||
package org.thingsboard.server.service.ws.notification.sub;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.thingsboard.server.common.data.id.NotificationId;
|
||||
import org.thingsboard.server.common.data.notification.Notification;
|
||||
import org.thingsboard.server.common.data.notification.NotificationStatus;
|
||||
|
||||
@ -32,7 +30,7 @@ import java.util.UUID;
|
||||
@Builder
|
||||
public class NotificationUpdate {
|
||||
|
||||
private NotificationId notificationId;
|
||||
private UUID notificationId;
|
||||
|
||||
private boolean created;
|
||||
private Notification notification;
|
||||
@ -43,10 +41,4 @@ public class NotificationUpdate {
|
||||
|
||||
private boolean deleted;
|
||||
|
||||
@JsonIgnore
|
||||
public UUID getNotificationId() {
|
||||
return notificationId != null ? notificationId.getId() :
|
||||
notification != null ? notification.getUuidId() : null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -19,6 +19,8 @@ import lombok.Data;
|
||||
import org.thingsboard.server.service.ws.telemetry.cmd.v1.AttributesSubscriptionCmd;
|
||||
import org.thingsboard.server.service.ws.telemetry.cmd.v1.GetHistoryCmd;
|
||||
import org.thingsboard.server.service.ws.telemetry.cmd.v1.TimeseriesSubscriptionCmd;
|
||||
import org.thingsboard.server.service.ws.telemetry.cmd.v2.AlarmCountCmd;
|
||||
import org.thingsboard.server.service.ws.telemetry.cmd.v2.AlarmCountUnsubscribeCmd;
|
||||
import org.thingsboard.server.service.ws.telemetry.cmd.v2.EntityCountUnsubscribeCmd;
|
||||
import org.thingsboard.server.service.ws.telemetry.cmd.v2.AlarmDataCmd;
|
||||
import org.thingsboard.server.service.ws.telemetry.cmd.v2.AlarmDataUnsubscribeCmd;
|
||||
@ -52,4 +54,8 @@ public class TelemetryPluginCmdsWrapper {
|
||||
|
||||
private List<EntityCountUnsubscribeCmd> entityCountUnsubscribeCmds;
|
||||
|
||||
private List<AlarmCountCmd> alarmCountCmds;
|
||||
|
||||
private List<AlarmCountUnsubscribeCmd> alarmCountUnsubscribeCmds;
|
||||
|
||||
}
|
||||
|
||||
@ -0,0 +1,34 @@
|
||||
/**
|
||||
* Copyright © 2016-2023 The Thingsboard Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.thingsboard.server.service.ws.telemetry.cmd.v2;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.Getter;
|
||||
import org.thingsboard.server.common.data.query.AlarmCountQuery;
|
||||
|
||||
public class AlarmCountCmd extends DataCmd {
|
||||
|
||||
@Getter
|
||||
private final AlarmCountQuery query;
|
||||
|
||||
@JsonCreator
|
||||
public AlarmCountCmd(@JsonProperty("cmdId") int cmdId,
|
||||
@JsonProperty("query") AlarmCountQuery query) {
|
||||
super(cmdId);
|
||||
this.query = query;
|
||||
}
|
||||
}
|
||||
@ -13,13 +13,13 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.thingsboard.server.common.data;
|
||||
package org.thingsboard.server.service.ws.telemetry.cmd.v2;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class ApiUsageStateMailMessage {
|
||||
private final ApiUsageRecordKey key;
|
||||
private final long threshold;
|
||||
private final long value;
|
||||
public class AlarmCountUnsubscribeCmd implements UnsubscribeCmd {
|
||||
|
||||
private final int cmdId;
|
||||
|
||||
}
|
||||
@ -0,0 +1,53 @@
|
||||
/**
|
||||
* Copyright © 2016-2023 The Thingsboard Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.thingsboard.server.service.ws.telemetry.cmd.v2;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.Getter;
|
||||
import lombok.ToString;
|
||||
import org.thingsboard.server.service.subscription.SubscriptionErrorCode;
|
||||
|
||||
@ToString
|
||||
public class AlarmCountUpdate extends CmdUpdate {
|
||||
|
||||
@Getter
|
||||
private int count;
|
||||
|
||||
public AlarmCountUpdate(int cmdId, int count) {
|
||||
super(cmdId, SubscriptionErrorCode.NO_ERROR.getCode(), null);
|
||||
this.count = count;
|
||||
}
|
||||
|
||||
public AlarmCountUpdate(int cmdId, int errorCode, String errorMsg) {
|
||||
super(cmdId, errorCode, errorMsg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CmdUpdateType getCmdUpdateType() {
|
||||
return CmdUpdateType.ALARM_COUNT_DATA;
|
||||
}
|
||||
|
||||
@JsonCreator
|
||||
public AlarmCountUpdate(@JsonProperty("cmdId") int cmdId,
|
||||
@JsonProperty("count") int count,
|
||||
@JsonProperty("errorCode") int errorCode,
|
||||
@JsonProperty("errorMsg") String errorMsg) {
|
||||
super(cmdId, errorCode, errorMsg);
|
||||
this.count = count;
|
||||
}
|
||||
|
||||
}
|
||||
@ -18,6 +18,7 @@ package org.thingsboard.server.service.ws.telemetry.cmd.v2;
|
||||
public enum CmdUpdateType {
|
||||
ENTITY_DATA,
|
||||
ALARM_DATA,
|
||||
ALARM_COUNT_DATA,
|
||||
COUNT_DATA,
|
||||
NOTIFICATIONS,
|
||||
NOTIFICATIONS_COUNT
|
||||
|
||||
@ -474,6 +474,12 @@ cache:
|
||||
userSettings:
|
||||
timeToLiveInMinutes: "${CACHE_SPECS_USER_SETTINGS_TTL:1440}"
|
||||
maxSize: "${CACHE_SPECS_USER_SETTINGS_MAX_SIZE:100000}"
|
||||
dashboardTitles:
|
||||
timeToLiveInMinutes: "${CACHE_SPECS_DASHBOARD_TITLES_TTL:1440}"
|
||||
maxSize: "${CACHE_SPECS_DASHBOARD_TITLES_MAX_SIZE:100000}"
|
||||
entityCount:
|
||||
timeToLiveInMinutes: "${CACHE_SPECS_ENTITY_COUNT_TTL:1440}"
|
||||
maxSize: "${CACHE_SPECS_ENTITY_COUNT_MAX_SIZE:100000}"
|
||||
|
||||
#Disable this because it is not required.
|
||||
spring.data.redis.repositories.enabled: false
|
||||
|
||||
@ -29,9 +29,12 @@ import org.thingsboard.server.common.data.Device;
|
||||
import org.thingsboard.server.common.data.EntityType;
|
||||
import org.thingsboard.server.common.data.Tenant;
|
||||
import org.thingsboard.server.common.data.User;
|
||||
import org.thingsboard.server.common.data.alarm.Alarm;
|
||||
import org.thingsboard.server.common.data.alarm.AlarmSeverity;
|
||||
import org.thingsboard.server.common.data.id.DeviceId;
|
||||
import org.thingsboard.server.common.data.id.EntityId;
|
||||
import org.thingsboard.server.common.data.page.PageData;
|
||||
import org.thingsboard.server.common.data.query.AlarmCountQuery;
|
||||
import org.thingsboard.server.common.data.query.DeviceTypeFilter;
|
||||
import org.thingsboard.server.common.data.query.DynamicValue;
|
||||
import org.thingsboard.server.common.data.query.DynamicValueSourceType;
|
||||
@ -52,6 +55,7 @@ import org.thingsboard.server.common.data.security.Authority;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
@ -141,6 +145,15 @@ public abstract class BaseEntityQueryControllerTest extends AbstractControllerTe
|
||||
|
||||
@Test
|
||||
public void testSysAdminCountEntitiesByQuery() throws Exception {
|
||||
loginSysAdmin();
|
||||
|
||||
EntityTypeFilter allDeviceFilter = new EntityTypeFilter();
|
||||
allDeviceFilter.setEntityType(EntityType.DEVICE);
|
||||
EntityCountQuery query = new EntityCountQuery(allDeviceFilter);
|
||||
Long initialCount = doPostWithResponse("/api/entitiesQuery/count", query, Long.class);
|
||||
|
||||
loginTenantAdmin();
|
||||
|
||||
List<Device> devices = new ArrayList<>();
|
||||
for (int i = 0; i < 97; i++) {
|
||||
Device device = new Device();
|
||||
@ -180,13 +193,139 @@ public abstract class BaseEntityQueryControllerTest extends AbstractControllerTe
|
||||
count = doPostWithResponse("/api/entitiesQuery/count", countQuery, Long.class);
|
||||
Assert.assertEquals(97, count.longValue());
|
||||
|
||||
EntityTypeFilter filter2 = new EntityTypeFilter();
|
||||
filter2.setEntityType(EntityType.DEVICE);
|
||||
Long count2 = doPostWithResponse("/api/entitiesQuery/count", query, Long.class);
|
||||
Assert.assertEquals(initialCount + 97, count2.longValue());
|
||||
}
|
||||
|
||||
EntityCountQuery countQuery2 = new EntityCountQuery(filter2);
|
||||
@Test
|
||||
public void testTenantCountAlarmsByQuery() throws Exception {
|
||||
loginTenantAdmin();
|
||||
List<Device> devices = new ArrayList<>();
|
||||
List<Alarm> alarms = new ArrayList<>();
|
||||
for (int i = 0; i < 97; i++) {
|
||||
Device device = new Device();
|
||||
device.setName("Device" + i);
|
||||
device.setType("default");
|
||||
device.setLabel("testLabel" + (int) (Math.random() * 1000));
|
||||
devices.add(doPost("/api/device", device, Device.class));
|
||||
Thread.sleep(1);
|
||||
}
|
||||
|
||||
Long count2 = doPostWithResponse("/api/entitiesQuery/count", countQuery2, Long.class);
|
||||
Assert.assertEquals(97, count2.longValue());
|
||||
for (int i = 0; i < devices.size(); i++) {
|
||||
Alarm alarm = new Alarm();
|
||||
alarm.setOriginator(devices.get(i).getId());
|
||||
alarm.setType("alarm" + i);
|
||||
alarm.setSeverity(AlarmSeverity.WARNING);
|
||||
alarms.add(doPost("/api/alarm", alarm, Alarm.class));
|
||||
Thread.sleep(1);
|
||||
}
|
||||
testCountAlarmsByQuery(alarms);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCustomerCountAlarmsByQuery() throws Exception {
|
||||
loginTenantAdmin();
|
||||
List<Device> devices = new ArrayList<>();
|
||||
List<Alarm> alarms = new ArrayList<>();
|
||||
for (int i = 0; i < 97; i++) {
|
||||
Device device = new Device();
|
||||
device.setCustomerId(customerId);
|
||||
device.setName("Device" + i);
|
||||
device.setType("default");
|
||||
device.setLabel("testLabel" + (int) (Math.random() * 1000));
|
||||
devices.add(doPost("/api/device", device, Device.class));
|
||||
Thread.sleep(1);
|
||||
}
|
||||
|
||||
loginCustomerUser();
|
||||
|
||||
for (int i = 0; i < devices.size(); i++) {
|
||||
Alarm alarm = new Alarm();
|
||||
alarm.setCustomerId(customerId);
|
||||
alarm.setOriginator(devices.get(i).getId());
|
||||
alarm.setType("alarm" + i);
|
||||
alarm.setSeverity(AlarmSeverity.WARNING);
|
||||
alarms.add(doPost("/api/alarm", alarm, Alarm.class));
|
||||
Thread.sleep(1);
|
||||
}
|
||||
testCountAlarmsByQuery(alarms);
|
||||
}
|
||||
|
||||
private void testCountAlarmsByQuery(List<Alarm> alarms) throws Exception {
|
||||
AlarmCountQuery countQuery = new AlarmCountQuery();
|
||||
|
||||
Long count = doPostWithResponse("/api/alarmsQuery/count", countQuery, Long.class);
|
||||
Assert.assertEquals(97, count.longValue());
|
||||
|
||||
countQuery = AlarmCountQuery.builder()
|
||||
.typeList(List.of("unknown"))
|
||||
.build();
|
||||
|
||||
count = doPostWithResponse("/api/alarmsQuery/count", countQuery, Long.class);
|
||||
Assert.assertEquals(0, count.longValue());
|
||||
|
||||
countQuery = AlarmCountQuery.builder()
|
||||
.typeList(List.of("alarm1", "alarm2", "alarm3"))
|
||||
.build();
|
||||
|
||||
count = doPostWithResponse("/api/alarmsQuery/count", countQuery, Long.class);
|
||||
Assert.assertEquals(3, count.longValue());
|
||||
|
||||
countQuery = AlarmCountQuery.builder()
|
||||
.typeList(alarms.stream().map(Alarm::getType).collect(Collectors.toList()))
|
||||
.build();
|
||||
|
||||
count = doPostWithResponse("/api/alarmsQuery/count", countQuery, Long.class);
|
||||
Assert.assertEquals(97, count.longValue());
|
||||
|
||||
countQuery = AlarmCountQuery.builder()
|
||||
.severityList(List.of(AlarmSeverity.CRITICAL))
|
||||
.build();
|
||||
|
||||
count = doPostWithResponse("/api/alarmsQuery/count", countQuery, Long.class);
|
||||
Assert.assertEquals(0, count.longValue());
|
||||
|
||||
countQuery = AlarmCountQuery.builder()
|
||||
.severityList(List.of(AlarmSeverity.WARNING))
|
||||
.build();
|
||||
|
||||
count = doPostWithResponse("/api/alarmsQuery/count", countQuery, Long.class);
|
||||
Assert.assertEquals(97, count.longValue());
|
||||
|
||||
long startTs = alarms.stream().map(Alarm::getCreatedTime).min(Long::compareTo).get();
|
||||
long endTs = alarms.stream().map(Alarm::getCreatedTime).max(Long::compareTo).get();
|
||||
|
||||
countQuery = AlarmCountQuery.builder()
|
||||
.startTs(startTs - 1)
|
||||
.endTs(endTs + 1)
|
||||
.build();
|
||||
|
||||
count = doPostWithResponse("/api/alarmsQuery/count", countQuery, Long.class);
|
||||
Assert.assertEquals(97, count.longValue());
|
||||
|
||||
countQuery = AlarmCountQuery.builder()
|
||||
.startTs(0)
|
||||
.endTs(endTs + 1)
|
||||
.build();
|
||||
|
||||
count = doPostWithResponse("/api/alarmsQuery/count", countQuery, Long.class);
|
||||
Assert.assertEquals(97, count.longValue());
|
||||
|
||||
countQuery = AlarmCountQuery.builder()
|
||||
.startTs(0)
|
||||
.endTs(System.currentTimeMillis())
|
||||
.build();
|
||||
|
||||
count = doPostWithResponse("/api/alarmsQuery/count", countQuery, Long.class);
|
||||
Assert.assertEquals(97, count.longValue());
|
||||
|
||||
countQuery = AlarmCountQuery.builder()
|
||||
.startTs(endTs + 1)
|
||||
.endTs(System.currentTimeMillis())
|
||||
.build();
|
||||
|
||||
count = doPostWithResponse("/api/alarmsQuery/count", countQuery, Long.class);
|
||||
Assert.assertEquals(0, count.longValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@ -26,11 +26,13 @@ import org.thingsboard.common.util.JacksonUtil;
|
||||
import org.thingsboard.server.common.data.AdminSettings;
|
||||
import org.thingsboard.server.common.data.ApiUsageState;
|
||||
import org.thingsboard.server.common.data.Customer;
|
||||
import org.thingsboard.server.common.data.Dashboard;
|
||||
import org.thingsboard.server.common.data.Device;
|
||||
import org.thingsboard.server.common.data.EntityType;
|
||||
import org.thingsboard.server.common.data.FeaturesInfo;
|
||||
import org.thingsboard.server.common.data.Tenant;
|
||||
import org.thingsboard.server.common.data.TenantProfile;
|
||||
import org.thingsboard.server.common.data.UsageInfo;
|
||||
import org.thingsboard.server.common.data.User;
|
||||
import org.thingsboard.server.common.data.asset.Asset;
|
||||
import org.thingsboard.server.common.data.id.TenantId;
|
||||
@ -49,7 +51,9 @@ import org.thingsboard.server.common.data.query.EntityData;
|
||||
import org.thingsboard.server.common.data.query.EntityTypeFilter;
|
||||
import org.thingsboard.server.common.data.query.TsValue;
|
||||
import org.thingsboard.server.common.data.security.Authority;
|
||||
import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration;
|
||||
import org.thingsboard.server.common.stats.TbApiUsageStateClient;
|
||||
import org.thingsboard.server.dao.tenant.TbTenantProfileCache;
|
||||
import org.thingsboard.server.service.ws.telemetry.cmd.v2.EntityCountCmd;
|
||||
import org.thingsboard.server.service.ws.telemetry.cmd.v2.EntityCountUpdate;
|
||||
import org.thingsboard.server.service.ws.telemetry.cmd.v2.EntityDataUpdate;
|
||||
@ -69,10 +73,13 @@ public abstract class BaseHomePageApiTest extends AbstractControllerTest {
|
||||
@Autowired
|
||||
private TbApiUsageStateClient apiUsageStateClient;
|
||||
|
||||
@Autowired
|
||||
private TbTenantProfileCache tenantProfileCache;
|
||||
|
||||
//For system administrator
|
||||
@Test
|
||||
public void testTenantsCountWsCmd() throws Exception {
|
||||
loginSysAdmin();
|
||||
Long initialCount = getInitialEntityCount(EntityType.TENANT);
|
||||
|
||||
List<Tenant> tenants = new ArrayList<>();
|
||||
for (int i = 0; i < 100; i++) {
|
||||
@ -87,7 +94,7 @@ public abstract class BaseHomePageApiTest extends AbstractControllerTest {
|
||||
getWsClient().send(cmd);
|
||||
EntityCountUpdate update = getWsClient().parseCountReply(getWsClient().waitForReply());
|
||||
Assert.assertEquals(1, update.getCmdId());
|
||||
Assert.assertEquals(101, update.getCount());
|
||||
Assert.assertEquals(initialCount + 100, update.getCount());
|
||||
|
||||
for (Tenant tenant : tenants) {
|
||||
doDelete("/api/tenant/" + tenant.getId().toString());
|
||||
@ -96,7 +103,7 @@ public abstract class BaseHomePageApiTest extends AbstractControllerTest {
|
||||
|
||||
@Test
|
||||
public void testTenantProfilesCountWsCmd() throws Exception {
|
||||
loginSysAdmin();
|
||||
Long initialCount = getInitialEntityCount(EntityType.TENANT_PROFILE);
|
||||
|
||||
List<TenantProfile> tenantProfiles = new ArrayList<>();
|
||||
for (int i = 0; i < 100; i++) {
|
||||
@ -111,7 +118,7 @@ public abstract class BaseHomePageApiTest extends AbstractControllerTest {
|
||||
getWsClient().send(cmd);
|
||||
EntityCountUpdate update = getWsClient().parseCountReply(getWsClient().waitForReply());
|
||||
Assert.assertEquals(1, update.getCmdId());
|
||||
Assert.assertEquals(101, update.getCount());
|
||||
Assert.assertEquals(initialCount + 100, update.getCount());
|
||||
|
||||
for (TenantProfile tenantProfile : tenantProfiles) {
|
||||
doDelete("/api/tenantProfile/" + tenantProfile.getId().toString());
|
||||
@ -120,7 +127,7 @@ public abstract class BaseHomePageApiTest extends AbstractControllerTest {
|
||||
|
||||
@Test
|
||||
public void testUsersCountWsCmd() throws Exception {
|
||||
loginSysAdmin();
|
||||
Long initialCount = getInitialEntityCount(EntityType.USER);
|
||||
|
||||
List<User> users = new ArrayList<>();
|
||||
for (int i = 0; i < 100; i++) {
|
||||
@ -137,7 +144,7 @@ public abstract class BaseHomePageApiTest extends AbstractControllerTest {
|
||||
getWsClient().send(cmd);
|
||||
EntityCountUpdate update = getWsClient().parseCountReply(getWsClient().waitForReply());
|
||||
Assert.assertEquals(1, update.getCmdId());
|
||||
Assert.assertEquals(103, update.getCount());
|
||||
Assert.assertEquals(initialCount + 100, update.getCount());
|
||||
|
||||
for (User user : users) {
|
||||
doDelete("/api/user/" + user.getId().toString());
|
||||
@ -146,6 +153,7 @@ public abstract class BaseHomePageApiTest extends AbstractControllerTest {
|
||||
|
||||
@Test
|
||||
public void testCustomersCountWsCmd() throws Exception {
|
||||
Long initialCount = getInitialEntityCount(EntityType.CUSTOMER);
|
||||
loginTenantAdmin();
|
||||
|
||||
List<Customer> customers = new ArrayList<>();
|
||||
@ -162,7 +170,7 @@ public abstract class BaseHomePageApiTest extends AbstractControllerTest {
|
||||
getWsClient().send(cmd);
|
||||
EntityCountUpdate update = getWsClient().parseCountReply(getWsClient().waitForReply());
|
||||
Assert.assertEquals(1, update.getCmdId());
|
||||
Assert.assertEquals(101, update.getCount());
|
||||
Assert.assertEquals(initialCount + 100, update.getCount());
|
||||
|
||||
loginTenantAdmin();
|
||||
for (Customer customer : customers) {
|
||||
@ -172,6 +180,7 @@ public abstract class BaseHomePageApiTest extends AbstractControllerTest {
|
||||
|
||||
@Test
|
||||
public void testDevicesCountWsCmd() throws Exception {
|
||||
Long initialCount = getInitialEntityCount(EntityType.DEVICE);
|
||||
loginTenantAdmin();
|
||||
|
||||
List<Device> devices = new ArrayList<>();
|
||||
@ -188,7 +197,7 @@ public abstract class BaseHomePageApiTest extends AbstractControllerTest {
|
||||
getWsClient().send(cmd);
|
||||
EntityCountUpdate update = getWsClient().parseCountReply(getWsClient().waitForReply());
|
||||
Assert.assertEquals(1, update.getCmdId());
|
||||
Assert.assertEquals(100, update.getCount());
|
||||
Assert.assertEquals(initialCount + 100, update.getCount());
|
||||
|
||||
loginTenantAdmin();
|
||||
for (Device device : devices) {
|
||||
@ -198,6 +207,7 @@ public abstract class BaseHomePageApiTest extends AbstractControllerTest {
|
||||
|
||||
@Test
|
||||
public void testAssetsCountWsCmd() throws Exception {
|
||||
Long initialCount = getInitialEntityCount(EntityType.ASSET);
|
||||
loginTenantAdmin();
|
||||
|
||||
List<Asset> assets = new ArrayList<>();
|
||||
@ -214,7 +224,7 @@ public abstract class BaseHomePageApiTest extends AbstractControllerTest {
|
||||
getWsClient().send(cmd);
|
||||
EntityCountUpdate update = getWsClient().parseCountReply(getWsClient().waitForReply());
|
||||
Assert.assertEquals(1, update.getCmdId());
|
||||
Assert.assertEquals(100, update.getCount());
|
||||
Assert.assertEquals(initialCount + 100, update.getCount());
|
||||
|
||||
loginTenantAdmin();
|
||||
for (Asset asset : assets) {
|
||||
@ -238,18 +248,19 @@ public abstract class BaseHomePageApiTest extends AbstractControllerTest {
|
||||
Assert.assertEquals(1, pageData.getData().size());
|
||||
Assert.assertEquals(apiUsageState.getId(), pageData.getData().get(0).getEntityId());
|
||||
|
||||
List<String> metrics = List.of("cpuUsage", "memoryUsage", "discUsage", "cpuCount", "totalMemory", "totalDiscSpace");
|
||||
update = getWsClient().subscribeTsUpdate(
|
||||
List.of("memoryUsage", "totalMemory", "freeMemory", "cpuUsage", "totalCpuUsage", "freeDiscSpace", "totalDiscSpace"),
|
||||
metrics,
|
||||
now, TimeUnit.HOURS.toMillis(1));
|
||||
Assert.assertEquals(1, update.getCmdId());
|
||||
List<EntityData> listData = update.getUpdate();
|
||||
Assert.assertNotNull(listData);
|
||||
Assert.assertEquals(1, listData.size());
|
||||
Assert.assertEquals(apiUsageState.getId(), listData.get(0).getEntityId());
|
||||
Assert.assertEquals(7, listData.get(0).getTimeseries().size());
|
||||
Assert.assertEquals(metrics.size(), listData.get(0).getTimeseries().size());
|
||||
|
||||
for (TsValue[] tsv : listData.get(0).getTimeseries().values()) {
|
||||
Assert.assertTrue(tsv.length > 1);
|
||||
Assert.assertTrue(tsv.length > 0);
|
||||
}
|
||||
}
|
||||
|
||||
@ -329,6 +340,108 @@ public abstract class BaseHomePageApiTest extends AbstractControllerTest {
|
||||
Assert.assertTrue(featuresInfo.isOauthEnabled());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUsageInfo() throws Exception {
|
||||
loginTenantAdmin();
|
||||
|
||||
TenantProfile tenantProfile = tenantProfileCache.get(tenantId);
|
||||
|
||||
Assert.assertNotNull(tenantProfile);
|
||||
|
||||
DefaultTenantProfileConfiguration configuration = (DefaultTenantProfileConfiguration) tenantProfile.getProfileData().getConfiguration();
|
||||
|
||||
UsageInfo usageInfo = doGet("/api/usage", UsageInfo.class);
|
||||
Assert.assertNotNull(usageInfo);
|
||||
Assert.assertEquals(0, usageInfo.getDevices());
|
||||
Assert.assertEquals(configuration.getMaxDevices(), usageInfo.getMaxDevices());
|
||||
|
||||
Assert.assertEquals(0, usageInfo.getAssets());
|
||||
Assert.assertEquals(configuration.getMaxAssets(), usageInfo.getMaxAssets());
|
||||
|
||||
Assert.assertEquals(1, usageInfo.getCustomers());
|
||||
Assert.assertEquals(configuration.getMaxCustomers(), usageInfo.getMaxCustomers());
|
||||
|
||||
Assert.assertEquals(2, usageInfo.getUsers());
|
||||
Assert.assertEquals(configuration.getMaxUsers(), usageInfo.getMaxUsers());
|
||||
|
||||
Assert.assertEquals(0, usageInfo.getDashboards());
|
||||
Assert.assertEquals(configuration.getMaxDashboards(), usageInfo.getMaxDashboards());
|
||||
|
||||
Assert.assertEquals(0, usageInfo.getTransportMessages());
|
||||
Assert.assertEquals(configuration.getMaxTransportMessages(), usageInfo.getMaxTransportMessages());
|
||||
|
||||
Assert.assertEquals(0, usageInfo.getJsExecutions());
|
||||
Assert.assertEquals(configuration.getMaxJSExecutions(), usageInfo.getMaxJsExecutions());
|
||||
|
||||
Assert.assertEquals(0, usageInfo.getEmails());
|
||||
Assert.assertEquals(configuration.getMaxEmails(), usageInfo.getMaxEmails());
|
||||
|
||||
Assert.assertEquals(0, usageInfo.getSms());
|
||||
Assert.assertEquals(configuration.getMaxSms(), usageInfo.getMaxSms());
|
||||
|
||||
Assert.assertEquals(0, usageInfo.getAlarms());
|
||||
Assert.assertEquals(configuration.getMaxCreatedAlarms(), usageInfo.getMaxAlarms());
|
||||
|
||||
List<Device> devices = new ArrayList<>();
|
||||
for (int i = 0; i < 97; i++) {
|
||||
Device device = new Device();
|
||||
device.setName("device" + i);
|
||||
devices.add(doPost("/api/device", device, Device.class));
|
||||
}
|
||||
|
||||
usageInfo = doGet("/api/usage", UsageInfo.class);
|
||||
Assert.assertEquals(devices.size(), usageInfo.getDevices());
|
||||
|
||||
List<Asset> assets = new ArrayList<>();
|
||||
for (int i = 0; i < 97; i++) {
|
||||
Asset asset = new Asset();
|
||||
asset.setName("asset" + i);
|
||||
assets.add(doPost("/api/asset", asset, Asset.class));
|
||||
}
|
||||
|
||||
usageInfo = doGet("/api/usage", UsageInfo.class);
|
||||
Assert.assertEquals(assets.size(), usageInfo.getAssets());
|
||||
|
||||
List<Customer> customers = new ArrayList<>();
|
||||
for (int i = 0; i < 97; i++) {
|
||||
Customer customer = new Customer();
|
||||
customer.setTitle("customer" + i);
|
||||
customers.add(doPost("/api/customer", customer, Customer.class));
|
||||
}
|
||||
|
||||
usageInfo = doGet("/api/usage", UsageInfo.class);
|
||||
Assert.assertEquals(customers.size() + 1, usageInfo.getCustomers());
|
||||
|
||||
List<User> users = new ArrayList<>();
|
||||
for (int i = 0; i < 97; i++) {
|
||||
User user = new User();
|
||||
user.setAuthority(Authority.TENANT_ADMIN);
|
||||
user.setEmail(i + "user@thingsboard.org");
|
||||
users.add(doPost("/api/user", user, User.class));
|
||||
}
|
||||
|
||||
usageInfo = doGet("/api/usage", UsageInfo.class);
|
||||
Assert.assertEquals(users.size() + 2, usageInfo.getUsers());
|
||||
|
||||
List<Dashboard> dashboards = new ArrayList<>();
|
||||
for (int i = 0; i < 97; i++) {
|
||||
Dashboard dashboard = new Dashboard();
|
||||
dashboard.setTitle("dashboard" + i);
|
||||
dashboards.add(doPost("/api/dashboard", dashboard, Dashboard.class));
|
||||
}
|
||||
|
||||
usageInfo = doGet("/api/usage", UsageInfo.class);
|
||||
Assert.assertEquals(dashboards.size(), usageInfo.getDashboards());
|
||||
}
|
||||
|
||||
private Long getInitialEntityCount(EntityType entityType) throws Exception {
|
||||
loginSysAdmin();
|
||||
|
||||
EntityTypeFilter allEntityFilter = new EntityTypeFilter();
|
||||
allEntityFilter.setEntityType(entityType);
|
||||
EntityCountQuery query = new EntityCountQuery(allEntityFilter);
|
||||
return doPostWithResponse("/api/entitiesQuery/count", query, Long.class);
|
||||
}
|
||||
|
||||
private OAuth2Info createDefaultOAuth2Info() {
|
||||
return new OAuth2Info(true, Lists.newArrayList(
|
||||
|
||||
@ -18,6 +18,7 @@ package org.thingsboard.server.controller;
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.apache.commons.lang3.RandomStringUtils;
|
||||
import org.junit.After;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Ignore;
|
||||
@ -31,6 +32,7 @@ import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.web.servlet.ResultActions;
|
||||
import org.thingsboard.server.common.data.Customer;
|
||||
import org.thingsboard.server.common.data.Dashboard;
|
||||
import org.thingsboard.server.common.data.StringUtils;
|
||||
import org.thingsboard.server.common.data.Tenant;
|
||||
import org.thingsboard.server.common.data.User;
|
||||
@ -42,6 +44,8 @@ import org.thingsboard.server.common.data.id.UserId;
|
||||
import org.thingsboard.server.common.data.page.PageData;
|
||||
import org.thingsboard.server.common.data.page.PageLink;
|
||||
import org.thingsboard.server.common.data.security.Authority;
|
||||
import org.thingsboard.server.common.data.settings.StarredDashboardInfo;
|
||||
import org.thingsboard.server.common.data.settings.UserDashboardsInfo;
|
||||
import org.thingsboard.server.dao.exception.DataValidationException;
|
||||
import org.thingsboard.server.dao.user.UserDao;
|
||||
|
||||
@ -356,7 +360,7 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
|
||||
String userIdStr = savedUser.getId().getId().toString();
|
||||
doGet("/api/user/" + userIdStr)
|
||||
.andExpect(status().isNotFound())
|
||||
.andExpect(statusReason(containsString( msgErrorNoFound("User",userIdStr))));
|
||||
.andExpect(statusReason(containsString(msgErrorNoFound("User", userIdStr))));
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -574,9 +578,9 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
|
||||
String email1 = "testEmail1";
|
||||
String email2 = "testEmail2";
|
||||
List<User> customerUsersEmail1 = new ArrayList<>();
|
||||
List<User> customerUsersEmail2= new ArrayList<>();
|
||||
List<User> customerUsersEmail2 = new ArrayList<>();
|
||||
for (int i = 0; i < 45; i++) {
|
||||
User customerUser = createCustomerUser( customerId);
|
||||
User customerUser = createCustomerUser(customerId);
|
||||
customerUser.setEmail(email1 + StringUtils.randomAlphanumeric((int) (5 + Math.random() * 10)) + "@thingsboard.org");
|
||||
customerUsersEmail1.add(doPost("/api/user", customerUser, User.class));
|
||||
|
||||
@ -685,7 +689,7 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
|
||||
|
||||
JsonNode retrievedSettings = doGet("/api/user/settings", JsonNode.class);
|
||||
Assert.assertEquals(retrievedSettings, userSettings);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testShouldNotSaveJsonWithRestrictedSymbols() throws Exception {
|
||||
@ -860,7 +864,7 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
|
||||
|
||||
List<UserEmailInfo> expectedUserInfos = customerUsersContainingWord.stream().map(customerUser -> new UserEmailInfo(customerUser.getId(),
|
||||
customerUser.getEmail(), customerUser.getFirstName() == null ? "" : customerUser.getFirstName(),
|
||||
customerUser.getLastName() == null ? "" : customerUser.getLastName()))
|
||||
customerUser.getLastName() == null ? "" : customerUser.getLastName()))
|
||||
.sorted(userDataIdComparator).collect(Collectors.toList());
|
||||
usersInfo.sort(userDataIdComparator);
|
||||
|
||||
@ -912,8 +916,8 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
|
||||
List<UserEmailInfo> usersInfo = getUsersInfo(pageLink);
|
||||
|
||||
List<UserEmailInfo> expectedUserInfos = usersContainingWord.stream().map(customerUser -> new UserEmailInfo(customerUser.getId(),
|
||||
customerUser.getEmail(), customerUser.getFirstName() == null ? "" : customerUser.getFirstName(),
|
||||
customerUser.getLastName() == null ? "" : customerUser.getLastName()))
|
||||
customerUser.getEmail(), customerUser.getFirstName() == null ? "" : customerUser.getFirstName(),
|
||||
customerUser.getLastName() == null ? "" : customerUser.getLastName()))
|
||||
.sorted(userDataIdComparator).collect(Collectors.toList());
|
||||
usersInfo.sort(userDataIdComparator);
|
||||
|
||||
@ -922,7 +926,7 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
|
||||
// find user by full last name
|
||||
pageLink = new PageLink(10, 0, searchText + "3");
|
||||
usersInfo = getUsersInfo(pageLink);
|
||||
Assert.assertEquals(2, usersInfo.size());
|
||||
Assert.assertEquals(2, usersInfo.size());
|
||||
|
||||
//clear users
|
||||
doDelete("/api/customer/" + customerId.getId().toString())
|
||||
@ -941,6 +945,7 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
|
||||
private static User createCustomerUser(CustomerId customerId) {
|
||||
return createCustomerUser(null, null, customerId);
|
||||
}
|
||||
|
||||
private static User createCustomerUser(String firstName, String lastName, CustomerId customerId) {
|
||||
String suffix = StringUtils.randomAlphanumeric((int) (5 + Math.random() * 10));
|
||||
return createCustomerUser(firstName, lastName, "testMail" + suffix + "@thingsboard.org", customerId);
|
||||
@ -959,6 +964,7 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
|
||||
private User createTenantAdminUser() {
|
||||
return createTenantAdminUser(null, null);
|
||||
}
|
||||
|
||||
private User createTenantAdminUser(String firstName, String lastName) {
|
||||
String suffix = StringUtils.randomAlphanumeric((int) (5 + Math.random() * 10));
|
||||
|
||||
@ -975,7 +981,8 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
|
||||
List<UserEmailInfo> loadedCustomerUsers = new ArrayList<>();
|
||||
PageData<UserEmailInfo> pageData = null;
|
||||
do {
|
||||
pageData = doGetTypedWithPageLink("/api/users/info?", new TypeReference<>() {}, pageLink);
|
||||
pageData = doGetTypedWithPageLink("/api/users/info?", new TypeReference<>() {
|
||||
}, pageLink);
|
||||
loadedCustomerUsers.addAll(pageData.getData());
|
||||
if (pageData.hasNext()) {
|
||||
pageLink = pageLink.nextPageLink();
|
||||
@ -984,4 +991,167 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
|
||||
return loadedCustomerUsers;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEmptyDashboardSettings() throws Exception {
|
||||
loginCustomerUser();
|
||||
|
||||
UserDashboardsInfo retrievedSettings = doGet("/api/user/dashboards", UserDashboardsInfo.class);
|
||||
Assert.assertNotNull(retrievedSettings);
|
||||
Assert.assertNotNull(retrievedSettings.getLast());
|
||||
Assert.assertTrue(retrievedSettings.getLast().isEmpty());
|
||||
Assert.assertNotNull(retrievedSettings.getStarred());
|
||||
Assert.assertTrue(retrievedSettings.getStarred().isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDashboardSettingsFlow() throws Exception {
|
||||
loginTenantAdmin();
|
||||
|
||||
Dashboard dashboard1 = new Dashboard();
|
||||
dashboard1.setTitle("My dashboard 1");
|
||||
Dashboard savedDashboard1 = doPost("/api/dashboard", dashboard1, Dashboard.class);
|
||||
Dashboard dashboard2 = new Dashboard();
|
||||
dashboard2.setTitle("My dashboard 2");
|
||||
Dashboard savedDashboard2 = doPost("/api/dashboard", dashboard2, Dashboard.class);
|
||||
|
||||
UserDashboardsInfo retrievedSettings = doGet("/api/user/dashboards", UserDashboardsInfo.class);
|
||||
Assert.assertNotNull(retrievedSettings);
|
||||
Assert.assertNotNull(retrievedSettings.getLast());
|
||||
Assert.assertTrue(retrievedSettings.getLast().isEmpty());
|
||||
Assert.assertNotNull(retrievedSettings.getStarred());
|
||||
Assert.assertTrue(retrievedSettings.getStarred().isEmpty());
|
||||
|
||||
UserDashboardsInfo newSettings = doGet("/api/user/dashboards/" + savedDashboard1.getId().getId() + "/visit", UserDashboardsInfo.class);
|
||||
Assert.assertNotNull(newSettings);
|
||||
Assert.assertNotNull(newSettings.getLast());
|
||||
Assert.assertEquals(1, newSettings.getLast().size());
|
||||
var lastVisited = newSettings.getLast().get(0);
|
||||
Assert.assertEquals(savedDashboard1.getId().getId(), lastVisited.getId());
|
||||
Assert.assertEquals(savedDashboard1.getTitle(), lastVisited.getTitle());
|
||||
Assert.assertNotNull(retrievedSettings.getStarred());
|
||||
Assert.assertTrue(retrievedSettings.getStarred().isEmpty());
|
||||
|
||||
newSettings = doGet("/api/user/dashboards/" + savedDashboard2.getId().getId() + "/visit", UserDashboardsInfo.class);
|
||||
Assert.assertNotNull(newSettings);
|
||||
Assert.assertNotNull(newSettings.getLast());
|
||||
Assert.assertEquals(2, newSettings.getLast().size());
|
||||
lastVisited = newSettings.getLast().get(0);
|
||||
Assert.assertEquals(savedDashboard2.getId().getId(), lastVisited.getId());
|
||||
Assert.assertEquals(savedDashboard2.getTitle(), lastVisited.getTitle());
|
||||
Assert.assertNotNull(retrievedSettings.getStarred());
|
||||
Assert.assertTrue(retrievedSettings.getStarred().isEmpty());
|
||||
|
||||
newSettings = doGet("/api/user/dashboards", UserDashboardsInfo.class);
|
||||
Assert.assertNotNull(newSettings);
|
||||
Assert.assertNotNull(newSettings.getLast());
|
||||
Assert.assertEquals(2, newSettings.getLast().size());
|
||||
lastVisited = newSettings.getLast().get(0);
|
||||
Assert.assertEquals(savedDashboard2.getId().getId(), lastVisited.getId());
|
||||
Assert.assertEquals(savedDashboard2.getTitle(), lastVisited.getTitle());
|
||||
Assert.assertNotNull(retrievedSettings.getStarred());
|
||||
Assert.assertTrue(retrievedSettings.getStarred().isEmpty());
|
||||
|
||||
newSettings = doGet("/api/user/dashboards/" + savedDashboard1.getId().getId() + "/star", UserDashboardsInfo.class);
|
||||
Assert.assertNotNull(newSettings);
|
||||
Assert.assertNotNull(newSettings.getLast());
|
||||
Assert.assertEquals(2, newSettings.getLast().size());
|
||||
lastVisited = newSettings.getLast().get(0);
|
||||
Assert.assertEquals(savedDashboard2.getId().getId(), lastVisited.getId());
|
||||
Assert.assertEquals(savedDashboard2.getTitle(), lastVisited.getTitle());
|
||||
Assert.assertFalse(lastVisited.isStarred());
|
||||
lastVisited = newSettings.getLast().get(1);
|
||||
Assert.assertEquals(savedDashboard1.getId().getId(), lastVisited.getId());
|
||||
Assert.assertEquals(savedDashboard1.getTitle(), lastVisited.getTitle());
|
||||
Assert.assertTrue(lastVisited.isStarred());
|
||||
Assert.assertNotNull(retrievedSettings.getStarred());
|
||||
Assert.assertEquals(1, newSettings.getStarred().size());
|
||||
StarredDashboardInfo starred = newSettings.getStarred().get(0);
|
||||
Assert.assertEquals(savedDashboard1.getId().getId(), starred.getId());
|
||||
Assert.assertEquals(savedDashboard1.getTitle(), starred.getTitle());
|
||||
|
||||
newSettings = doGet("/api/user/dashboards/" + savedDashboard2.getId().getId() + "/star", UserDashboardsInfo.class);
|
||||
Assert.assertNotNull(newSettings);
|
||||
Assert.assertNotNull(newSettings.getLast());
|
||||
Assert.assertEquals(2, newSettings.getLast().size());
|
||||
lastVisited = newSettings.getLast().get(0);
|
||||
Assert.assertEquals(savedDashboard2.getId().getId(), lastVisited.getId());
|
||||
Assert.assertEquals(savedDashboard2.getTitle(), lastVisited.getTitle());
|
||||
Assert.assertTrue(lastVisited.isStarred());
|
||||
lastVisited = newSettings.getLast().get(1);
|
||||
Assert.assertEquals(savedDashboard1.getId().getId(), lastVisited.getId());
|
||||
Assert.assertEquals(savedDashboard1.getTitle(), lastVisited.getTitle());
|
||||
Assert.assertTrue(lastVisited.isStarred());
|
||||
Assert.assertNotNull(retrievedSettings.getStarred());
|
||||
Assert.assertEquals(2, newSettings.getStarred().size());
|
||||
starred = newSettings.getStarred().get(0);
|
||||
Assert.assertEquals(savedDashboard2.getId().getId(), starred.getId());
|
||||
Assert.assertEquals(savedDashboard2.getTitle(), starred.getTitle());
|
||||
|
||||
newSettings = doGet("/api/user/dashboards/" + savedDashboard1.getId().getId() + "/unstar", UserDashboardsInfo.class);
|
||||
Assert.assertNotNull(newSettings);
|
||||
Assert.assertNotNull(newSettings.getLast());
|
||||
Assert.assertEquals(2, newSettings.getLast().size());
|
||||
lastVisited = newSettings.getLast().get(0);
|
||||
Assert.assertEquals(savedDashboard2.getId().getId(), lastVisited.getId());
|
||||
Assert.assertEquals(savedDashboard2.getTitle(), lastVisited.getTitle());
|
||||
Assert.assertTrue(lastVisited.isStarred());
|
||||
lastVisited = newSettings.getLast().get(1);
|
||||
Assert.assertEquals(savedDashboard1.getId().getId(), lastVisited.getId());
|
||||
Assert.assertEquals(savedDashboard1.getTitle(), lastVisited.getTitle());
|
||||
Assert.assertFalse(lastVisited.isStarred());
|
||||
Assert.assertNotNull(retrievedSettings.getStarred());
|
||||
Assert.assertEquals(1, newSettings.getStarred().size());
|
||||
starred = newSettings.getStarred().get(0);
|
||||
Assert.assertEquals(savedDashboard2.getId().getId(), starred.getId());
|
||||
Assert.assertEquals(savedDashboard2.getTitle(), starred.getTitle());
|
||||
|
||||
//TEST renaming in the cache.
|
||||
savedDashboard1.setTitle(RandomStringUtils.randomAlphanumeric(10));
|
||||
savedDashboard1 = doPost("/api/dashboard", savedDashboard1, Dashboard.class);
|
||||
savedDashboard2.setTitle(RandomStringUtils.randomAlphanumeric(10));
|
||||
savedDashboard2 = doPost("/api/dashboard", savedDashboard2, Dashboard.class);
|
||||
|
||||
newSettings = doGet("/api/user/dashboards/" + savedDashboard1.getId().getId() + "/unstar", UserDashboardsInfo.class);
|
||||
Assert.assertNotNull(newSettings);
|
||||
Assert.assertNotNull(newSettings.getLast());
|
||||
Assert.assertEquals(2, newSettings.getLast().size());
|
||||
lastVisited = newSettings.getLast().get(0);
|
||||
Assert.assertEquals(savedDashboard2.getId().getId(), lastVisited.getId());
|
||||
Assert.assertEquals(savedDashboard2.getTitle(), lastVisited.getTitle());
|
||||
Assert.assertTrue(lastVisited.isStarred());
|
||||
lastVisited = newSettings.getLast().get(1);
|
||||
Assert.assertEquals(savedDashboard1.getId().getId(), lastVisited.getId());
|
||||
Assert.assertEquals(savedDashboard1.getTitle(), lastVisited.getTitle());
|
||||
Assert.assertFalse(lastVisited.isStarred());
|
||||
Assert.assertNotNull(retrievedSettings.getStarred());
|
||||
Assert.assertEquals(1, newSettings.getStarred().size());
|
||||
starred = newSettings.getStarred().get(0);
|
||||
Assert.assertEquals(savedDashboard2.getId().getId(), starred.getId());
|
||||
Assert.assertEquals(savedDashboard2.getTitle(), starred.getTitle());
|
||||
|
||||
doDelete("/api/dashboard/" + savedDashboard1.getId().getId().toString()).andExpect(status().isOk());
|
||||
|
||||
newSettings = doGet("/api/user/dashboards", UserDashboardsInfo.class);
|
||||
Assert.assertNotNull(newSettings);
|
||||
Assert.assertNotNull(newSettings.getLast());
|
||||
Assert.assertEquals(1, newSettings.getLast().size());
|
||||
lastVisited = newSettings.getLast().get(0);
|
||||
Assert.assertEquals(savedDashboard2.getId().getId(), lastVisited.getId());
|
||||
Assert.assertEquals(savedDashboard2.getTitle(), lastVisited.getTitle());
|
||||
Assert.assertTrue(lastVisited.isStarred());
|
||||
Assert.assertEquals(1, newSettings.getStarred().size());
|
||||
starred = newSettings.getStarred().get(0);
|
||||
Assert.assertEquals(savedDashboard2.getId().getId(), starred.getId());
|
||||
Assert.assertEquals(savedDashboard2.getTitle(), starred.getTitle());
|
||||
|
||||
doDelete("/api/dashboard/" + savedDashboard2.getId().getId().toString()).andExpect(status().isOk());
|
||||
|
||||
retrievedSettings = doGet("/api/user/dashboards", UserDashboardsInfo.class);
|
||||
Assert.assertNotNull(retrievedSettings);
|
||||
Assert.assertNotNull(retrievedSettings.getLast());
|
||||
Assert.assertTrue(retrievedSettings.getLast().isEmpty());
|
||||
Assert.assertNotNull(retrievedSettings.getStarred());
|
||||
Assert.assertTrue(retrievedSettings.getStarred().isEmpty());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -26,6 +26,8 @@ import org.junit.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.thingsboard.common.util.JacksonUtil;
|
||||
import org.thingsboard.server.common.data.Device;
|
||||
import org.thingsboard.server.common.data.alarm.Alarm;
|
||||
import org.thingsboard.server.common.data.alarm.AlarmSeverity;
|
||||
import org.thingsboard.server.common.data.id.EntityId;
|
||||
import org.thingsboard.server.common.data.id.TenantId;
|
||||
import org.thingsboard.server.common.data.kv.AttributeKvEntry;
|
||||
@ -35,6 +37,7 @@ import org.thingsboard.server.common.data.kv.LongDataEntry;
|
||||
import org.thingsboard.server.common.data.kv.StringDataEntry;
|
||||
import org.thingsboard.server.common.data.kv.TsKvEntry;
|
||||
import org.thingsboard.server.common.data.page.PageData;
|
||||
import org.thingsboard.server.common.data.query.AlarmCountQuery;
|
||||
import org.thingsboard.server.common.data.query.DeviceTypeFilter;
|
||||
import org.thingsboard.server.common.data.query.EntityCountQuery;
|
||||
import org.thingsboard.server.common.data.query.EntityData;
|
||||
@ -51,6 +54,8 @@ import org.thingsboard.server.common.data.query.TsValue;
|
||||
import org.thingsboard.server.service.subscription.SubscriptionErrorCode;
|
||||
import org.thingsboard.server.service.subscription.TbAttributeSubscriptionScope;
|
||||
import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService;
|
||||
import org.thingsboard.server.service.ws.telemetry.cmd.v2.AlarmCountCmd;
|
||||
import org.thingsboard.server.service.ws.telemetry.cmd.v2.AlarmCountUpdate;
|
||||
import org.thingsboard.server.service.ws.telemetry.cmd.v2.EntityCountCmd;
|
||||
import org.thingsboard.server.service.ws.telemetry.cmd.v2.EntityCountUpdate;
|
||||
import org.thingsboard.server.service.ws.telemetry.cmd.v2.EntityDataUpdate;
|
||||
@ -237,6 +242,74 @@ public abstract class BaseWebsocketApiTest extends AbstractControllerTest {
|
||||
Assert.assertEquals(0, update4.getCount());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAlarmCountWsCmd() throws Exception {
|
||||
loginTenantAdmin();
|
||||
|
||||
AlarmCountCmd cmd1 = new AlarmCountCmd(1, new AlarmCountQuery());
|
||||
|
||||
getWsClient().send(cmd1);
|
||||
|
||||
AlarmCountUpdate update = getWsClient().parseAlarmCountReply(getWsClient().waitForReply());
|
||||
Assert.assertEquals(1, update.getCmdId());
|
||||
Assert.assertEquals(0, update.getCount());
|
||||
|
||||
Alarm alarm = new Alarm();
|
||||
alarm.setOriginator(tenantId);
|
||||
alarm.setType("TEST ALARM");
|
||||
alarm.setSeverity(AlarmSeverity.WARNING);
|
||||
|
||||
alarm = doPost("/api/alarm", alarm, Alarm.class);
|
||||
|
||||
AlarmCountCmd cmd2 = new AlarmCountCmd(2, new AlarmCountQuery());
|
||||
|
||||
getWsClient().send(cmd2);
|
||||
|
||||
update = getWsClient().parseAlarmCountReply(getWsClient().waitForReply());
|
||||
Assert.assertEquals(2, update.getCmdId());
|
||||
Assert.assertEquals(1, update.getCount());
|
||||
|
||||
AlarmCountCmd cmd3 = new AlarmCountCmd(3, AlarmCountQuery.builder().assigneeId(tenantAdminUserId).build());
|
||||
|
||||
getWsClient().send(cmd3);
|
||||
|
||||
update = getWsClient().parseAlarmCountReply(getWsClient().waitForReply());
|
||||
Assert.assertEquals(3, update.getCmdId());
|
||||
Assert.assertEquals(0, update.getCount());
|
||||
|
||||
alarm.setAssigneeId(tenantAdminUserId);
|
||||
alarm = doPost("/api/alarm", alarm, Alarm.class);
|
||||
|
||||
AlarmCountCmd cmd4 = new AlarmCountCmd(4, AlarmCountQuery.builder().assigneeId(tenantAdminUserId).build());
|
||||
|
||||
getWsClient().send(cmd4);
|
||||
|
||||
update = getWsClient().parseAlarmCountReply(getWsClient().waitForReply());
|
||||
Assert.assertEquals(4, update.getCmdId());
|
||||
Assert.assertEquals(1, update.getCount());
|
||||
|
||||
AlarmCountCmd cmd5 = new AlarmCountCmd(5,
|
||||
AlarmCountQuery.builder().severityList(Collections.singletonList(AlarmSeverity.CRITICAL)).build());
|
||||
|
||||
getWsClient().send(cmd5);
|
||||
|
||||
update = getWsClient().parseAlarmCountReply(getWsClient().waitForReply());
|
||||
Assert.assertEquals(5, update.getCmdId());
|
||||
Assert.assertEquals(0, update.getCount());
|
||||
|
||||
alarm.setSeverity(AlarmSeverity.CRITICAL);
|
||||
doPost("/api/alarm", alarm, Alarm.class);
|
||||
|
||||
AlarmCountCmd cmd6 = new AlarmCountCmd(6,
|
||||
AlarmCountQuery.builder().severityList(Collections.singletonList(AlarmSeverity.CRITICAL)).build());
|
||||
|
||||
getWsClient().send(cmd6);
|
||||
|
||||
update = getWsClient().parseAlarmCountReply(getWsClient().waitForReply());
|
||||
Assert.assertEquals(6, update.getCmdId());
|
||||
Assert.assertEquals(1, update.getCount());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEntityDataLatestWidgetFlow() throws Exception {
|
||||
List<EntityKey> keys = List.of(new EntityKey(EntityKeyType.TIME_SERIES, "temperature"));
|
||||
|
||||
@ -30,6 +30,8 @@ import org.thingsboard.server.common.data.query.EntityFilter;
|
||||
import org.thingsboard.server.common.data.query.EntityKey;
|
||||
import org.thingsboard.server.service.ws.telemetry.cmd.TelemetryPluginCmdsWrapper;
|
||||
import org.thingsboard.server.service.ws.telemetry.cmd.v1.AttributesSubscriptionCmd;
|
||||
import org.thingsboard.server.service.ws.telemetry.cmd.v2.AlarmCountCmd;
|
||||
import org.thingsboard.server.service.ws.telemetry.cmd.v2.AlarmCountUpdate;
|
||||
import org.thingsboard.server.service.ws.telemetry.cmd.v2.EntityCountCmd;
|
||||
import org.thingsboard.server.service.ws.telemetry.cmd.v2.EntityCountUpdate;
|
||||
import org.thingsboard.server.service.ws.telemetry.cmd.v2.EntityDataCmd;
|
||||
@ -115,6 +117,12 @@ public class TbTestWebSocketClient extends WebSocketClient {
|
||||
this.send(JacksonUtil.toString(wrapper));
|
||||
}
|
||||
|
||||
public void send(AlarmCountCmd cmd) throws NotYetConnectedException {
|
||||
TelemetryPluginCmdsWrapper wrapper = new TelemetryPluginCmdsWrapper();
|
||||
wrapper.setAlarmCountCmds(Collections.singletonList(cmd));
|
||||
this.send(JacksonUtil.toString(wrapper));
|
||||
}
|
||||
|
||||
public String waitForUpdate() {
|
||||
return waitForUpdate(false);
|
||||
}
|
||||
@ -179,6 +187,10 @@ public class TbTestWebSocketClient extends WebSocketClient {
|
||||
return JacksonUtil.fromString(msg, EntityCountUpdate.class);
|
||||
}
|
||||
|
||||
public AlarmCountUpdate parseAlarmCountReply(String msg) {
|
||||
return JacksonUtil.fromString(msg, AlarmCountUpdate.class);
|
||||
}
|
||||
|
||||
public EntityDataUpdate subscribeLatestUpdate(List<EntityKey> keys, EntityFilter entityFilter) {
|
||||
EntityDataQuery edq = new EntityDataQuery(entityFilter, new EntityDataPageLink(1, 0, null, null),
|
||||
Collections.emptyList(), Collections.emptyList(), Collections.emptyList());
|
||||
|
||||
@ -19,12 +19,14 @@ import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.google.protobuf.AbstractMessage;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.thingsboard.common.util.JacksonUtil;
|
||||
import org.thingsboard.server.common.data.Device;
|
||||
import org.thingsboard.server.common.data.EntityType;
|
||||
import org.thingsboard.server.common.data.alarm.Alarm;
|
||||
import org.thingsboard.server.common.data.alarm.AlarmInfo;
|
||||
import org.thingsboard.server.common.data.alarm.AlarmSeverity;
|
||||
import org.thingsboard.server.common.data.alarm.AlarmStatus;
|
||||
import org.thingsboard.server.common.data.id.AlarmId;
|
||||
import org.thingsboard.server.common.data.page.PageData;
|
||||
import org.thingsboard.server.common.data.page.PageLink;
|
||||
import org.thingsboard.server.gen.edge.v1.AlarmUpdateMsg;
|
||||
@ -33,6 +35,7 @@ import org.thingsboard.server.gen.edge.v1.UplinkMsg;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
@ -42,8 +45,11 @@ abstract public class BaseAlarmEdgeTest extends AbstractEdgeTest {
|
||||
public void testSendAlarmToCloud() throws Exception {
|
||||
Device device = saveDeviceOnCloudAndVerifyDeliveryToEdge();
|
||||
|
||||
UUID alarmUUID = UUID.randomUUID();
|
||||
UplinkMsg.Builder uplinkMsgBuilder = UplinkMsg.newBuilder();
|
||||
AlarmUpdateMsg.Builder alarmUpdateMgBuilder = AlarmUpdateMsg.newBuilder();
|
||||
alarmUpdateMgBuilder.setIdMSB(alarmUUID.getMostSignificantBits());
|
||||
alarmUpdateMgBuilder.setIdLSB(alarmUUID.getLeastSignificantBits());
|
||||
alarmUpdateMgBuilder.setName("alarm from edge");
|
||||
alarmUpdateMgBuilder.setStatus(AlarmStatus.ACTIVE_UNACK.name());
|
||||
alarmUpdateMgBuilder.setSeverity(AlarmSeverity.CRITICAL.name());
|
||||
@ -65,6 +71,7 @@ abstract public class BaseAlarmEdgeTest extends AbstractEdgeTest {
|
||||
Optional<AlarmInfo> foundAlarm = alarms.stream().filter(alarm -> alarm.getType().equals("alarm from edge")).findAny();
|
||||
Assert.assertTrue(foundAlarm.isPresent());
|
||||
AlarmInfo alarmInfo = foundAlarm.get();
|
||||
Assert.assertEquals(new AlarmId(alarmUUID), alarmInfo.getId());
|
||||
Assert.assertEquals(device.getId(), alarmInfo.getOriginator());
|
||||
Assert.assertEquals(AlarmStatus.ACTIVE_UNACK, alarmInfo.getStatus());
|
||||
Assert.assertEquals(AlarmSeverity.CRITICAL, alarmInfo.getSeverity());
|
||||
@ -85,12 +92,28 @@ abstract public class BaseAlarmEdgeTest extends AbstractEdgeTest {
|
||||
Assert.assertTrue(latestMessage instanceof AlarmUpdateMsg);
|
||||
AlarmUpdateMsg alarmUpdateMsg = (AlarmUpdateMsg) latestMessage;
|
||||
Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, alarmUpdateMsg.getMsgType());
|
||||
Assert.assertEquals(savedAlarm.getUuidId().getMostSignificantBits(), alarmUpdateMsg.getIdMSB());
|
||||
Assert.assertEquals(savedAlarm.getUuidId().getLeastSignificantBits(), alarmUpdateMsg.getIdLSB());
|
||||
Assert.assertEquals(savedAlarm.getType(), alarmUpdateMsg.getType());
|
||||
Assert.assertEquals(savedAlarm.getName(), alarmUpdateMsg.getName());
|
||||
Assert.assertEquals(device.getName(), alarmUpdateMsg.getOriginatorName());
|
||||
Assert.assertEquals(savedAlarm.getStatus().name(), alarmUpdateMsg.getStatus());
|
||||
Assert.assertEquals(savedAlarm.getSeverity().name(), alarmUpdateMsg.getSeverity());
|
||||
|
||||
// update alarm
|
||||
String updatedDetails = "{\"testKey\":\"testValue\"}";
|
||||
savedAlarm.setDetails(JacksonUtil.OBJECT_MAPPER.readTree(updatedDetails));
|
||||
edgeImitator.expectMessageAmount(1);
|
||||
savedAlarm = doPost("/api/alarm", savedAlarm, Alarm.class);
|
||||
Assert.assertTrue(edgeImitator.waitForMessages());
|
||||
latestMessage = edgeImitator.getLatestMessage();
|
||||
Assert.assertTrue(latestMessage instanceof AlarmUpdateMsg);
|
||||
alarmUpdateMsg = (AlarmUpdateMsg) latestMessage;
|
||||
Assert.assertEquals(UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE, alarmUpdateMsg.getMsgType());
|
||||
Assert.assertEquals(savedAlarm.getUuidId().getMostSignificantBits(), alarmUpdateMsg.getIdMSB());
|
||||
Assert.assertEquals(savedAlarm.getUuidId().getLeastSignificantBits(), alarmUpdateMsg.getIdLSB());
|
||||
Assert.assertEquals(updatedDetails, alarmUpdateMsg.getDetails());
|
||||
|
||||
// ack alarm
|
||||
edgeImitator.expectMessageAmount(1);
|
||||
doPost("/api/alarm/" + savedAlarm.getUuidId() + "/ack");
|
||||
|
||||
@ -15,11 +15,17 @@
|
||||
*/
|
||||
package org.thingsboard.server.edge;
|
||||
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
import com.google.protobuf.AbstractMessage;
|
||||
import org.awaitility.Awaitility;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.thingsboard.common.util.JacksonUtil;
|
||||
import org.thingsboard.server.common.data.DataConstants;
|
||||
import org.thingsboard.server.common.data.Device;
|
||||
import org.thingsboard.server.common.data.asset.Asset;
|
||||
import org.thingsboard.server.common.data.edge.EdgeEvent;
|
||||
import org.thingsboard.server.common.data.edge.EdgeEventActionType;
|
||||
import org.thingsboard.server.common.data.edge.EdgeEventType;
|
||||
@ -27,9 +33,11 @@ import org.thingsboard.server.common.data.id.EntityId;
|
||||
import org.thingsboard.server.gen.edge.v1.AttributeDeleteMsg;
|
||||
import org.thingsboard.server.gen.edge.v1.DeviceUpdateMsg;
|
||||
import org.thingsboard.server.gen.edge.v1.EntityDataProto;
|
||||
import org.thingsboard.server.gen.edge.v1.UplinkMsg;
|
||||
import org.thingsboard.server.gen.transport.TransportProtos;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
abstract public class BaseTelemetryEdgeTest extends AbstractEdgeTest {
|
||||
|
||||
@ -233,4 +241,50 @@ abstract public class BaseTelemetryEdgeTest extends AbstractEdgeTest {
|
||||
Assert.assertEquals("value1", keyValueProto.getStringV());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSendAttributesDeleteRequestToCloud_nonDeviceEntity() throws Exception {
|
||||
edgeImitator.expectMessageAmount(2);
|
||||
Asset savedAsset = saveAsset("Delete Attribute Test");
|
||||
doPost("/api/edge/" + edge.getUuidId() + "/asset/" + savedAsset.getUuidId(), Asset.class);
|
||||
Assert.assertTrue(edgeImitator.waitForMessages());
|
||||
|
||||
final String attributeKey = "key1";
|
||||
ObjectNode attributesData = JacksonUtil.OBJECT_MAPPER.createObjectNode();
|
||||
attributesData.put(attributeKey, "value1");
|
||||
doPost("/api/plugins/telemetry/ASSET/" + savedAsset.getId() + "/attributes/" + DataConstants.SERVER_SCOPE, attributesData);
|
||||
|
||||
// Wait before device attributes saved to database before deleting them
|
||||
Awaitility.await()
|
||||
.atMost(10, TimeUnit.SECONDS)
|
||||
.until(() -> {
|
||||
String urlTemplate = "/api/plugins/telemetry/ASSET/" + savedAsset.getId() + "/keys/attributes/" + DataConstants.SERVER_SCOPE;
|
||||
List<String> actualKeys = doGetAsyncTyped(urlTemplate, new TypeReference<>() {});
|
||||
return actualKeys != null && !actualKeys.isEmpty() && actualKeys.contains(attributeKey);
|
||||
});
|
||||
|
||||
EntityDataProto.Builder builder = EntityDataProto.newBuilder()
|
||||
.setEntityIdMSB(savedAsset.getUuidId().getMostSignificantBits())
|
||||
.setEntityIdLSB(savedAsset.getUuidId().getLeastSignificantBits())
|
||||
.setEntityType(savedAsset.getId().getEntityType().name());
|
||||
AttributeDeleteMsg.Builder attributeDeleteMsg = AttributeDeleteMsg.newBuilder();
|
||||
attributeDeleteMsg.setScope(DataConstants.SERVER_SCOPE);
|
||||
attributeDeleteMsg.addAllAttributeNames(List.of(attributeKey));
|
||||
attributeDeleteMsg.build();
|
||||
builder.setAttributeDeleteMsg(attributeDeleteMsg);
|
||||
UplinkMsg.Builder uplinkMsgBuilder = UplinkMsg.newBuilder();
|
||||
uplinkMsgBuilder.addEntityData(builder.build());
|
||||
|
||||
edgeImitator.expectResponsesAmount(1);
|
||||
edgeImitator.sendUplinkMsg(uplinkMsgBuilder.build());
|
||||
Assert.assertTrue(edgeImitator.waitForResponses());
|
||||
|
||||
Awaitility.await()
|
||||
.atMost(10, TimeUnit.SECONDS)
|
||||
.until(() -> {
|
||||
String urlTemplate = "/api/plugins/telemetry/ASSET/" + savedAsset.getId() + "/keys/attributes/" + DataConstants.SERVER_SCOPE;
|
||||
List<String> actualKeys = doGetAsyncTyped(urlTemplate, new TypeReference<>() {});
|
||||
return actualKeys != null && actualKeys.isEmpty();
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -15,7 +15,6 @@
|
||||
*/
|
||||
package org.thingsboard.server.service.entitiy.alarm;
|
||||
|
||||
import com.google.common.util.concurrent.Futures;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
@ -30,8 +29,7 @@ import org.thingsboard.server.common.data.alarm.Alarm;
|
||||
import org.thingsboard.server.common.data.alarm.AlarmInfo;
|
||||
import org.thingsboard.server.common.data.exception.ThingsboardException;
|
||||
import org.thingsboard.server.common.data.id.UserId;
|
||||
import org.thingsboard.server.dao.alarm.AlarmApiCallResult;
|
||||
import org.thingsboard.server.dao.alarm.AlarmCommentService;
|
||||
import org.thingsboard.server.common.data.alarm.AlarmApiCallResult;
|
||||
import org.thingsboard.server.dao.alarm.AlarmService;
|
||||
import org.thingsboard.server.dao.customer.CustomerService;
|
||||
import org.thingsboard.server.dao.edge.EdgeService;
|
||||
|
||||
@ -35,7 +35,6 @@ import org.thingsboard.server.common.data.notification.NotificationRequestConfig
|
||||
import org.thingsboard.server.common.data.notification.NotificationRequestInfo;
|
||||
import org.thingsboard.server.common.data.notification.NotificationRequestStats;
|
||||
import org.thingsboard.server.common.data.notification.NotificationType;
|
||||
import org.thingsboard.server.common.data.notification.info.UserOriginatedNotificationInfo;
|
||||
import org.thingsboard.server.common.data.notification.settings.NotificationSettings;
|
||||
import org.thingsboard.server.common.data.notification.targets.NotificationTarget;
|
||||
import org.thingsboard.server.common.data.notification.targets.platform.PlatformUsersNotificationTargetConfig;
|
||||
@ -44,8 +43,8 @@ import org.thingsboard.server.common.data.notification.template.DeliveryMethodNo
|
||||
import org.thingsboard.server.common.data.notification.template.EmailDeliveryMethodNotificationTemplate;
|
||||
import org.thingsboard.server.common.data.notification.template.NotificationTemplate;
|
||||
import org.thingsboard.server.common.data.notification.template.NotificationTemplateConfig;
|
||||
import org.thingsboard.server.common.data.notification.template.WebDeliveryMethodNotificationTemplate;
|
||||
import org.thingsboard.server.common.data.notification.template.SmsDeliveryMethodNotificationTemplate;
|
||||
import org.thingsboard.server.common.data.notification.template.WebDeliveryMethodNotificationTemplate;
|
||||
import org.thingsboard.server.common.data.page.PageData;
|
||||
import org.thingsboard.server.common.data.page.PageLink;
|
||||
import org.thingsboard.server.common.data.security.Authority;
|
||||
@ -110,12 +109,9 @@ public abstract class AbstractNotificationApiTest extends AbstractControllerTest
|
||||
protected NotificationRequest submitNotificationRequest(List<NotificationTargetId> targets, NotificationTemplateId notificationTemplateId, int delayInSec) {
|
||||
NotificationRequestConfig config = new NotificationRequestConfig();
|
||||
config.setSendingDelayInSec(delayInSec);
|
||||
UserOriginatedNotificationInfo notificationInfo = new UserOriginatedNotificationInfo();
|
||||
notificationInfo.setDescription("My description");
|
||||
NotificationRequest notificationRequest = NotificationRequest.builder()
|
||||
.targets(targets.stream().map(UUIDBased::getId).collect(Collectors.toList()))
|
||||
.templateId(notificationTemplateId)
|
||||
.info(notificationInfo)
|
||||
.additionalConfig(config)
|
||||
.build();
|
||||
return doPost("/api/notification/request", notificationRequest, NotificationRequest.class);
|
||||
|
||||
@ -15,7 +15,6 @@
|
||||
*/
|
||||
package org.thingsboard.server.service.notification;
|
||||
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.assertj.core.data.Offset;
|
||||
import org.java_websocket.client.WebSocketClient;
|
||||
@ -25,7 +24,6 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.thingsboard.rule.engine.api.NotificationCenter;
|
||||
import org.thingsboard.server.common.data.User;
|
||||
import org.thingsboard.server.common.data.id.NotificationTargetId;
|
||||
import org.thingsboard.server.common.data.id.UserId;
|
||||
import org.thingsboard.server.common.data.notification.Notification;
|
||||
import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod;
|
||||
import org.thingsboard.server.common.data.notification.NotificationRequest;
|
||||
@ -35,11 +33,9 @@ import org.thingsboard.server.common.data.notification.NotificationRequestPrevie
|
||||
import org.thingsboard.server.common.data.notification.NotificationRequestStats;
|
||||
import org.thingsboard.server.common.data.notification.NotificationRequestStatus;
|
||||
import org.thingsboard.server.common.data.notification.NotificationType;
|
||||
import org.thingsboard.server.common.data.notification.info.UserOriginatedNotificationInfo;
|
||||
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.NotificationTarget;
|
||||
import org.thingsboard.server.common.data.notification.targets.platform.AllUsersFilter;
|
||||
import org.thingsboard.server.common.data.notification.targets.platform.CustomerUsersFilter;
|
||||
import org.thingsboard.server.common.data.notification.targets.platform.PlatformUsersNotificationTargetConfig;
|
||||
import org.thingsboard.server.common.data.notification.targets.platform.UserListFilter;
|
||||
@ -49,11 +45,9 @@ import org.thingsboard.server.common.data.notification.template.DeliveryMethodNo
|
||||
import org.thingsboard.server.common.data.notification.template.EmailDeliveryMethodNotificationTemplate;
|
||||
import org.thingsboard.server.common.data.notification.template.NotificationTemplate;
|
||||
import org.thingsboard.server.common.data.notification.template.NotificationTemplateConfig;
|
||||
import org.thingsboard.server.common.data.notification.template.WebDeliveryMethodNotificationTemplate;
|
||||
import org.thingsboard.server.common.data.notification.template.SlackDeliveryMethodNotificationTemplate;
|
||||
import org.thingsboard.server.common.data.notification.template.SmsDeliveryMethodNotificationTemplate;
|
||||
import org.thingsboard.server.common.data.page.PageData;
|
||||
import org.thingsboard.server.common.data.page.PageLink;
|
||||
import org.thingsboard.server.common.data.notification.template.WebDeliveryMethodNotificationTemplate;
|
||||
import org.thingsboard.server.common.data.security.Authority;
|
||||
import org.thingsboard.server.dao.DaoUtil;
|
||||
import org.thingsboard.server.dao.notification.NotificationDao;
|
||||
@ -66,8 +60,6 @@ import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.InstanceOfAssertFactories.type;
|
||||
@ -340,8 +332,8 @@ public class NotificationApiTest extends AbstractNotificationApiTest {
|
||||
target1Config.setUsersFilter(userListFilter);
|
||||
target1.setConfiguration(target1Config);
|
||||
target1 = saveNotificationTarget(target1);
|
||||
List<UserId> recipients = new ArrayList<>();
|
||||
recipients.add(tenantAdminUserId);
|
||||
List<String> recipients = new ArrayList<>();
|
||||
recipients.add(TENANT_ADMIN_EMAIL);
|
||||
|
||||
createDifferentCustomer();
|
||||
loginTenantAdmin();
|
||||
@ -353,7 +345,7 @@ public class NotificationApiTest extends AbstractNotificationApiTest {
|
||||
customerUser.setCustomerId(differentCustomerId);
|
||||
customerUser.setEmail("other-customer-" + i + "@thingsboard.org");
|
||||
customerUser = createUser(customerUser, "12345678");
|
||||
recipients.add(customerUser.getId());
|
||||
recipients.add(customerUser.getEmail());
|
||||
}
|
||||
NotificationTarget target2 = new NotificationTarget();
|
||||
target2.setName("Other customer users");
|
||||
@ -377,7 +369,7 @@ public class NotificationApiTest extends AbstractNotificationApiTest {
|
||||
|
||||
WebDeliveryMethodNotificationTemplate webNotificationTemplate = new WebDeliveryMethodNotificationTemplate();
|
||||
webNotificationTemplate.setEnabled(true);
|
||||
webNotificationTemplate.setBody("Message for WEB: ${recipientEmail}");
|
||||
webNotificationTemplate.setBody("Message for WEB: ${recipientEmail} ${unknownParam}");
|
||||
webNotificationTemplate.setSubject("Subject for WEB: ${recipientEmail}");
|
||||
templates.put(NotificationDeliveryMethod.WEB, webNotificationTemplate);
|
||||
|
||||
@ -409,14 +401,14 @@ public class NotificationApiTest extends AbstractNotificationApiTest {
|
||||
assertThat(preview.getRecipientsCountByTarget().get(target1.getName())).isEqualTo(1);
|
||||
assertThat(preview.getRecipientsCountByTarget().get(target2.getName())).isEqualTo(customerUsersCount);
|
||||
assertThat(preview.getTotalRecipientsCount()).isEqualTo(1 + customerUsersCount);
|
||||
assertThat(preview.getRecipientsPreview()).extracting(User::getId).containsAll(recipients);
|
||||
assertThat(preview.getRecipientsPreview()).containsAll(recipients);
|
||||
|
||||
Map<NotificationDeliveryMethod, DeliveryMethodNotificationTemplate> processedTemplates = preview.getProcessedTemplates();
|
||||
assertThat(processedTemplates.get(NotificationDeliveryMethod.WEB)).asInstanceOf(type(WebDeliveryMethodNotificationTemplate.class))
|
||||
.satisfies(template -> {
|
||||
assertThat(template.getBody())
|
||||
.startsWith("Message for WEB")
|
||||
.endsWith(requestorEmail);
|
||||
.endsWith(requestorEmail + " ${unknownParam}");
|
||||
assertThat(template.getSubject())
|
||||
.startsWith("Subject for WEB")
|
||||
.endsWith(requestorEmail);
|
||||
@ -439,7 +431,7 @@ public class NotificationApiTest extends AbstractNotificationApiTest {
|
||||
assertThat(processedTemplates.get(NotificationDeliveryMethod.SLACK)).asInstanceOf(type(SlackDeliveryMethodNotificationTemplate.class))
|
||||
.satisfies(template -> {
|
||||
assertThat(template.getBody())
|
||||
.isEqualTo("Message for SLACK: "); // ${recipientEmail} should be removed
|
||||
.isEqualTo("Message for SLACK: ${recipientEmail}"); // ${recipientEmail} should not be processed
|
||||
});
|
||||
}
|
||||
|
||||
@ -475,51 +467,6 @@ public class NotificationApiTest extends AbstractNotificationApiTest {
|
||||
assertThat(stats.getSent().get(NotificationDeliveryMethod.WEB)).hasValue(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNotificationsForALotOfUsers() throws Exception {
|
||||
int usersCount = 5000;
|
||||
|
||||
List<User> users = new ArrayList<>();
|
||||
for (int i = 1; i <= usersCount; i++) {
|
||||
User user = new User();
|
||||
user.setTenantId(tenantId);
|
||||
user.setAuthority(Authority.TENANT_ADMIN);
|
||||
user.setEmail("test-user-" + i + "@thingsboard.org");
|
||||
user = doPost("/api/user", user, User.class);
|
||||
users.add(user);
|
||||
}
|
||||
|
||||
NotificationTarget notificationTarget = new NotificationTarget();
|
||||
notificationTarget.setTenantId(tenantId);
|
||||
notificationTarget.setName("All my users");
|
||||
PlatformUsersNotificationTargetConfig config = new PlatformUsersNotificationTargetConfig();
|
||||
AllUsersFilter filter = new AllUsersFilter();
|
||||
config.setUsersFilter(filter);
|
||||
notificationTarget.setConfiguration(config);
|
||||
notificationTarget = saveNotificationTarget(notificationTarget);
|
||||
NotificationTargetId notificationTargetId = notificationTarget.getId();
|
||||
|
||||
ListenableFuture<NotificationRequest> request = executor.submit(() -> {
|
||||
return submitNotificationRequest(notificationTargetId, "Hello, ${recipientEmail}", 0, NotificationDeliveryMethod.WEB);
|
||||
});
|
||||
await().atMost(10, TimeUnit.SECONDS).until(request::isDone);
|
||||
NotificationRequest notificationRequest = request.get();
|
||||
|
||||
await().atMost(5, TimeUnit.SECONDS)
|
||||
.pollInterval(200, TimeUnit.MILLISECONDS)
|
||||
.until(() -> {
|
||||
PageData<Notification> sentNotifications = notificationDao.findByRequestId(tenantId, notificationRequest.getId(), new PageLink(1));
|
||||
return sentNotifications.getTotalElements() >= usersCount;
|
||||
});
|
||||
|
||||
PageData<Notification> sentNotifications = notificationDao.findByRequestId(tenantId, notificationRequest.getId(), new PageLink(Integer.MAX_VALUE));
|
||||
assertThat(sentNotifications.getData()).extracting(Notification::getRecipientId)
|
||||
.containsAll(users.stream().map(User::getId).collect(Collectors.toSet()));
|
||||
|
||||
NotificationRequestStats stats = getStats(notificationRequest.getId());
|
||||
assertThat(stats.getSent().values().stream().mapToInt(AtomicInteger::get).sum()).isGreaterThanOrEqualTo(usersCount);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSlackNotifications() throws Exception {
|
||||
NotificationSettings settings = new NotificationSettings();
|
||||
@ -537,7 +484,7 @@ public class NotificationApiTest extends AbstractNotificationApiTest {
|
||||
NotificationTemplateConfig config = new NotificationTemplateConfig();
|
||||
SlackDeliveryMethodNotificationTemplate slackNotificationTemplate = new SlackDeliveryMethodNotificationTemplate();
|
||||
slackNotificationTemplate.setEnabled(true);
|
||||
slackNotificationTemplate.setBody("To Slack :) ${recipientEmail}");
|
||||
slackNotificationTemplate.setBody("To Slack :)");
|
||||
config.setDeliveryMethodsTemplates(Map.of(
|
||||
NotificationDeliveryMethod.SLACK, slackNotificationTemplate
|
||||
));
|
||||
@ -550,14 +497,17 @@ public class NotificationApiTest extends AbstractNotificationApiTest {
|
||||
notificationTarget.setTenantId(tenantId);
|
||||
notificationTarget.setName(conversationName + " in Slack");
|
||||
SlackNotificationTargetConfig targetConfig = new SlackNotificationTargetConfig();
|
||||
targetConfig.setConversation(new SlackConversation(conversationId, conversationName));
|
||||
targetConfig.setConversation(SlackConversation.builder()
|
||||
.id(conversationId)
|
||||
.title(conversationName)
|
||||
.build());
|
||||
notificationTarget.setConfiguration(targetConfig);
|
||||
notificationTarget = saveNotificationTarget(notificationTarget);
|
||||
|
||||
NotificationRequest successfulNotificationRequest = submitNotificationRequest(List.of(notificationTarget.getId()), notificationTemplate.getId(), 0);
|
||||
await().atMost(2, TimeUnit.SECONDS)
|
||||
.until(() -> findNotificationRequest(successfulNotificationRequest.getId()).isSent());
|
||||
verify(slackService).sendMessage(eq(tenantId), eq(slackToken), eq(conversationId), eq("To Slack :) "));
|
||||
verify(slackService).sendMessage(eq(tenantId), eq(slackToken), eq(conversationId), eq(slackNotificationTemplate.getBody()));
|
||||
NotificationRequestStats stats = getStats(successfulNotificationRequest.getId());
|
||||
assertThat(stats.getSent().get(NotificationDeliveryMethod.SLACK)).hasValue(1);
|
||||
|
||||
|
||||
@ -123,7 +123,7 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest {
|
||||
@Test
|
||||
public void testNotificationRuleProcessing_entityActionTrigger() throws Exception {
|
||||
String notificationSubject = "${actionType}: ${entityType} [${entityId}]";
|
||||
String notificationText = "User: ${originatorUserName}";
|
||||
String notificationText = "User: ${userEmail}";
|
||||
NotificationTemplate notificationTemplate = createNotificationTemplate(NotificationType.GENERAL, notificationSubject, notificationText, NotificationDeliveryMethod.WEB);
|
||||
|
||||
NotificationRule notificationRule = new NotificationRule();
|
||||
@ -132,7 +132,7 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest {
|
||||
notificationRule.setTriggerType(NotificationRuleTriggerType.ENTITY_ACTION);
|
||||
|
||||
EntityActionNotificationRuleTriggerConfig triggerConfig = new EntityActionNotificationRuleTriggerConfig();
|
||||
triggerConfig.setEntityType(EntityType.DEVICE);
|
||||
triggerConfig.setEntityTypes(Set.of(EntityType.DEVICE));
|
||||
triggerConfig.setCreated(true);
|
||||
triggerConfig.setUpdated(true);
|
||||
triggerConfig.setDeleted(true);
|
||||
|
||||
@ -33,10 +33,10 @@ import org.thingsboard.server.common.msg.edge.FromEdgeSyncResponse;
|
||||
import org.thingsboard.server.common.msg.edge.ToEdgeSyncRequest;
|
||||
import org.thingsboard.server.common.msg.queue.TopicPartitionInfo;
|
||||
import org.thingsboard.server.common.msg.rpc.FromDeviceRpcResponse;
|
||||
import org.thingsboard.server.gen.transport.TransportProtos.ToVersionControlServiceMsg;
|
||||
import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg;
|
||||
import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg;
|
||||
import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg;
|
||||
import org.thingsboard.server.gen.transport.TransportProtos.ToVersionControlServiceMsg;
|
||||
import org.thingsboard.server.queue.TbQueueCallback;
|
||||
import org.thingsboard.server.queue.TbQueueClusterService;
|
||||
|
||||
@ -95,4 +95,5 @@ public interface TbClusterService extends TbQueueClusterService {
|
||||
void pushEdgeSyncResponseToCore(FromEdgeSyncResponse fromEdgeSyncResponse);
|
||||
|
||||
void sendNotificationMsgToEdge(TenantId tenantId, EdgeId edgeId, EntityId entityId, String body, EdgeEventType type, EdgeEventActionType action);
|
||||
|
||||
}
|
||||
|
||||
@ -31,13 +31,12 @@ message ServiceInfo {
|
||||
}
|
||||
|
||||
message SystemInfoProto {
|
||||
double cpuUsage = 1;
|
||||
double totalCpuUsage = 2;
|
||||
int64 cpuUsage = 1;
|
||||
int64 cpuCount = 2;
|
||||
int64 memoryUsage = 3;
|
||||
int64 totalMemory = 4;
|
||||
int64 freeMemory = 5;
|
||||
int64 freeDiscSpace = 6;
|
||||
int64 totalDiscSpace = 7;
|
||||
int64 diskUsage = 5;
|
||||
int64 totalDiscSpace = 6;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -974,6 +973,7 @@ message ToCoreMsg {
|
||||
EdgeNotificationMsgProto edgeNotificationMsg = 5;
|
||||
DeviceActivityProto deviceActivityMsg = 6;
|
||||
NotificationSchedulerServiceMsg notificationSchedulerServiceMsg = 7;
|
||||
NotificationRuleProcessorMsg notificationRuleProcessorMsg = 8;
|
||||
}
|
||||
|
||||
/* High priority messages with low latency are handled by ThingsBoard Core Service separately */
|
||||
@ -1059,3 +1059,7 @@ message NotificationSchedulerServiceMsg {
|
||||
int64 requestIdLSB = 4;
|
||||
int64 ts = 5;
|
||||
}
|
||||
|
||||
message NotificationRuleProcessorMsg {
|
||||
bytes trigger = 1;
|
||||
}
|
||||
|
||||
@ -19,7 +19,7 @@ import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import org.thingsboard.server.common.data.alarm.Alarm;
|
||||
import org.thingsboard.server.common.data.alarm.AlarmAssigneeUpdate;
|
||||
import org.thingsboard.server.common.data.alarm.AlarmApiCallResult;
|
||||
import org.thingsboard.server.common.data.alarm.AlarmSeverity;
|
||||
import org.thingsboard.server.common.data.id.EntityId;
|
||||
|
||||
|
||||
@ -18,19 +18,21 @@ package org.thingsboard.server.dao.alarm;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
import org.thingsboard.server.common.data.alarm.Alarm;
|
||||
import org.thingsboard.server.common.data.alarm.AlarmApiCallResult;
|
||||
import org.thingsboard.server.common.data.alarm.AlarmCreateOrUpdateActiveRequest;
|
||||
import org.thingsboard.server.common.data.alarm.AlarmInfo;
|
||||
import org.thingsboard.server.common.data.alarm.AlarmQuery;
|
||||
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.alarm.AlarmUpdateRequest;
|
||||
import org.thingsboard.server.common.data.alarm.AlarmCreateOrUpdateActiveRequest;
|
||||
import org.thingsboard.server.common.data.id.AlarmId;
|
||||
import org.thingsboard.server.common.data.id.CustomerId;
|
||||
import org.thingsboard.server.common.data.id.EntityId;
|
||||
import org.thingsboard.server.common.data.id.TenantId;
|
||||
import org.thingsboard.server.common.data.id.UserId;
|
||||
import org.thingsboard.server.common.data.page.PageData;
|
||||
import org.thingsboard.server.common.data.query.AlarmCountQuery;
|
||||
import org.thingsboard.server.common.data.query.AlarmData;
|
||||
import org.thingsboard.server.common.data.query.AlarmDataQuery;
|
||||
import org.thingsboard.server.dao.entity.EntityDaoService;
|
||||
@ -112,4 +114,6 @@ public interface AlarmService extends EntityDaoService {
|
||||
AlarmDataQuery query, Collection<EntityId> orderedEntityIds);
|
||||
|
||||
void deleteEntityAlarmRelations(TenantId tenantId, EntityId entityId);
|
||||
|
||||
long countAlarmsByQuery(TenantId tenantId, CustomerId customerId, AlarmCountQuery query);
|
||||
}
|
||||
|
||||
@ -36,6 +36,8 @@ public interface DashboardService extends EntityDaoService {
|
||||
|
||||
DashboardInfo findDashboardInfoById(TenantId tenantId, DashboardId dashboardId);
|
||||
|
||||
String findDashboardTitleById(TenantId tenantId, DashboardId dashboardId);
|
||||
|
||||
ListenableFuture<DashboardInfo> findDashboardInfoByIdAsync(TenantId tenantId, DashboardId dashboardId);
|
||||
|
||||
Dashboard saveDashboard(Dashboard dashboard);
|
||||
|
||||
@ -17,11 +17,11 @@ package org.thingsboard.server.dao.device;
|
||||
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
import org.thingsboard.server.common.data.Device;
|
||||
import org.thingsboard.server.common.data.DeviceIdInfo;
|
||||
import org.thingsboard.server.common.data.DeviceInfo;
|
||||
import org.thingsboard.server.common.data.DeviceProfile;
|
||||
import org.thingsboard.server.common.data.DeviceTransportType;
|
||||
import org.thingsboard.server.common.data.EntitySubtype;
|
||||
import org.thingsboard.server.common.data.DeviceIdInfo;
|
||||
import org.thingsboard.server.common.data.device.DeviceSearchQuery;
|
||||
import org.thingsboard.server.common.data.id.CustomerId;
|
||||
import org.thingsboard.server.common.data.id.DeviceId;
|
||||
@ -118,5 +118,4 @@ public interface DeviceService extends EntityDaoService {
|
||||
|
||||
PageData<Device> findDevicesByTenantIdAndEdgeIdAndType(TenantId tenantId, EdgeId edgeId, String type, PageLink pageLink);
|
||||
|
||||
long countByTenantId(TenantId tenantId);
|
||||
}
|
||||
|
||||
@ -0,0 +1,26 @@
|
||||
/**
|
||||
* Copyright © 2016-2023 The Thingsboard Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.thingsboard.server.dao.entity;
|
||||
|
||||
import org.thingsboard.server.common.data.EntityType;
|
||||
import org.thingsboard.server.common.data.id.TenantId;
|
||||
|
||||
public interface EntityCountService {
|
||||
|
||||
long countByTenantIdAndEntityType(TenantId tenantId, EntityType entityType);
|
||||
|
||||
void publishCountEntityEvictEvent(TenantId tenantId, EntityType entityType);
|
||||
}
|
||||
@ -26,6 +26,10 @@ public interface EntityDaoService {
|
||||
|
||||
Optional<HasId<?>> findEntity(TenantId tenantId, EntityId entityId);
|
||||
|
||||
default long countByTenantId(TenantId tenantId) {
|
||||
throw new IllegalArgumentException("Not implemented for " + getEntityType());
|
||||
}
|
||||
|
||||
EntityType getEntityType();
|
||||
|
||||
}
|
||||
|
||||
@ -20,8 +20,10 @@ import org.thingsboard.server.common.data.id.CustomerId;
|
||||
import org.thingsboard.server.common.data.id.NotificationTargetId;
|
||||
import org.thingsboard.server.common.data.id.TenantId;
|
||||
import org.thingsboard.server.common.data.notification.NotificationType;
|
||||
import org.thingsboard.server.common.data.notification.info.RuleOriginatedNotificationInfo;
|
||||
import org.thingsboard.server.common.data.notification.targets.NotificationTarget;
|
||||
import org.thingsboard.server.common.data.notification.targets.NotificationTargetConfig;
|
||||
import org.thingsboard.server.common.data.notification.targets.platform.PlatformUsersNotificationTargetConfig;
|
||||
import org.thingsboard.server.common.data.page.PageData;
|
||||
import org.thingsboard.server.common.data.page.PageLink;
|
||||
|
||||
@ -41,9 +43,9 @@ public interface NotificationTargetService {
|
||||
|
||||
PageData<User> findRecipientsForNotificationTarget(TenantId tenantId, CustomerId customerId, NotificationTargetId targetId, PageLink pageLink);
|
||||
|
||||
int countRecipientsForNotificationTargetConfig(TenantId tenantId, NotificationTargetConfig targetConfig);
|
||||
PageData<User> findRecipientsForNotificationTargetConfig(TenantId tenantId, PlatformUsersNotificationTargetConfig targetConfig, PageLink pageLink);
|
||||
|
||||
PageData<User> findRecipientsForNotificationTargetConfig(TenantId tenantId, CustomerId customerId, NotificationTargetConfig targetConfig, PageLink pageLink);
|
||||
PageData<User> findRecipientsForRuleNotificationTargetConfig(TenantId tenantId, PlatformUsersNotificationTargetConfig targetConfig, RuleOriginatedNotificationInfo info, PageLink pageLink);
|
||||
|
||||
void deleteNotificationTargetById(TenantId tenantId, NotificationTargetId id);
|
||||
|
||||
|
||||
@ -13,14 +13,13 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.thingsboard.server.common.data.notification.rule.trigger;
|
||||
package org.thingsboard.server.dao.usage;
|
||||
|
||||
import org.thingsboard.server.common.data.id.EntityId;
|
||||
import org.thingsboard.server.common.data.UsageInfo;
|
||||
import org.thingsboard.server.common.data.id.TenantId;
|
||||
|
||||
public interface NotificationRuleTrigger {
|
||||
public interface UsageInfoService {
|
||||
|
||||
NotificationRuleTriggerType getType();
|
||||
|
||||
EntityId getOriginatorEntityId();
|
||||
UsageInfo getUsageInfo(TenantId tenantId);
|
||||
|
||||
}
|
||||
@ -18,18 +18,19 @@ package org.thingsboard.server.dao.user;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import org.thingsboard.server.common.data.id.TenantId;
|
||||
import org.thingsboard.server.common.data.id.UserId;
|
||||
import org.thingsboard.server.common.data.security.UserSettings;
|
||||
import org.thingsboard.server.common.data.settings.UserSettings;
|
||||
import org.thingsboard.server.common.data.settings.UserSettingsType;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface UserSettingsService {
|
||||
|
||||
void updateUserSettings(TenantId tenantId, UserId userId, JsonNode settings);
|
||||
void updateUserSettings(TenantId tenantId, UserId userId, UserSettingsType type, JsonNode settings);
|
||||
|
||||
UserSettings saveUserSettings(TenantId tenantId, UserSettings userSettings);
|
||||
|
||||
UserSettings findUserSettings(TenantId tenantId, UserId userId);
|
||||
UserSettings findUserSettings(TenantId tenantId, UserId userId, UserSettingsType type);
|
||||
|
||||
void deleteUserSettings(TenantId tenantId, UserId userId, List<String> jsonPaths);
|
||||
void deleteUserSettings(TenantId tenantId, UserId userId, UserSettingsType type, List<String> jsonPaths);
|
||||
|
||||
}
|
||||
|
||||
@ -24,7 +24,7 @@ public enum ApiFeature {
|
||||
JS("jsExecutionApiState", "JavaScript functions execution"),
|
||||
EMAIL("emailApiState", "Email messages"),
|
||||
SMS("smsApiState", "SMS messages"),
|
||||
ALARM("alarmApiState", "Created alarms");
|
||||
ALARM("alarmApiState", "Alarms");
|
||||
|
||||
@Getter
|
||||
private final String apiStateKey;
|
||||
|
||||
@ -19,14 +19,14 @@ import lombok.Getter;
|
||||
|
||||
public enum ApiUsageRecordKey {
|
||||
|
||||
TRANSPORT_MSG_COUNT(ApiFeature.TRANSPORT, "transportMsgCount", "transportMsgLimit"),
|
||||
TRANSPORT_DP_COUNT(ApiFeature.TRANSPORT, "transportDataPointsCount", "transportDataPointsLimit"),
|
||||
STORAGE_DP_COUNT(ApiFeature.DB, "storageDataPointsCount", "storageDataPointsLimit"),
|
||||
RE_EXEC_COUNT(ApiFeature.RE, "ruleEngineExecutionCount", "ruleEngineExecutionLimit"),
|
||||
JS_EXEC_COUNT(ApiFeature.JS, "jsExecutionCount", "jsExecutionLimit"),
|
||||
EMAIL_EXEC_COUNT(ApiFeature.EMAIL, "emailCount", "emailLimit"),
|
||||
SMS_EXEC_COUNT(ApiFeature.SMS, "smsCount", "smsLimit"),
|
||||
CREATED_ALARMS_COUNT(ApiFeature.ALARM, "createdAlarmsCount", "createdAlarmsLimit");
|
||||
TRANSPORT_MSG_COUNT(ApiFeature.TRANSPORT, "transportMsgCount", "transportMsgLimit", "message"),
|
||||
TRANSPORT_DP_COUNT(ApiFeature.TRANSPORT, "transportDataPointsCount", "transportDataPointsLimit", "data point"),
|
||||
STORAGE_DP_COUNT(ApiFeature.DB, "storageDataPointsCount", "storageDataPointsLimit", "data point"),
|
||||
RE_EXEC_COUNT(ApiFeature.RE, "ruleEngineExecutionCount", "ruleEngineExecutionLimit", "Rule Engine execution"),
|
||||
JS_EXEC_COUNT(ApiFeature.JS, "jsExecutionCount", "jsExecutionLimit", "JavaScript execution"),
|
||||
EMAIL_EXEC_COUNT(ApiFeature.EMAIL, "emailCount", "emailLimit", "email message"),
|
||||
SMS_EXEC_COUNT(ApiFeature.SMS, "smsCount", "smsLimit", "SMS message"),
|
||||
CREATED_ALARMS_COUNT(ApiFeature.ALARM, "createdAlarmsCount", "createdAlarmsLimit", "alarm");
|
||||
|
||||
private static final ApiUsageRecordKey[] JS_RECORD_KEYS = {JS_EXEC_COUNT};
|
||||
private static final ApiUsageRecordKey[] RE_RECORD_KEYS = {RE_EXEC_COUNT};
|
||||
@ -42,11 +42,14 @@ public enum ApiUsageRecordKey {
|
||||
private final String apiCountKey;
|
||||
@Getter
|
||||
private final String apiLimitKey;
|
||||
@Getter
|
||||
private final String unitLabel;
|
||||
|
||||
ApiUsageRecordKey(ApiFeature apiFeature, String apiCountKey, String apiLimitKey) {
|
||||
ApiUsageRecordKey(ApiFeature apiFeature, String apiCountKey, String apiLimitKey, String unitLabel) {
|
||||
this.apiFeature = apiFeature;
|
||||
this.apiCountKey = apiCountKey;
|
||||
this.apiLimitKey = apiLimitKey;
|
||||
this.unitLabel = unitLabel;
|
||||
}
|
||||
|
||||
public static ApiUsageRecordKey[] getKeys(ApiFeature feature) {
|
||||
|
||||
@ -0,0 +1,48 @@
|
||||
/**
|
||||
* Copyright © 2016-2023 The Thingsboard Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.thingsboard.server.common.data;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
@Data
|
||||
public class ApiUsageRecordState implements Serializable {
|
||||
|
||||
private final ApiFeature apiFeature;
|
||||
private final ApiUsageRecordKey key;
|
||||
private final long threshold;
|
||||
private final long value;
|
||||
|
||||
public String getValueAsString() {
|
||||
return valueAsString(value);
|
||||
}
|
||||
|
||||
public String getThresholdAsString() {
|
||||
return valueAsString(threshold);
|
||||
}
|
||||
|
||||
private String valueAsString(long value) {
|
||||
if (value > 1_000_000 && value % 1_000_000 < 10_000) {
|
||||
return value / 1_000_000 + "M";
|
||||
} else if (value > 10_000) {
|
||||
return String.format("%.2fM", ((double) value) / 1_000_000);
|
||||
} else {
|
||||
return value + "";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -42,4 +42,6 @@ public class CacheConstants {
|
||||
public static final String TWO_FA_VERIFICATION_CODES_CACHE = "twoFaVerificationCodes";
|
||||
public static final String VERSION_CONTROL_TASK_CACHE = "versionControlTask";
|
||||
public static final String USER_SETTINGS_CACHE = "userSettings";
|
||||
public static final String DASHBOARD_TITLES_CACHE = "dashboardTitles";
|
||||
public static final String ENTITY_COUNT_CACHE = "entityCount";
|
||||
}
|
||||
|
||||
@ -17,6 +17,7 @@ package org.thingsboard.server.common.data;
|
||||
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
import org.thingsboard.server.common.data.kv.LongDataEntry;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@ -26,18 +27,17 @@ public class SystemInfoData {
|
||||
private String serviceId;
|
||||
@ApiModelProperty(position = 2, value = "Service type.")
|
||||
private String serviceType;
|
||||
@ApiModelProperty(position = 3, value = "CPU usage.")
|
||||
private Double cpuUsage;
|
||||
@ApiModelProperty(position = 3, value = "CPU usage, in percent.")
|
||||
private Long cpuUsage;
|
||||
@ApiModelProperty(position = 4, value = "Total CPU usage.")
|
||||
private Double totalCpuUsage;
|
||||
@ApiModelProperty(position = 5, value = "Memory usage in bytes.")
|
||||
private Long cpuCount;
|
||||
@ApiModelProperty(position = 5, value = "Memory usage, in percent.")
|
||||
private Long memoryUsage;
|
||||
@ApiModelProperty(position = 6, value = "Total memory in bytes.")
|
||||
private Long totalMemory;
|
||||
@ApiModelProperty(position = 6, value = "Free memory in bytes.")
|
||||
private Long freeMemory;
|
||||
@ApiModelProperty(position = 7, value = "Free disc space in bytes.")
|
||||
private Long freeDiscSpace;
|
||||
@ApiModelProperty(position = 7, value = "Total disc space in bytes.")
|
||||
@ApiModelProperty(position = 7, value = "Disk usage, in percent.")
|
||||
private Long discUsage;
|
||||
@ApiModelProperty(position = 8, value = "Total disc space in bytes.")
|
||||
private Long totalDiscSpace;
|
||||
|
||||
}
|
||||
|
||||
@ -19,14 +19,24 @@ import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
@ApiModel
|
||||
@Data
|
||||
public class UpdateMessage {
|
||||
public class UpdateMessage implements Serializable {
|
||||
|
||||
@ApiModelProperty(position = 1, value = "The message about new platform update available.")
|
||||
private final String message;
|
||||
@ApiModelProperty(position = 2, value = "'True' if new platform update is available.")
|
||||
private final boolean isUpdateAvailable;
|
||||
@ApiModelProperty(position = 3, value = "Current ThingsBoard version.")
|
||||
@ApiModelProperty(position = 1, value = "'True' if new platform update is available.")
|
||||
private final boolean updateAvailable;
|
||||
@ApiModelProperty(position = 2, value = "Current ThingsBoard version.")
|
||||
private final String currentVersion;
|
||||
@ApiModelProperty(position = 3, value = "Latest ThingsBoard version.")
|
||||
private final String latestVersion;
|
||||
@ApiModelProperty(position = 4, value = "Upgrade instructions URL.")
|
||||
private final String upgradeInstructionsUrl;
|
||||
@ApiModelProperty(position = 5, value = "Current ThingsBoard version release notes URL.")
|
||||
private final String currentVersionReleaseNotesUrl;
|
||||
@ApiModelProperty(position = 6, value = "Latest ThingsBoard version release notes URL.")
|
||||
private final String latestVersionReleaseNotesUrl;
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -0,0 +1,43 @@
|
||||
/**
|
||||
* Copyright © 2016-2023 The Thingsboard Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.thingsboard.server.common.data;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class UsageInfo {
|
||||
private long devices;
|
||||
private long maxDevices;
|
||||
private long assets;
|
||||
private long maxAssets;
|
||||
private long customers;
|
||||
private long maxCustomers;
|
||||
private long users;
|
||||
private long maxUsers;
|
||||
private long dashboards;
|
||||
private long maxDashboards;
|
||||
|
||||
private long transportMessages;
|
||||
private long maxTransportMessages;
|
||||
private long jsExecutions;
|
||||
private long maxJsExecutions;
|
||||
private long emails;
|
||||
private long maxEmails;
|
||||
private long sms;
|
||||
private long maxSms;
|
||||
private long alarms;
|
||||
private long maxAlarms;
|
||||
}
|
||||
@ -30,6 +30,8 @@ import org.thingsboard.server.common.data.security.Authority;
|
||||
import org.thingsboard.server.common.data.validation.Length;
|
||||
import org.thingsboard.server.common.data.validation.NoXss;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.isNotEmpty;
|
||||
|
||||
@ApiModel
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class User extends SearchTextBasedWithAdditionalInfo<UserId> implements HasName, HasTenantId, HasCustomerId, NotificationRecipient {
|
||||
@ -165,6 +167,24 @@ public class User extends SearchTextBasedWithAdditionalInfo<UserId> implements H
|
||||
return getEmail();
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
public String getTitle() {
|
||||
String title = "";
|
||||
if (isNotEmpty(firstName)) {
|
||||
title += firstName;
|
||||
}
|
||||
if (isNotEmpty(lastName)) {
|
||||
if (!title.isEmpty()) {
|
||||
title += " ";
|
||||
}
|
||||
title += lastName;
|
||||
}
|
||||
if (title.isEmpty()) {
|
||||
title = email;
|
||||
}
|
||||
return title;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
@ -13,20 +13,18 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.thingsboard.server.dao.alarm;
|
||||
package org.thingsboard.server.common.data.alarm;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import org.thingsboard.server.common.data.alarm.Alarm;
|
||||
import org.thingsboard.server.common.data.alarm.AlarmInfo;
|
||||
import org.thingsboard.server.common.data.alarm.AlarmSeverity;
|
||||
import org.thingsboard.server.common.data.id.EntityId;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
@Data
|
||||
public class AlarmApiCallResult {
|
||||
public class AlarmApiCallResult implements Serializable {
|
||||
|
||||
private final boolean successful;
|
||||
private final boolean created;
|
||||
@ -19,6 +19,7 @@ import com.fasterxml.jackson.databind.JsonNode;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import org.thingsboard.server.common.data.id.AlarmId;
|
||||
import org.thingsboard.server.common.data.id.CustomerId;
|
||||
import org.thingsboard.server.common.data.id.EntityId;
|
||||
import org.thingsboard.server.common.data.id.TenantId;
|
||||
@ -61,11 +62,17 @@ public class AlarmCreateOrUpdateActiveRequest implements AlarmModificationReques
|
||||
|
||||
private UserId userId;
|
||||
|
||||
private AlarmId edgeAlarmId;
|
||||
|
||||
public static AlarmCreateOrUpdateActiveRequest fromAlarm(Alarm a) {
|
||||
return fromAlarm(a, null);
|
||||
}
|
||||
|
||||
public static AlarmCreateOrUpdateActiveRequest fromAlarm(Alarm a, UserId userId) {
|
||||
return fromAlarm(a, userId, null);
|
||||
}
|
||||
|
||||
public static AlarmCreateOrUpdateActiveRequest fromAlarm(Alarm a, UserId userId, AlarmId edgeAlarmId) {
|
||||
return AlarmCreateOrUpdateActiveRequest.builder()
|
||||
.tenantId(a.getTenantId())
|
||||
.customerId(a.getCustomerId())
|
||||
@ -81,6 +88,7 @@ public class AlarmCreateOrUpdateActiveRequest implements AlarmModificationReques
|
||||
.propagateToTenant(a.isPropagateToTenant())
|
||||
.propagateRelationTypes(a.getPropagateRelationTypes()).build())
|
||||
.userId(userId)
|
||||
.edgeAlarmId(edgeAlarmId)
|
||||
.build();
|
||||
}
|
||||
|
||||
|
||||
@ -16,7 +16,6 @@
|
||||
package org.thingsboard.server.common.data.notification;
|
||||
|
||||
import lombok.Data;
|
||||
import org.thingsboard.server.common.data.User;
|
||||
import org.thingsboard.server.common.data.notification.template.DeliveryMethodNotificationTemplate;
|
||||
|
||||
import java.util.Collection;
|
||||
@ -28,6 +27,6 @@ public class NotificationRequestPreview {
|
||||
private Map<NotificationDeliveryMethod, DeliveryMethodNotificationTemplate> processedTemplates;
|
||||
private int totalRecipientsCount;
|
||||
private Map<String, Integer> recipientsCountByTarget;
|
||||
private Collection<User> recipientsPreview;
|
||||
private Collection<String> recipientsPreview;
|
||||
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user