Merge with develop/3.5
This commit is contained in:
		
							parent
							
								
									de921b4fa1
								
							
						
					
					
						commit
						d71307402e
					
				@ -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')")
 | 
			
		||||
 | 
			
		||||
@ -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);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -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;
 | 
			
		||||
@ -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);
 | 
			
		||||
 | 
			
		||||
@ -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);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -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;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,25 @@
 | 
			
		||||
/**
 | 
			
		||||
 * 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 lombok.Data;
 | 
			
		||||
 | 
			
		||||
@Data
 | 
			
		||||
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
 | 
			
		||||
 | 
			
		||||
@ -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;
 | 
			
		||||
@ -189,6 +193,137 @@ public abstract class BaseEntityQueryControllerTest extends AbstractControllerTe
 | 
			
		||||
        Assert.assertEquals(97, count2.longValue());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @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);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        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
 | 
			
		||||
    public void testSimpleFindEntityDataByQuery() throws Exception {
 | 
			
		||||
        List<Device> devices = new ArrayList<>();
 | 
			
		||||
 | 
			
		||||
@ -330,7 +330,6 @@ public abstract class BaseHomePageApiTest extends AbstractControllerTest {
 | 
			
		||||
        Assert.assertTrue(featuresInfo.isOauthEnabled());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    private OAuth2Info createDefaultOAuth2Info() {
 | 
			
		||||
        return new OAuth2Info(true, Lists.newArrayList(
 | 
			
		||||
                OAuth2ParamsInfo.builder()
 | 
			
		||||
 | 
			
		||||
@ -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,19 +19,20 @@ 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;
 | 
			
		||||
@ -113,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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -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.query;
 | 
			
		||||
 | 
			
		||||
import lombok.AllArgsConstructor;
 | 
			
		||||
import lombok.Builder;
 | 
			
		||||
import lombok.Getter;
 | 
			
		||||
import lombok.NoArgsConstructor;
 | 
			
		||||
import lombok.ToString;
 | 
			
		||||
import org.thingsboard.server.common.data.alarm.AlarmSearchStatus;
 | 
			
		||||
import org.thingsboard.server.common.data.alarm.AlarmSeverity;
 | 
			
		||||
import org.thingsboard.server.common.data.id.UserId;
 | 
			
		||||
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
@Builder
 | 
			
		||||
@NoArgsConstructor
 | 
			
		||||
@AllArgsConstructor
 | 
			
		||||
@Getter
 | 
			
		||||
@ToString
 | 
			
		||||
public class AlarmCountQuery extends EntityCountQuery {
 | 
			
		||||
    private long startTs;
 | 
			
		||||
    private long endTs;
 | 
			
		||||
    private long timeWindow;
 | 
			
		||||
    private List<String> typeList;
 | 
			
		||||
    private List<AlarmSearchStatus> statusList;
 | 
			
		||||
    private List<AlarmSeverity> severityList;
 | 
			
		||||
    private boolean searchPropagatedAlarms;
 | 
			
		||||
    private UserId assigneeId;
 | 
			
		||||
}
 | 
			
		||||
@ -19,12 +19,12 @@ 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.AlarmSeverity;
 | 
			
		||||
import org.thingsboard.server.common.data.alarm.AlarmStatusFilter;
 | 
			
		||||
import org.thingsboard.server.common.data.alarm.AlarmUpdateRequest;
 | 
			
		||||
import org.thingsboard.server.common.data.alarm.AlarmCreateOrUpdateActiveRequest;
 | 
			
		||||
import org.thingsboard.server.common.data.alarm.EntityAlarm;
 | 
			
		||||
import org.thingsboard.server.common.data.id.AlarmId;
 | 
			
		||||
import org.thingsboard.server.common.data.id.CustomerId;
 | 
			
		||||
@ -33,6 +33,7 @@ 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.page.PageLink;
 | 
			
		||||
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.Dao;
 | 
			
		||||
@ -89,4 +90,6 @@ public interface AlarmDao extends Dao<Alarm> {
 | 
			
		||||
 | 
			
		||||
    AlarmApiCallResult unassignAlarm(TenantId tenantId, AlarmId alarmId, long unassignTime);
 | 
			
		||||
 | 
			
		||||
    long countAlarmsByQuery(TenantId tenantId, CustomerId customerId, AlarmCountQuery query);
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -28,15 +28,15 @@ import org.springframework.util.CollectionUtils;
 | 
			
		||||
import org.thingsboard.server.common.data.EntityType;
 | 
			
		||||
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.AlarmModificationRequest;
 | 
			
		||||
import org.thingsboard.server.common.data.alarm.AlarmStatusFilter;
 | 
			
		||||
import org.thingsboard.server.common.data.alarm.AlarmUpdateRequest;
 | 
			
		||||
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.AlarmCreateOrUpdateActiveRequest;
 | 
			
		||||
import org.thingsboard.server.common.data.alarm.EntityAlarm;
 | 
			
		||||
import org.thingsboard.server.common.data.exception.ApiUsageLimitsExceededException;
 | 
			
		||||
import org.thingsboard.server.common.data.id.AlarmId;
 | 
			
		||||
@ -46,6 +46,7 @@ import org.thingsboard.server.common.data.id.HasId;
 | 
			
		||||
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.relation.EntityRelation;
 | 
			
		||||
@ -358,6 +359,12 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
 | 
			
		||||
        alarmDao.deleteEntityAlarmRecords(tenantId, entityId);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public long countAlarmsByQuery(TenantId tenantId, CustomerId customerId, AlarmCountQuery query) {
 | 
			
		||||
        validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
 | 
			
		||||
        return alarmDao.countAlarmsByQuery(tenantId, customerId, query);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private Alarm merge(Alarm existing, Alarm alarm) {
 | 
			
		||||
        if (alarm.getStartTs() > existing.getEndTs()) {
 | 
			
		||||
            existing.setEndTs(alarm.getStartTs());
 | 
			
		||||
 | 
			
		||||
@ -29,13 +29,13 @@ import org.thingsboard.server.common.data.EntityType;
 | 
			
		||||
import org.thingsboard.server.common.data.StringUtils;
 | 
			
		||||
import org.thingsboard.server.common.data.alarm.Alarm;
 | 
			
		||||
import org.thingsboard.server.common.data.alarm.AlarmAssignee;
 | 
			
		||||
import org.thingsboard.server.common.data.alarm.AlarmCreateOrUpdateActiveRequest;
 | 
			
		||||
import org.thingsboard.server.common.data.alarm.AlarmInfo;
 | 
			
		||||
import org.thingsboard.server.common.data.alarm.AlarmPropagationInfo;
 | 
			
		||||
import org.thingsboard.server.common.data.alarm.AlarmQuery;
 | 
			
		||||
import org.thingsboard.server.common.data.alarm.AlarmSeverity;
 | 
			
		||||
import org.thingsboard.server.common.data.alarm.AlarmStatusFilter;
 | 
			
		||||
import org.thingsboard.server.common.data.alarm.AlarmUpdateRequest;
 | 
			
		||||
import org.thingsboard.server.common.data.alarm.AlarmCreateOrUpdateActiveRequest;
 | 
			
		||||
import org.thingsboard.server.common.data.alarm.EntityAlarm;
 | 
			
		||||
import org.thingsboard.server.common.data.id.AlarmId;
 | 
			
		||||
import org.thingsboard.server.common.data.id.CustomerId;
 | 
			
		||||
@ -45,6 +45,7 @@ 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.page.PageLink;
 | 
			
		||||
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.DaoUtil;
 | 
			
		||||
@ -297,6 +298,11 @@ public class JpaAlarmDao extends JpaAbstractDao<AlarmEntity, Alarm> implements A
 | 
			
		||||
        return toAlarmApiResult(alarmRepository.unassignAlarm(tenantId.getId(), id.getId(), unassignTime));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public long countAlarmsByQuery(TenantId tenantId, CustomerId customerId, AlarmCountQuery query) {
 | 
			
		||||
        return alarmQueryRepository.countAlarmsByQuery(tenantId, customerId, query);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @NotNull
 | 
			
		||||
    private static String getPropagationTypes(AlarmPropagationInfo ap) {
 | 
			
		||||
        String propagateRelationTypes;
 | 
			
		||||
 | 
			
		||||
@ -15,9 +15,11 @@
 | 
			
		||||
 */
 | 
			
		||||
package org.thingsboard.server.dao.sql.query;
 | 
			
		||||
 | 
			
		||||
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.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;
 | 
			
		||||
 | 
			
		||||
@ -28,4 +30,6 @@ public interface AlarmQueryRepository {
 | 
			
		||||
    PageData<AlarmData> findAlarmDataByQueryForEntities(TenantId tenantId,
 | 
			
		||||
                                                        AlarmDataQuery query, Collection<EntityId> orderedEntityIds);
 | 
			
		||||
 | 
			
		||||
    long countAlarmsByQuery(TenantId tenantId, CustomerId customerId, AlarmCountQuery query);
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -19,13 +19,16 @@ import lombok.extern.slf4j.Slf4j;
 | 
			
		||||
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
 | 
			
		||||
import org.springframework.stereotype.Repository;
 | 
			
		||||
import org.springframework.transaction.support.TransactionTemplate;
 | 
			
		||||
import org.springframework.util.CollectionUtils;
 | 
			
		||||
import org.thingsboard.server.common.data.EntityType;
 | 
			
		||||
import org.thingsboard.server.common.data.StringUtils;
 | 
			
		||||
import org.thingsboard.server.common.data.alarm.AlarmSeverity;
 | 
			
		||||
import org.thingsboard.server.common.data.alarm.AlarmStatusFilter;
 | 
			
		||||
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.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.AlarmDataPageLink;
 | 
			
		||||
import org.thingsboard.server.common.data.query.AlarmDataQuery;
 | 
			
		||||
@ -305,6 +308,95 @@ public class DefaultAlarmQueryRepository implements AlarmQueryRepository {
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public long countAlarmsByQuery(TenantId tenantId, CustomerId customerId, AlarmCountQuery query) {
 | 
			
		||||
        QueryContext ctx = new QueryContext(new QuerySecurityContext(tenantId, null, EntityType.ALARM));
 | 
			
		||||
 | 
			
		||||
        ctx.append("select count(id) from alarm_info a ");
 | 
			
		||||
 | 
			
		||||
        if (query.isSearchPropagatedAlarms()) {
 | 
			
		||||
            ctx.append(JOIN_ENTITY_ALARMS);
 | 
			
		||||
            ctx.append("where a.tenant_id = :tenantId and ea.tenant_id = :tenantId");
 | 
			
		||||
            ctx.addUuidParameter("tenantId", tenantId.getId());
 | 
			
		||||
            if (customerId != null && !customerId.isNullUid()) {
 | 
			
		||||
                ctx.append(" and a.customer_id = :customerId and ea.customer_id = :customerId");
 | 
			
		||||
                ctx.addUuidParameter("customerId", customerId.getId());
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            ctx.append("where a.tenant_id = :tenantId");
 | 
			
		||||
            ctx.addUuidParameter("tenantId", tenantId.getId());
 | 
			
		||||
            if (customerId != null && !customerId.isNullUid()) {
 | 
			
		||||
                ctx.append(" and a.customer_id = :customerId");
 | 
			
		||||
                ctx.addUuidParameter("customerId", customerId.getId());
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        long startTs;
 | 
			
		||||
        long endTs;
 | 
			
		||||
        if (query.getTimeWindow() > 0) {
 | 
			
		||||
            endTs = System.currentTimeMillis();
 | 
			
		||||
            startTs = endTs - query.getTimeWindow();
 | 
			
		||||
        } else {
 | 
			
		||||
            startTs = query.getStartTs();
 | 
			
		||||
            endTs = query.getEndTs();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (startTs > 0) {
 | 
			
		||||
            ctx.append(" and a.created_time >= :startTime");
 | 
			
		||||
            ctx.addLongParameter("startTime", startTs);
 | 
			
		||||
            if (query.isSearchPropagatedAlarms()) {
 | 
			
		||||
                ctx.append(" and ea.created_time >= :startTime");
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (endTs > 0) {
 | 
			
		||||
            ctx.append(" and a.created_time <= :endTime");
 | 
			
		||||
            ctx.addLongParameter("endTime", endTs);
 | 
			
		||||
            if (query.isSearchPropagatedAlarms()) {
 | 
			
		||||
                ctx.append(" and ea.created_time <= :endTime");
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!CollectionUtils.isEmpty(query.getTypeList())) {
 | 
			
		||||
            ctx.append(" and a.type in (:alarmTypes)");
 | 
			
		||||
            ctx.addStringListParameter("alarmTypes", query.getTypeList());
 | 
			
		||||
            if (query.isSearchPropagatedAlarms()) {
 | 
			
		||||
                ctx.append(" and ea.alarm_type in (:alarmTypes)");
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (query.getSeverityList() != null && !query.getSeverityList().isEmpty()) {
 | 
			
		||||
            ctx.append(" and a.severity in (:alarmSeverities)");
 | 
			
		||||
            ctx.addStringListParameter("alarmSeverities", query.getSeverityList().stream().map(AlarmSeverity::name).collect(Collectors.toList()));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        AlarmStatusFilter asf = AlarmStatusFilter.from(query.getStatusList());
 | 
			
		||||
        if (asf.hasAnyFilter()) {
 | 
			
		||||
            if (asf.hasAckFilter()) {
 | 
			
		||||
                ctx.append(" and a.acknowledged = :ackStatus");
 | 
			
		||||
                ctx.addBooleanParameter("ackStatus", asf.getAckFilter());
 | 
			
		||||
            }
 | 
			
		||||
            if (asf.hasClearFilter()) {
 | 
			
		||||
                ctx.append(" and a.cleared = :clearStatus");
 | 
			
		||||
                ctx.addBooleanParameter("clearStatus", asf.getClearFilter());
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (query.getAssigneeId() != null) {
 | 
			
		||||
            ctx.addUuidParameter("assigneeId", query.getAssigneeId().getId());
 | 
			
		||||
            ctx.append(" and a.assignee_id = :assigneeId");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return transactionTemplate.execute(trStatus -> {
 | 
			
		||||
            long queryTs = System.currentTimeMillis();
 | 
			
		||||
            try {
 | 
			
		||||
                return jdbcTemplate.queryForObject(ctx.getQuery(), ctx, Long.class);
 | 
			
		||||
            } finally {
 | 
			
		||||
                queryLog.logQuery(ctx, ctx.getQuery(), System.currentTimeMillis() - queryTs);
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private String buildTextSearchQuery(QueryContext ctx, List<EntityKey> selectionMapping, String searchText) {
 | 
			
		||||
        if (!StringUtils.isEmpty(searchText) && selectionMapping != null && !selectionMapping.isEmpty()) {
 | 
			
		||||
            String lowerSearchText = searchText.toLowerCase() + "%";
 | 
			
		||||
 | 
			
		||||
@ -37,6 +37,7 @@ import org.thingsboard.server.common.data.id.AssetId;
 | 
			
		||||
import org.thingsboard.server.common.data.page.PageData;
 | 
			
		||||
import org.thingsboard.server.common.data.page.SortOrder;
 | 
			
		||||
import org.thingsboard.server.common.data.page.TimePageLink;
 | 
			
		||||
import org.thingsboard.server.common.data.query.AlarmCountQuery;
 | 
			
		||||
import org.thingsboard.server.common.data.query.AlarmData;
 | 
			
		||||
import org.thingsboard.server.common.data.query.AlarmDataPageLink;
 | 
			
		||||
import org.thingsboard.server.common.data.query.AlarmDataQuery;
 | 
			
		||||
@ -699,6 +700,69 @@ public abstract class BaseAlarmServiceTest extends AbstractServiceTest {
 | 
			
		||||
        Assert.assertEquals(created, new AlarmInfo(alarms.getData().get(0)));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    public void testCountAlarmsUsingAlarmDataQuery() throws ExecutionException, InterruptedException {
 | 
			
		||||
        AssetId childId = new AssetId(Uuids.timeBased());
 | 
			
		||||
 | 
			
		||||
        long ts = System.currentTimeMillis();
 | 
			
		||||
        AlarmApiCallResult result = alarmService.createAlarm(AlarmCreateOrUpdateActiveRequest.builder()
 | 
			
		||||
                .tenantId(tenantId)
 | 
			
		||||
                .originator(childId)
 | 
			
		||||
                .type(TEST_ALARM)
 | 
			
		||||
                .severity(AlarmSeverity.CRITICAL)
 | 
			
		||||
                .startTs(ts).build());
 | 
			
		||||
        AlarmInfo created = result.getAlarm();
 | 
			
		||||
 | 
			
		||||
        AlarmCountQuery countQuery = AlarmCountQuery.builder()
 | 
			
		||||
                .startTs(0L)
 | 
			
		||||
                .endTs(System.currentTimeMillis())
 | 
			
		||||
                .searchPropagatedAlarms(false)
 | 
			
		||||
                .severityList(Arrays.asList(AlarmSeverity.CRITICAL, AlarmSeverity.WARNING))
 | 
			
		||||
                .statusList(List.of(AlarmSearchStatus.ACTIVE))
 | 
			
		||||
                .build();
 | 
			
		||||
 | 
			
		||||
        long alarmsCount = alarmService.countAlarmsByQuery(tenantId, null, countQuery);
 | 
			
		||||
 | 
			
		||||
        Assert.assertEquals(1, alarmsCount);
 | 
			
		||||
 | 
			
		||||
        countQuery = AlarmCountQuery.builder()
 | 
			
		||||
                .startTs(0L)
 | 
			
		||||
                .endTs(System.currentTimeMillis())
 | 
			
		||||
                .searchPropagatedAlarms(true)
 | 
			
		||||
                .severityList(Arrays.asList(AlarmSeverity.CRITICAL, AlarmSeverity.WARNING))
 | 
			
		||||
                .statusList(List.of(AlarmSearchStatus.ACTIVE))
 | 
			
		||||
                .build();
 | 
			
		||||
 | 
			
		||||
        alarmsCount = alarmService.countAlarmsByQuery(tenantId, null, countQuery);
 | 
			
		||||
 | 
			
		||||
        Assert.assertEquals(1, alarmsCount);
 | 
			
		||||
 | 
			
		||||
        created = alarmService.acknowledgeAlarm(tenantId, created.getId(), System.currentTimeMillis()).getAlarm();
 | 
			
		||||
 | 
			
		||||
        alarmsCount = alarmService.countAlarmsByQuery(tenantId, null, countQuery);
 | 
			
		||||
 | 
			
		||||
        Assert.assertEquals(1, alarmsCount);
 | 
			
		||||
 | 
			
		||||
        alarmService.clearAlarm(tenantId, created.getId(), System.currentTimeMillis(), null);
 | 
			
		||||
        created = alarmService.findAlarmInfoById(tenantId, created.getId());
 | 
			
		||||
 | 
			
		||||
        alarmsCount = alarmService.countAlarmsByQuery(tenantId, null, countQuery);
 | 
			
		||||
 | 
			
		||||
        Assert.assertEquals(0, alarmsCount);
 | 
			
		||||
 | 
			
		||||
        countQuery = AlarmCountQuery.builder()
 | 
			
		||||
                .startTs(0L)
 | 
			
		||||
                .endTs(System.currentTimeMillis())
 | 
			
		||||
                .searchPropagatedAlarms(true)
 | 
			
		||||
                .severityList(Arrays.asList(AlarmSeverity.CRITICAL, AlarmSeverity.WARNING))
 | 
			
		||||
                .statusList(List.of(AlarmSearchStatus.ACTIVE, AlarmSearchStatus.CLEARED))
 | 
			
		||||
                .build();
 | 
			
		||||
 | 
			
		||||
        alarmsCount = alarmService.countAlarmsByQuery(tenantId, null, countQuery);
 | 
			
		||||
 | 
			
		||||
        Assert.assertEquals(1, alarmsCount);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    public void testDeleteAlarm() throws ExecutionException, InterruptedException {
 | 
			
		||||
        AssetId parentId = new AssetId(Uuids.timeBased());
 | 
			
		||||
 | 
			
		||||
@ -122,6 +122,7 @@ import org.thingsboard.server.common.data.page.SortOrder;
 | 
			
		||||
import org.thingsboard.server.common.data.page.TimePageLink;
 | 
			
		||||
import org.thingsboard.server.common.data.plugin.ComponentDescriptor;
 | 
			
		||||
import org.thingsboard.server.common.data.plugin.ComponentType;
 | 
			
		||||
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;
 | 
			
		||||
@ -1649,6 +1650,10 @@ public class RestClient implements Closeable {
 | 
			
		||||
                }).getBody();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public Long countAlarmsByQuery(AlarmCountQuery query) {
 | 
			
		||||
        return restTemplate.postForObject(baseURL + "/api/alarmsQuery/count", query, Long.class);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void saveRelation(EntityRelation relation) {
 | 
			
		||||
        restTemplate.postForLocation(baseURL + "/api/relation", relation);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user