diff --git a/application/src/main/java/org/thingsboard/server/controller/EventController.java b/application/src/main/java/org/thingsboard/server/controller/EventController.java index d1e9ad8efc..ea77fce94c 100644 --- a/application/src/main/java/org/thingsboard/server/controller/EventController.java +++ b/application/src/main/java/org/thingsboard/server/controller/EventController.java @@ -18,6 +18,7 @@ package org.thingsboard.server.controller; import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiParam; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.PathVariable; @@ -26,6 +27,7 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestController; import org.thingsboard.server.common.data.Event; import org.thingsboard.server.common.data.event.EventFilter; @@ -238,4 +240,30 @@ public class EventController extends BaseController { } } + @ApiOperation(value = "Clear Events (clearEvents)", notes = "Clears events by filter for specified entity.") + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") + @RequestMapping(value = "/events/{entityType}/{entityId}/clear", method = RequestMethod.POST) + @ResponseStatus(HttpStatus.OK) + public void clearEvents(@ApiParam(value = ENTITY_TYPE_PARAM_DESCRIPTION, required = true) + @PathVariable(ENTITY_TYPE) String strEntityType, + @ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION, required = true) + @PathVariable(ENTITY_ID) String strEntityId, + @ApiParam(value = EVENT_START_TIME_DESCRIPTION) + @RequestParam(required = false) Long startTime, + @ApiParam(value = EVENT_END_TIME_DESCRIPTION) + @RequestParam(required = false) Long endTime, + @ApiParam(value = EVENT_FILTER_DEFINITION) + @RequestBody EventFilter eventFilter) throws ThingsboardException { + checkParameter("EntityId", strEntityId); + checkParameter("EntityType", strEntityType); + try { + EntityId entityId = EntityIdFactory.getByTypeAndId(strEntityType, strEntityId); + checkEntityId(entityId, Operation.WRITE); + + eventService.removeEvents(getTenantId(), entityId, eventFilter, startTime, endTime); + } catch (Exception e) { + throw handleException(e); + } + } + } diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/event/EventService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/event/EventService.java index db1c77697e..f43d17915e 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/event/EventService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/event/EventService.java @@ -46,6 +46,8 @@ public interface EventService { void removeEvents(TenantId tenantId, EntityId entityId); + void removeEvents(TenantId tenantId, EntityId entityId, EventFilter eventFilter, Long startTime, Long endTime); + void cleanupEvents(long ttl, long debugTtl); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/event/BaseEventService.java b/dao/src/main/java/org/thingsboard/server/dao/event/BaseEventService.java index 2785df90df..0c9cede327 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/event/BaseEventService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/event/BaseEventService.java @@ -25,6 +25,7 @@ import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.Event; import org.thingsboard.server.common.data.event.EventFilter; import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.IdBased; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.TimePageLink; @@ -33,6 +34,7 @@ import org.thingsboard.server.dao.service.DataValidator; import java.util.List; import java.util.Optional; +import java.util.stream.Collectors; @Service @Slf4j @@ -118,17 +120,28 @@ public class BaseEventService implements EventService { @Override public void removeEvents(TenantId tenantId, EntityId entityId) { - PageData eventPageData; - TimePageLink eventPageLink = new TimePageLink(1000); + removeEvents(tenantId, entityId, null, null, null); + } + + @Override + public void removeEvents(TenantId tenantId, EntityId entityId, EventFilter eventFilter, Long startTime, Long endTime) { + TimePageLink eventsPageLink = new TimePageLink(1000, 0, null, null, startTime, endTime); + PageData eventsPageData; do { - eventPageData = findEvents(tenantId, entityId, eventPageLink); - for (Event event : eventPageData.getData()) { - eventDao.removeById(tenantId, event.getUuidId()); + if (eventFilter == null) { + eventsPageData = findEvents(tenantId, entityId, eventsPageLink); + } else { + eventsPageData = findEventsByFilter(tenantId, entityId, eventFilter, eventsPageLink); } - if (eventPageData.hasNext()) { - eventPageLink = eventPageLink.nextPageLink(); + + eventDao.removeAllByIds(eventsPageData.getData().stream() + .map(IdBased::getUuidId) + .collect(Collectors.toList())); + + if (eventsPageData.hasNext()) { + eventsPageLink = eventsPageLink.nextPageLink(); } - } while (eventPageData.hasNext()); + } while (eventsPageData.hasNext()); } @Override diff --git a/ui-ngx/src/app/core/http/event.service.ts b/ui-ngx/src/app/core/http/event.service.ts index fd740d73a7..df3466bc3e 100644 --- a/ui-ngx/src/app/core/http/event.service.ts +++ b/ui-ngx/src/app/core/http/event.service.ts @@ -44,5 +44,12 @@ export class EventService { return this.http.post>(`/api/events/${entityId.entityType}/${entityId.id}` + `${pageLink.toQuery()}&tenantId=${tenantId}`, {...filters, eventType}, defaultHttpOptionsFromConfig(config)); } - + + public clearEvents(entityId: EntityId, eventType: EventType | DebugEventType, filters: FilterEventBody, tenantId: string, + pageLink: TimePageLink, config?: RequestConfig) { + return this.http.post(`/api/events/${entityId.entityType}/${entityId.id}/clear?tenantId=${tenantId}` + + (pageLink.startTime ? `&startTime=${pageLink.startTime}` : ``) + + (pageLink.endTime ? `&endTime=${pageLink.endTime}` : ``), {...filters, eventType}, + defaultHttpOptionsFromConfig(config)); + } } diff --git a/ui-ngx/src/app/modules/home/components/event/event-table-config.ts b/ui-ngx/src/app/modules/home/components/event/event-table-config.ts index 9bf6912c9f..d407cf262b 100644 --- a/ui-ngx/src/app/modules/home/components/event/event-table-config.ts +++ b/ui-ngx/src/app/modules/home/components/event/event-table-config.ts @@ -135,6 +135,19 @@ export class EventTableConfig extends EntityTableConfig { onAction: ($event) => { this.editEventFilter($event); } + }, + { + name: this.translate.instant('event.clean-events'), + icon: 'delete', + isEnabled: () => true, + onAction: ($event) => { + this.eventService.clearEvents(this.entityId, this.eventType, this.filterParams, this.tenantId, this.table.pageLink as TimePageLink).subscribe( + () => { + this.table.paginator.pageIndex = 0; + this.table.updateData(); + } + ); + } }); } diff --git a/ui-ngx/src/app/modules/home/models/entity/entities-table-config.models.ts b/ui-ngx/src/app/modules/home/models/entity/entities-table-config.models.ts index 2f3d58e61f..93e68e40b7 100644 --- a/ui-ngx/src/app/modules/home/models/entity/entities-table-config.models.ts +++ b/ui-ngx/src/app/modules/home/models/entity/entities-table-config.models.ts @@ -25,7 +25,8 @@ import { EntityComponent } from '@home/components/entity/entity.component'; import { Type } from '@angular/core'; import { EntityAction } from './entity-component.models'; import { HasUUID } from '@shared/models/id/has-uuid'; -import { PageLink } from '@shared/models/page/page-link'; +import { PageLink, TimePageLink } from '@shared/models/page/page-link'; +import { EntitiesTableComponent } from '@home/components/entity/entities-table.component'; import { EntityTableHeaderComponent } from '@home/components/entity/entity-table-header.component'; import { ActivatedRoute } from '@angular/router'; import { EntityTabsComponent } from '../../components/entity/entity-tabs.component'; diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index 5d3adba457..fee65c086f 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -1853,6 +1853,7 @@ "event": { "event-type": "Event type", "events-filter": "Events Filter", + "clean-events": "Clear Events", "type-error": "Error", "type-lc-event": "Lifecycle event", "type-stats": "Statistics",