Browse Source

Feature/event filters

* Event filter implementation

* Removed redundant imports

* convert createdTime to column name

* UI: Added ui to filter events parameters

* Case sensitivity fixed, boolean Is Error added

* UI: Added support event filter for error

* Improvement for implementation of Event Filter

* UI: Refactoring event filter

* UI: Add trim

* Filter added for all event types

* UI: Updated filter events for new API

* Refactoring

* Code cleaning. Debug Rule Chain added

* Removed redundant casts in JpaBaseEventDao, correct created_time check

* Removed redundant abstract method in DebugEvent

* refactoring of the filter queries

* Filters for debug events

* UI: Refactoring event table panel

Co-authored-by: AndrewVolosytnykhThingsboard <avolostnykh@thingsboard.io>
Co-authored-by: Vladyslav_Prykhodko <vprykhodko@thingsboard.io>
Co-authored-by: AndrewVolosytnykhThingsboard <77969531+AndrewVolosytnykhThingsboard@users.noreply.github.com>
pull/4416/head
Andrew Shvayka 5 years ago
committed by GitHub
parent
commit
e08d1b2c07
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 37
      application/src/main/java/org/thingsboard/server/controller/EventController.java
  2. 4
      application/src/main/resources/thingsboard.yml
  3. 3
      common/dao-api/src/main/java/org/thingsboard/server/dao/event/EventService.java
  4. 45
      common/data/src/main/java/org/thingsboard/server/common/data/event/DebugEvent.java
  5. 23
      common/data/src/main/java/org/thingsboard/server/common/data/event/DebugRuleChainEventFilter.java
  6. 23
      common/data/src/main/java/org/thingsboard/server/common/data/event/DebugRuleNodeEventFilter.java
  7. 36
      common/data/src/main/java/org/thingsboard/server/common/data/event/ErrorEventFilter.java
  8. 40
      common/data/src/main/java/org/thingsboard/server/common/data/event/EventFilter.java
  9. 20
      common/data/src/main/java/org/thingsboard/server/common/data/event/EventType.java
  10. 37
      common/data/src/main/java/org/thingsboard/server/common/data/event/LifeCycleEventFilter.java
  11. 36
      common/data/src/main/java/org/thingsboard/server/common/data/event/StatisticsEventFilter.java
  12. 7
      dao/src/main/java/org/thingsboard/server/dao/event/BaseEventService.java
  13. 3
      dao/src/main/java/org/thingsboard/server/dao/event/EventDao.java
  14. 171
      dao/src/main/java/org/thingsboard/server/dao/sql/event/EventRepository.java
  15. 106
      dao/src/main/java/org/thingsboard/server/dao/sql/event/JpaBaseEventDao.java
  16. 8
      ui-ngx/src/app/core/http/event.service.ts
  17. 64
      ui-ngx/src/app/modules/home/components/event/event-filter-panel.component.html
  18. 35
      ui-ngx/src/app/modules/home/components/event/event-filter-panel.component.scss
  19. 101
      ui-ngx/src/app/modules/home/components/event/event-filter-panel.component.ts
  20. 122
      ui-ngx/src/app/modules/home/components/event/event-table-config.ts
  21. 13
      ui-ngx/src/app/modules/home/components/event/event-table.component.ts
  22. 2
      ui-ngx/src/app/modules/home/components/home-components.module.ts
  23. 34
      ui-ngx/src/app/shared/models/event.models.ts
  24. 5
      ui-ngx/src/assets/locale/locale.constant-cs_CZ.json
  25. 5
      ui-ngx/src/assets/locale/locale.constant-de_DE.json
  26. 5
      ui-ngx/src/assets/locale/locale.constant-el_GR.json
  27. 8
      ui-ngx/src/assets/locale/locale.constant-en_US.json
  28. 5
      ui-ngx/src/assets/locale/locale.constant-es_ES.json
  29. 5
      ui-ngx/src/assets/locale/locale.constant-fa_IR.json
  30. 5
      ui-ngx/src/assets/locale/locale.constant-fr_FR.json
  31. 5
      ui-ngx/src/assets/locale/locale.constant-it_IT.json
  32. 5
      ui-ngx/src/assets/locale/locale.constant-ja_JA.json
  33. 5
      ui-ngx/src/assets/locale/locale.constant-ka_GE.json
  34. 5
      ui-ngx/src/assets/locale/locale.constant-ko_KR.json
  35. 5
      ui-ngx/src/assets/locale/locale.constant-lv_LV.json
  36. 5
      ui-ngx/src/assets/locale/locale.constant-pt_BR.json
  37. 9
      ui-ngx/src/assets/locale/locale.constant-ro_RO.json
  38. 5
      ui-ngx/src/assets/locale/locale.constant-ru_RU.json
  39. 5
      ui-ngx/src/assets/locale/locale.constant-sl_SI.json
  40. 5
      ui-ngx/src/assets/locale/locale.constant-tr_TR.json
  41. 5
      ui-ngx/src/assets/locale/locale.constant-uk_UA.json
  42. 5
      ui-ngx/src/assets/locale/locale.constant-zh_CN.json
  43. 5
      ui-ngx/src/assets/locale/locale.constant-zh_TW.json

37
application/src/main/java/org/thingsboard/server/controller/EventController.java

@ -18,12 +18,14 @@ package org.thingsboard.server.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
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.RestController;
import org.thingsboard.server.common.data.Event;
import org.thingsboard.server.common.data.event.EventFilter;
import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.EntityIdFactory;
@ -31,6 +33,7 @@ import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.TimePageLink;
import org.thingsboard.server.dao.event.EventService;
import org.thingsboard.server.dao.model.ModelConstants;
import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.security.permission.Operation;
@ -101,4 +104,38 @@ public class EventController extends BaseController {
}
}
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/events/{entityType}/{entityId}", method = RequestMethod.POST)
@ResponseBody
public PageData<Event> getEvents(
@PathVariable("entityType") String strEntityType,
@PathVariable("entityId") String strEntityId,
@RequestParam("tenantId") String strTenantId,
@RequestParam int pageSize,
@RequestParam int page,
@RequestBody EventFilter eventFilter,
@RequestParam(required = false) String textSearch,
@RequestParam(required = false) String sortProperty,
@RequestParam(required = false) String sortOrder,
@RequestParam(required = false) Long startTime,
@RequestParam(required = false) Long endTime) throws ThingsboardException {
checkParameter("EntityId", strEntityId);
checkParameter("EntityType", strEntityType);
try {
TenantId tenantId = new TenantId(toUUID(strTenantId));
EntityId entityId = EntityIdFactory.getByTypeAndId(strEntityType, strEntityId);
checkEntityId(entityId, Operation.READ);
if(sortProperty != null && sortProperty.equals("createdTime") && eventFilter.hasFilterForJsonBody()) {
sortProperty = ModelConstants.CREATED_TIME_PROPERTY;
}
TimePageLink pageLink = createTimePageLink(pageSize, page, textSearch, sortProperty, sortOrder, startTime, endTime);
return checkNotNull(eventService.findEventsByFilter(tenantId, entityId, eventFilter, pageLink));
} catch (Exception e) {
throw handleException(e);
}
}
}

4
application/src/main/resources/thingsboard.yml

@ -322,7 +322,7 @@ actors:
# Enable/disable actor statistics
enabled: "${ACTORS_STATISTICS_ENABLED:true}"
js_print_interval_ms: "${ACTORS_JS_STATISTICS_PRINT_INTERVAL_MS:10000}"
persist_frequency: "${ACTORS_STATISTICS_PERSIST_FREQUENCY:3600000}"
persist_frequency: "${ACTORS_STATISTICS_PERSIST_FREQUENCY:10000}"
cache:
# caffeine or redis
@ -516,7 +516,7 @@ js:
# Built-in JVM JavaScript environment properties
local:
# Use Sandboxed (secured) JVM JavaScript environment
use_js_sandbox: "${USE_LOCAL_JS_SANDBOX:false}"
use_js_sandbox: "${USE_LOCAL_JS_SANDBOX:true}"
# Specify thread pool size for JavaScript sandbox resource monitor
monitor_thread_pool_size: "${LOCAL_JS_SANDBOX_MONITOR_THREAD_POOL_SIZE:4}"
# Maximum CPU time in milliseconds allowed for script execution

3
common/dao-api/src/main/java/org/thingsboard/server/dao/event/EventService.java

@ -17,6 +17,7 @@ package org.thingsboard.server.dao.event;
import com.google.common.util.concurrent.ListenableFuture;
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.TenantId;
import org.thingsboard.server.common.data.page.PageData;
@ -41,6 +42,8 @@ public interface EventService {
List<Event> findLatestEvents(TenantId tenantId, EntityId entityId, String eventType, int limit);
PageData<Event> findEventsByFilter(TenantId tenantId, EntityId entityId, EventFilter eventFilter, TimePageLink pageLink);
void removeEvents(TenantId tenantId, EntityId entityId);
}

45
common/data/src/main/java/org/thingsboard/server/common/data/event/DebugEvent.java

@ -0,0 +1,45 @@
/**
* Copyright © 2016-2021 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.event;
import lombok.Data;
import org.eclipse.leshan.core.util.StringUtils;
@Data
public abstract class DebugEvent implements EventFilter {
private String msgDirectionType;
private String server;
private String dataSearch;
private String metadataSearch;
private String entityName;
private String relationType;
private String entityId;
private String msgType;
private boolean isError;
private String error;
public void setIsError(boolean isError) {
this.isError = isError;
}
@Override
public boolean hasFilterForJsonBody() {
return !StringUtils.isEmpty(msgDirectionType) || !StringUtils.isEmpty(server) || !StringUtils.isEmpty(dataSearch) || !StringUtils.isEmpty(metadataSearch)
|| !StringUtils.isEmpty(entityName) || !StringUtils.isEmpty(relationType) || !StringUtils.isEmpty(entityId) || !StringUtils.isEmpty(msgType) || !StringUtils.isEmpty(error) || isError;
}
}

23
common/data/src/main/java/org/thingsboard/server/common/data/event/DebugRuleChainEventFilter.java

@ -0,0 +1,23 @@
/**
* Copyright © 2016-2021 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.event;
public class DebugRuleChainEventFilter extends DebugEvent {
@Override
public EventType getEventType() {
return EventType.DEBUG_RULE_CHAIN;
}
}

23
common/data/src/main/java/org/thingsboard/server/common/data/event/DebugRuleNodeEventFilter.java

@ -0,0 +1,23 @@
/**
* Copyright © 2016-2021 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.event;
public class DebugRuleNodeEventFilter extends DebugEvent {
@Override
public EventType getEventType() {
return EventType.DEBUG_RULE_NODE;
}
}

36
common/data/src/main/java/org/thingsboard/server/common/data/event/ErrorEventFilter.java

@ -0,0 +1,36 @@
/**
* Copyright © 2016-2021 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.event;
import lombok.Data;
import org.eclipse.leshan.core.util.StringUtils;
@Data
public class ErrorEventFilter implements EventFilter {
private String server;
private String method;
private String error;
@Override
public EventType getEventType() {
return EventType.ERROR;
}
@Override
public boolean hasFilterForJsonBody() {
return !StringUtils.isEmpty(server) || !StringUtils.isEmpty(method) || !StringUtils.isEmpty(error);
}
}

40
common/data/src/main/java/org/thingsboard/server/common/data/event/EventFilter.java

@ -0,0 +1,40 @@
/**
* Copyright © 2016-2021 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.event;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
@JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.PROPERTY,
property = "eventType")
@JsonSubTypes({
@JsonSubTypes.Type(value = DebugRuleNodeEventFilter.class, name = "DEBUG_RULE_NODE"),
@JsonSubTypes.Type(value = DebugRuleChainEventFilter.class, name = "DEBUG_RULE_CHAIN"),
@JsonSubTypes.Type(value = ErrorEventFilter.class, name = "ERROR"),
@JsonSubTypes.Type(value = LifeCycleEventFilter.class, name = "LC_EVENT"),
@JsonSubTypes.Type(value = StatisticsEventFilter.class, name = "STATS")
})
public interface EventFilter {
@JsonIgnore
EventType getEventType();
boolean hasFilterForJsonBody();
}

20
common/data/src/main/java/org/thingsboard/server/common/data/event/EventType.java

@ -0,0 +1,20 @@
/**
* Copyright © 2016-2021 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.event;
public enum EventType {
ERROR, LC_EVENT, STATS, DEBUG_RULE_NODE, DEBUG_RULE_CHAIN
}

37
common/data/src/main/java/org/thingsboard/server/common/data/event/LifeCycleEventFilter.java

@ -0,0 +1,37 @@
/**
* Copyright © 2016-2021 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.event;
import lombok.Data;
import org.eclipse.leshan.core.util.StringUtils;
@Data
public class LifeCycleEventFilter implements EventFilter {
private String server;
private String event;
private String status;
private String error;
@Override
public EventType getEventType() {
return EventType.LC_EVENT;
}
@Override
public boolean hasFilterForJsonBody() {
return !StringUtils.isEmpty(server) || !StringUtils.isEmpty(event) || !StringUtils.isEmpty(status) || !StringUtils.isEmpty(error);
}
}

36
common/data/src/main/java/org/thingsboard/server/common/data/event/StatisticsEventFilter.java

@ -0,0 +1,36 @@
/**
* Copyright © 2016-2021 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.event;
import lombok.Data;
import org.eclipse.leshan.core.util.StringUtils;
@Data
public class StatisticsEventFilter implements EventFilter {
private String server;
private Integer messagesProcessed;
private Integer errorsOccurred;
@Override
public EventType getEventType() {
return EventType.STATS;
}
@Override
public boolean hasFilterForJsonBody() {
return !StringUtils.isEmpty(server) || (messagesProcessed != null && messagesProcessed > 0) || (errorsOccurred != null && errorsOccurred > 0);
}
}

7
dao/src/main/java/org/thingsboard/server/dao/event/BaseEventService.java

@ -23,6 +23,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
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.TenantId;
import org.thingsboard.server.common.data.page.PageData;
@ -30,7 +31,6 @@ import org.thingsboard.server.common.data.page.TimePageLink;
import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.dao.service.DataValidator;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Optional;
@ -111,6 +111,11 @@ public class BaseEventService implements EventService {
return eventDao.findLatestEvents(tenantId.getId(), entityId, eventType, limit);
}
@Override
public PageData<Event> findEventsByFilter(TenantId tenantId, EntityId entityId, EventFilter eventFilter, TimePageLink pageLink) {
return eventDao.findEventByFilter(tenantId.getId(), entityId, eventFilter, pageLink);
}
@Override
public void removeEvents(TenantId tenantId, EntityId entityId) {
PageData<Event> eventPageData;

3
dao/src/main/java/org/thingsboard/server/dao/event/EventDao.java

@ -17,6 +17,7 @@ package org.thingsboard.server.dao.event;
import com.google.common.util.concurrent.ListenableFuture;
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.TenantId;
import org.thingsboard.server.common.data.page.PageData;
@ -88,6 +89,8 @@ public interface EventDao extends Dao<Event> {
*/
PageData<Event> findEvents(UUID tenantId, EntityId entityId, String eventType, TimePageLink pageLink);
PageData<Event> findEventByFilter(UUID tenantId, EntityId entityId, EventFilter eventFilter, TimePageLink pageLink);
/**
* Find latest events by tenantId, entityId and eventType.
*

171
dao/src/main/java/org/thingsboard/server/dao/sql/event/EventRepository.java

@ -44,11 +44,11 @@ public interface EventRepository extends PagingAndSortingRepository<EventEntity,
@Query("SELECT e FROM EventEntity e WHERE e.tenantId = :tenantId AND e.entityType = :entityType " +
"AND e.entityId = :entityId AND e.eventType = :eventType ORDER BY e.eventType DESC, e.id DESC")
List<EventEntity> findLatestByTenantIdAndEntityTypeAndEntityIdAndEventType(
@Param("tenantId") UUID tenantId,
@Param("entityType") EntityType entityType,
@Param("entityId") UUID entityId,
@Param("eventType") String eventType,
Pageable pageable);
@Param("tenantId") UUID tenantId,
@Param("entityType") EntityType entityType,
@Param("entityId") UUID entityId,
@Param("eventType") String eventType,
Pageable pageable);
@Query("SELECT e FROM EventEntity e WHERE " +
"e.tenantId = :tenantId " +
@ -80,4 +80,165 @@ public interface EventRepository extends PagingAndSortingRepository<EventEntity,
@Param("endTime") Long endTime,
Pageable pageable);
@Query(nativeQuery = true,
value = "SELECT e.id, e.created_time, e.body, e.entity_id, e.entity_type, e.event_type, e.event_uid, e.tenant_id, ts FROM " +
"(SELECT *, e.body\\:\\:jsonb as json_body FROM event e WHERE " +
"e.tenant_id = :tenantId " +
"AND e.entity_type = :entityType " +
"AND e.entity_id = :entityId " +
"AND e.event_type = :eventType " +
"AND e.created_time >= :startTime AND (:endTime = 0 OR e.created_time <= :endTime) " +
") AS e WHERE " +
"(:type IS NULL OR lower(json_body->>'type') LIKE concat('%', lower(:type\\:\\:varchar), '%')) " +
"AND (:server IS NULL OR lower(json_body->>'server') LIKE concat('%', lower(:server\\:\\:varchar), '%')) " +
"AND (:entityName IS NULL OR lower(json_body->>'entityName') LIKE concat('%', lower(:entityName\\:\\:varchar), '%')) " +
"AND (:relationType IS NULL OR lower(json_body->>'relationType') LIKE concat('%', lower(:relationType\\:\\:varchar), '%')) " +
"AND (:bodyEntityId IS NULL OR lower(json_body->>'entityId') LIKE concat('%', lower(:bodyEntityId\\:\\:varchar), '%')) " +
"AND (:msgType IS NULL OR lower(json_body->>'msgType') LIKE concat('%', lower(:msgType\\:\\:varchar), '%')) " +
"AND ((:isError = FALSE) OR (json_body->>'error') IS NOT NULL) " +
"AND (:error IS NULL OR lower(json_body->>'error') LIKE concat('%', lower(:error\\:\\:varchar), '%')) " +
"AND (:data IS NULL OR lower(json_body->>'data') LIKE concat('%', lower(:data\\:\\:varchar), '%')) " +
"AND (:metadata IS NULL OR lower(json_body->>'metadata') LIKE concat('%', lower(:metadata\\:\\:varchar), '%')) ",
countQuery = "SELECT count(*) FROM " +
"(SELECT *, e.body\\:\\:jsonb as json_body FROM event e WHERE " +
"e.tenant_id = :tenantId " +
"AND e.entity_type = :entityType " +
"AND e.entity_id = :entityId " +
"AND e.event_type = :eventType " +
"AND e.created_time >= :startTime AND (:endTime = 0 OR e.created_time <= :endTime) " +
") AS e WHERE " +
"(:type IS NULL OR lower(json_body->>'type') LIKE concat('%', lower(:type\\:\\:varchar), '%')) " +
"AND (:server IS NULL OR lower(json_body->>'server') LIKE concat('%', lower(:server\\:\\:varchar), '%')) " +
"AND (:entityName IS NULL OR lower(json_body->>'entityName') LIKE concat('%', lower(:entityName\\:\\:varchar), '%')) " +
"AND (:relationType IS NULL OR lower(json_body->>'relationType') LIKE concat('%', lower(:relationType\\:\\:varchar), '%')) " +
"AND (:bodyEntityId IS NULL OR lower(json_body->>'entityId') LIKE concat('%', lower(:bodyEntityId\\:\\:varchar), '%')) " +
"AND (:msgType IS NULL OR lower(json_body->>'msgType') LIKE concat('%', lower(:msgType\\:\\:varchar), '%')) " +
"AND ((:isError = FALSE) OR (json_body->>'error') IS NOT NULL) " +
"AND (:error IS NULL OR lower(json_body->>'error') LIKE concat('%', lower(:error\\:\\:varchar), '%')) " +
"AND (:data IS NULL OR lower(json_body->>'data') LIKE concat('%', lower(:data\\:\\:varchar), '%')) " +
"AND (:metadata IS NULL OR lower(json_body->>'metadata') LIKE concat('%', lower(:metadata\\:\\:varchar), '%'))"
)
Page<EventEntity> findDebugRuleNodeEvents(@Param("tenantId") UUID tenantId,
@Param("entityId") UUID entityId,
@Param("entityType") String entityType,
@Param("eventType") String eventType,
@Param("startTime") Long startTime,
@Param("endTime") Long endTime,
@Param("type") String type,
@Param("server") String server,
@Param("entityName") String entityName,
@Param("relationType") String relationType,
@Param("bodyEntityId") String bodyEntityId,
@Param("msgType") String msgType,
@Param("isError") boolean isError,
@Param("error") String error,
@Param("data") String data,
@Param("metadata") String metadata,
Pageable pageable);
@Query(nativeQuery = true,
value = "SELECT e.id, e.created_time, e.body, e.entity_id, e.entity_type, e.event_type, e.event_uid, e.tenant_id, ts FROM " +
"(SELECT *, e.body\\:\\:jsonb as json_body FROM event e WHERE " +
"e.tenant_id = :tenantId " +
"AND e.entity_type = :entityType " +
"AND e.entity_id = :entityId " +
"AND e.event_type = 'ERROR' " +
"AND e.created_time >= :startTime AND (:endTime = 0 OR e.created_time <= :endTime) " +
") AS e WHERE " +
"(:server IS NULL OR lower(json_body->>'server') LIKE concat('%', lower(:server\\:\\:varchar), '%')) " +
"AND (:method IS NULL OR lower(json_body->>'method') LIKE concat('%', lower(:method\\:\\:varchar), '%')) " +
"AND (:error IS NULL OR lower(json_body->>'error') LIKE concat('%', lower(:error\\:\\:varchar), '%'))",
countQuery = "SELECT count(*) FROM " +
"(SELECT *, e.body\\:\\:jsonb as json_body FROM event e WHERE " +
"e.tenant_id = :tenantId " +
"AND e.entity_type = :entityType " +
"AND e.entity_id = :entityId " +
"AND e.event_type = 'ERROR' " +
"AND e.created_time >= :startTime AND (:endTime = 0 OR e.created_time <= :endTime) " +
") AS e WHERE " +
"(:server IS NULL OR lower(json_body->>'server') LIKE concat('%', lower(:server\\:\\:varchar), '%')) " +
"AND (:method IS NULL OR lower(json_body->>'method') LIKE concat('%', lower(:method\\:\\:varchar), '%')) " +
"AND (:error IS NULL OR lower(json_body->>'error') LIKE concat('%', lower(:error\\:\\:varchar), '%'))")
Page<EventEntity> findErrorEvents(@Param("tenantId") UUID tenantId,
@Param("entityId") UUID entityId,
@Param("entityType") String entityType,
@Param("startTime") Long startTime,
@Param("endTime") Long endTIme,
@Param("server") String server,
@Param("method") String method,
@Param("error") String error,
Pageable pageable);
@Query(nativeQuery = true,
value = "SELECT e.id, e.created_time, e.body, e.entity_id, e.entity_type, e.event_type, e.event_uid, e.tenant_id, ts FROM " +
"(SELECT *, e.body\\:\\:jsonb as json_body FROM event e WHERE " +
"e.tenant_id = :tenantId " +
"AND e.entity_type = :entityType " +
"AND e.entity_id = :entityId " +
"AND e.event_type = 'LC_EVENT' " +
"AND e.created_time >= :startTime AND (:endTime = 0 OR e.created_time <= :endTime) " +
") AS e WHERE " +
"(:server IS NULL OR lower(json_body->>'server') LIKE concat('%', lower(:server\\:\\:varchar), '%')) " +
"AND (:event IS NULL OR lower(json_body->>'event') LIKE concat('%', lower(:event\\:\\:varchar), '%')) " +
"AND ((:statusFilterEnabled = FALSE) OR lower(json_body->>'success')\\:\\:boolean = :statusFilter) " +
"AND (:error IS NULL OR lower(json_body->>'error') LIKE concat('%', lower(:error\\:\\:varchar), '%'))"
,
countQuery = "SELECT count(*) FROM " +
"(SELECT *, e.body\\:\\:jsonb as json_body FROM event e WHERE " +
"e.tenant_id = :tenantId " +
"AND e.entity_type = :entityType " +
"AND e.entity_id = :entityId " +
"AND e.event_type = 'LC_EVENT' " +
"AND e.created_time >= :startTime AND (:endTime = 0 OR e.created_time <= :endTime) " +
") AS e WHERE " +
"(:server IS NULL OR lower(json_body->>'server') LIKE concat('%', lower(:server\\:\\:varchar), '%')) " +
"AND (:event IS NULL OR lower(json_body->>'event') LIKE concat('%', lower(:event\\:\\:varchar), '%')) " +
"AND ((:statusFilterEnabled = FALSE) OR lower(json_body->>'success')\\:\\:boolean = :statusFilter) " +
"AND (:error IS NULL OR lower(json_body->>'error') LIKE concat('%', lower(:error\\:\\:varchar), '%'))"
)
Page<EventEntity> findLifeCycleEvents(@Param("tenantId") UUID tenantId,
@Param("entityId") UUID entityId,
@Param("entityType") String entityType,
@Param("startTime") Long startTime,
@Param("endTime") Long endTIme,
@Param("server") String server,
@Param("event") String event,
@Param("statusFilterEnabled") boolean statusFilterEnabled,
@Param("statusFilter") boolean statusFilter,
@Param("error") String error,
Pageable pageable);
@Query(nativeQuery = true,
value = "SELECT e.id, e.created_time, e.body, e.entity_id, e.entity_type, e.event_type, e.event_uid, e.tenant_id, ts FROM " +
"(SELECT *, e.body\\:\\:jsonb as json_body FROM event e WHERE " +
"e.tenant_id = :tenantId " +
"AND e.entity_type = :entityType " +
"AND e.entity_id = :entityId " +
"AND e.event_type = 'STATS' " +
"AND e.created_time >= :startTime AND (:endTime = 0 OR e.created_time <= :endTime) " +
") AS e WHERE " +
"(:server IS NULL OR lower(e.body\\:\\:json->>'server') LIKE concat('%', lower(:server\\:\\:varchar), '%')) " +
"AND (:messagesProcessed = 0 OR (json_body->>'messagesProcessed')\\:\\:integer >= :messagesProcessed) " +
"AND (:errorsOccurred = 0 OR (json_body->>'errorsOccurred')\\:\\:integer >= :errorsOccurred) ",
countQuery = "SELECT count(*) FROM " +
"(SELECT *, e.body\\:\\:jsonb as json_body FROM event e WHERE " +
"e.tenant_id = :tenantId " +
"AND e.entity_type = :entityType " +
"AND e.entity_id = :entityId " +
"AND e.event_type = 'LC_EVENT' " +
"AND e.created_time >= :startTime AND (:endTime = 0 OR e.created_time <= :endTime) " +
") AS e WHERE " +
"(:server IS NULL OR lower(e.body\\:\\:json->>'server') LIKE concat('%', lower(:server\\:\\:varchar), '%')) " +
"AND (:messagesProcessed = 0 OR (json_body->>'messagesProcessed')\\:\\:integer >= :messagesProcessed) " +
"AND (:errorsOccurred = 0 OR (json_body->>'errorsOccurred')\\:\\:integer >= :errorsOccurred) ")
Page<EventEntity> findStatisticsEvents(@Param("tenantId") UUID tenantId,
@Param("entityId") UUID entityId,
@Param("entityType") String entityType,
@Param("startTime") Long startTime,
@Param("endTime") Long endTIme,
@Param("server") String server,
@Param("messagesProcessed") Integer messagesProcessed,
@Param("errorsOccurred") Integer errorsOccurred,
Pageable pageable);
}

106
dao/src/main/java/org/thingsboard/server/dao/sql/event/JpaBaseEventDao.java

@ -24,6 +24,12 @@ import org.springframework.data.domain.PageRequest;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.Event;
import org.thingsboard.server.common.data.event.DebugEvent;
import org.thingsboard.server.common.data.event.ErrorEventFilter;
import org.thingsboard.server.common.data.event.EventFilter;
import org.thingsboard.server.common.data.event.EventType;
import org.thingsboard.server.common.data.event.LifeCycleEventFilter;
import org.thingsboard.server.common.data.event.StatisticsEventFilter;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.EventId;
import org.thingsboard.server.common.data.id.TenantId;
@ -147,6 +153,98 @@ public class JpaBaseEventDao extends JpaAbstractDao<EventEntity, Event> implemen
DaoUtil.toPageable(pageLink)));
}
@Override
public PageData<Event> findEventByFilter(UUID tenantId, EntityId entityId, EventFilter eventFilter, TimePageLink pageLink) {
if (eventFilter.hasFilterForJsonBody()) {
switch (eventFilter.getEventType()) {
case DEBUG_RULE_NODE:
case DEBUG_RULE_CHAIN:
return findEventByFilter(tenantId, entityId, (DebugEvent) eventFilter, pageLink);
case LC_EVENT:
return findEventByFilter(tenantId, entityId, (LifeCycleEventFilter) eventFilter, pageLink);
case ERROR:
return findEventByFilter(tenantId, entityId, (ErrorEventFilter) eventFilter, pageLink);
case STATS:
return findEventByFilter(tenantId, entityId, (StatisticsEventFilter) eventFilter, pageLink);
default:
throw new RuntimeException("Not supported event type: " + eventFilter.getEventType());
}
} else {
return findEvents(tenantId, entityId, eventFilter.getEventType().name(), pageLink);
}
}
private PageData<Event> findEventByFilter(UUID tenantId, EntityId entityId, DebugEvent eventFilter, TimePageLink pageLink) {
return DaoUtil.toPageData(
eventRepository.findDebugRuleNodeEvents(
tenantId,
entityId.getId(),
entityId.getEntityType().name(),
eventFilter.getEventType().name(),
notNull(pageLink.getStartTime()),
notNull(pageLink.getEndTime()),
eventFilter.getMsgDirectionType(),
eventFilter.getServer(),
eventFilter.getEntityName(),
eventFilter.getRelationType(),
eventFilter.getEntityId(),
eventFilter.getMsgType(),
eventFilter.isError(),
eventFilter.getError(),
eventFilter.getDataSearch(),
eventFilter.getMetadataSearch(),
DaoUtil.toPageable(pageLink)));
}
private PageData<Event> findEventByFilter(UUID tenantId, EntityId entityId, ErrorEventFilter eventFilter, TimePageLink pageLink) {
return DaoUtil.toPageData(
eventRepository.findErrorEvents(
tenantId,
entityId.getId(),
entityId.getEntityType().name(),
notNull(pageLink.getStartTime()),
notNull(pageLink.getEndTime()),
eventFilter.getServer(),
eventFilter.getMethod(),
eventFilter.getError(),
DaoUtil.toPageable(pageLink))
);
}
private PageData<Event> findEventByFilter(UUID tenantId, EntityId entityId, LifeCycleEventFilter eventFilter, TimePageLink pageLink) {
boolean statusFilterEnabled = !StringUtils.isEmpty(eventFilter.getStatus());
boolean statusFilter = statusFilterEnabled && eventFilter.getStatus().equalsIgnoreCase("Success");
return DaoUtil.toPageData(
eventRepository.findLifeCycleEvents(
tenantId,
entityId.getId(),
entityId.getEntityType().name(),
notNull(pageLink.getStartTime()),
notNull(pageLink.getEndTime()),
eventFilter.getServer(),
eventFilter.getEvent(),
statusFilterEnabled,
statusFilter,
eventFilter.getError(),
DaoUtil.toPageable(pageLink))
);
}
private PageData<Event> findEventByFilter(UUID tenantId, EntityId entityId, StatisticsEventFilter eventFilter, TimePageLink pageLink) {
return DaoUtil.toPageData(
eventRepository.findStatisticsEvents(
tenantId,
entityId.getId(),
entityId.getEntityType().name(),
notNull(pageLink.getStartTime()),
notNull(pageLink.getEndTime()),
eventFilter.getServer(),
notNull(eventFilter.getMessagesProcessed()),
notNull(eventFilter.getErrorsOccurred()),
DaoUtil.toPageable(pageLink))
);
}
@Override
public List<Event> findLatestEvents(UUID tenantId, EntityId entityId, String eventType, int limit) {
List<EventEntity> latest = eventRepository.findLatestByTenantIdAndEntityTypeAndEntityIdAndEventType(
@ -177,4 +275,12 @@ public class JpaBaseEventDao extends JpaAbstractDao<EventEntity, Event> implemen
return Optional.of(DaoUtil.getData(eventInsertRepository.saveOrUpdate(entity)));
}
private long notNull(Long value) {
return value != null ? value : 0;
}
private int notNull(Integer value) {
return value != null ? value : 0;
}
}

8
ui-ngx/src/app/core/http/event.service.ts

@ -21,7 +21,7 @@ import { HttpClient } from '@angular/common/http';
import { TimePageLink } from '@shared/models/page/page-link';
import { PageData } from '@shared/models/page/page-data';
import { EntityId } from '@shared/models/id/entity-id';
import { DebugEventType, Event, EventType } from '@shared/models/event.models';
import { DebugEventType, Event, EventType, FilterEventBody } from '@shared/models/event.models';
@Injectable({
providedIn: 'root'
@ -39,4 +39,10 @@ export class EventService {
defaultHttpOptionsFromConfig(config));
}
public getFilterEvents(entityId: EntityId, eventType: EventType | DebugEventType, tenantId: string,
filters: FilterEventBody, pageLink: TimePageLink, config?: RequestConfig): Observable<PageData<Event>> {
return this.http.post<PageData<Event>>(`/api/events/${entityId.entityType}/${entityId.id}` +
`${pageLink.toQuery()}&tenantId=${tenantId}`, {...filters, eventType}, defaultHttpOptionsFromConfig(config));
}
}

64
ui-ngx/src/app/modules/home/components/event/event-filter-panel.component.html

@ -0,0 +1,64 @@
<!--
Copyright © 2016-2021 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.
-->
<form fxLayout="column" class="mat-content mat-padding" [formGroup]="eventFilterFormGroup" (ngSubmit)="update()">
<ng-container *ngFor="let column of showColumns">
<ng-container [ngSwitch]="column.key">
<ng-template [ngSwitchCase]="isSelector(column.key)">
<mat-form-field>
<mat-label>{{ column.title | translate}}</mat-label>
<mat-select [formControlName]="column.key">
<mat-option [value]="">{{ 'event.all-events' | translate}}</mat-option>
<mat-option *ngFor="let value of selectorValues(column.key)" [value]="value">
{{ value }}
</mat-option>
</mat-select>
</mat-form-field>
</ng-template>
<ng-template [ngSwitchCase]="'isError'">
<tb-checkbox formControlName="isError" [falseValue]="''">
{{ 'event.has-error' | translate }}
</tb-checkbox>
</ng-template>
<ng-template [ngSwitchCase]="'error'">
<mat-form-field fxHide [fxShow]="showErrorMsgFields()">
<mat-label>{{ column.title | translate}}</mat-label>
<input matInput type="text" name="errorSearchText" formControlName="error">
</mat-form-field>
</ng-template>
<ng-container *ngSwitchDefault>
<mat-form-field>
<mat-label>{{ column.title | translate}}</mat-label>
<input matInput type="text" [name]="column.key" [formControlName]="column.key">
</mat-form-field>
</ng-container>
</ng-container>
</ng-container>
<div fxLayout="row" class="tb-panel-actions" fxLayoutAlign="end center">
<button type="button"
mat-button
(click)="cancel()">
{{ 'action.cancel' | translate }}
</button>
<button type="submit"
mat-raised-button
color="primary"
[disabled]="eventFilterFormGroup.invalid || !eventFilterFormGroup.dirty">
{{ 'action.update' | translate }}
</button>
</div>
</form>

35
ui-ngx/src/app/modules/home/components/event/event-filter-panel.component.scss

@ -0,0 +1,35 @@
/**
* Copyright © 2016-2021 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.
*/
:host {
width: 100%;
min-width: 300px;
overflow: auto;
background: #fff;
border-radius: 4px;
box-shadow:
0 7px 8px -4px rgba(0, 0, 0, .2),
0 13px 19px 2px rgba(0, 0, 0, .14),
0 5px 24px 4px rgba(0, 0, 0, .12);
.mat-content {
overflow: hidden;
background-color: #fff;
}
.mat-padding {
padding: 16px;
}
}

101
ui-ngx/src/app/modules/home/components/event/event-filter-panel.component.ts

@ -0,0 +1,101 @@
///
/// Copyright © 2016-2021 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.
///
import { Component, Inject, InjectionToken } from '@angular/core';
import { FormBuilder, FormGroup } from '@angular/forms';
import { OverlayRef } from '@angular/cdk/overlay';
import { EntityType } from '@shared/models/entity-type.models';
import { FilterEventBody } from '@shared/models/event.models';
import { deepTrim } from '@core/utils';
export const EVENT_FILTER_PANEL_DATA = new InjectionToken<any>('AlarmFilterPanelData');
export interface EventFilterPanelData {
filterParams: FilterEventBody;
columns: Array<FilterEntityColumn>;
}
export interface FilterEntityColumn {
key: string;
title: string;
}
@Component({
selector: 'tb-event-filter-panel',
templateUrl: './event-filter-panel.component.html',
styleUrls: ['./event-filter-panel.component.scss']
})
export class EventFilterPanelComponent {
eventFilterFormGroup: FormGroup;
result: EventFilterPanelData;
private conditionError = false;
private msgDirectionTypes = ['IN', 'OUT'];
private statusTypes = ['Success', 'Failure'];
private entityTypes = Object.keys(EntityType);
showColumns: FilterEntityColumn[] = [];
constructor(@Inject(EVENT_FILTER_PANEL_DATA)
public data: EventFilterPanelData,
public overlayRef: OverlayRef,
private fb: FormBuilder) {
this.eventFilterFormGroup = this.fb.group({});
this.data.columns.forEach((column) => {
this.showColumns.push(column);
this.eventFilterFormGroup.addControl(column.key, this.fb.control(this.data.filterParams[column.key] || ''));
if (column.key === 'isError') {
this.conditionError = true;
}
});
}
isSelector(key: string): string {
return ['msgDirectionType', 'status', 'entityName'].includes(key) ? key : '';
}
selectorValues(key: string): string[] {
switch (key) {
case 'msgDirectionType':
return this.msgDirectionTypes;
case 'status':
return this.statusTypes;
case 'entityName':
return this.entityTypes;
}
}
update() {
const filter = deepTrim(Object.fromEntries(Object.entries(this.eventFilterFormGroup.value).filter(([_, v]) => v !== '')));
this.result = {
filterParams: filter,
columns: this.data.columns
};
this.overlayRef.dispose();
}
showErrorMsgFields() {
return !this.conditionError || this.eventFilterFormGroup.get('isError').value !== '';
}
cancel() {
this.overlayRef.dispose();
}
}

122
ui-ngx/src/app/modules/home/components/event/event-table-config.ts

@ -20,7 +20,7 @@ import {
EntityTableColumn,
EntityTableConfig
} from '@home/models/entity/entities-table-config.models';
import { DebugEventType, Event, EventType } from '@shared/models/event.models';
import { DebugEventType, Event, EventType, FilterEventBody } from '@shared/models/event.models';
import { TimePageLink } from '@shared/models/page/page-link';
import { TranslateService } from '@ngx-translate/core';
import { DatePipe } from '@angular/common';
@ -38,16 +38,29 @@ import {
EventContentDialogComponent,
EventContentDialogData
} from '@home/components/event/event-content-dialog.component';
import { sortObjectKeys } from '@core/utils';
import { isEqual, sortObjectKeys } from '@core/utils';
import { ConnectedPosition, Overlay, OverlayConfig, OverlayRef } from '@angular/cdk/overlay';
import { ChangeDetectorRef, Injector, StaticProvider, ViewContainerRef } from '@angular/core';
import { ComponentPortal } from '@angular/cdk/portal';
import {
EVENT_FILTER_PANEL_DATA,
EventFilterPanelComponent,
EventFilterPanelData,
FilterEntityColumn
} from '@home/components/event/event-filter-panel.component';
export class EventTableConfig extends EntityTableConfig<Event, TimePageLink> {
eventTypeValue: EventType | DebugEventType;
private filterParams: FilterEventBody = {};
private filterColumns: FilterEntityColumn[] = [];
set eventType(eventType: EventType | DebugEventType) {
if (this.eventTypeValue !== eventType) {
this.eventTypeValue = eventType;
this.updateColumns(true);
this.updateFilterColumns();
}
}
@ -66,7 +79,10 @@ export class EventTableConfig extends EntityTableConfig<Event, TimePageLink> {
public tenantId: string,
private defaultEventType: EventType | DebugEventType,
private disabledEventTypes: Array<EventType | DebugEventType> = null,
private debugEventTypes: Array<DebugEventType> = null) {
private debugEventTypes: Array<DebugEventType> = null,
private overlay: Overlay,
private viewContainerRef: ViewContainerRef,
private cd: ChangeDetectorRef) {
super();
this.loadDataOnInit = false;
this.tableTitle = '';
@ -101,10 +117,20 @@ export class EventTableConfig extends EntityTableConfig<Event, TimePageLink> {
this.defaultSortOrder = {property: 'createdTime', direction: Direction.DESC};
this.updateColumns();
this.updateFilterColumns();
this.headerActionDescriptors.push({
name: this.translate.instant('event.events-filter'),
icon: 'filter_list',
isEnabled: () => true,
onAction: ($event) => {
this.editEventFilter($event);
}
});
}
fetchEvents(pageLink: TimePageLink): Observable<PageData<Event>> {
return this.eventService.getEvents(this.entityId, this.eventType, this.tenantId, pageLink);
return this.eventService.getFilterEvents(this.entityId, this.eventType, this.tenantId, this.filterParams, pageLink);
}
updateColumns(updateTableColumns: boolean = false): void {
@ -169,7 +195,7 @@ export class EventTableConfig extends EntityTableConfig<Event, TimePageLink> {
}), false, key => ({
padding: '0 12px 0 0'
})),
new EntityTableColumn<Event>('entity', 'event.entity', '100px',
new EntityTableColumn<Event>('entityName', 'event.entity-type', '100px',
(entity) => entity.body.entityName, entity => ({
padding: '0 12px 0 0',
}), false, key => ({
@ -249,5 +275,91 @@ export class EventTableConfig extends EntityTableConfig<Event, TimePageLink> {
}
});
}
private updateFilterColumns() {
this.filterParams = {};
this.filterColumns = [{key: 'server', title: 'event.server'}];
switch (this.eventType) {
case EventType.ERROR:
this.filterColumns.push(
{key: 'method', title: 'event.method'},
{key: 'error', title: 'event.error'}
);
break;
case EventType.LC_EVENT:
this.filterColumns.push(
{key: 'method', title: 'event.event'},
{key: 'status', title: 'event.status'},
{key: 'error', title: 'event.error'}
);
break;
case EventType.STATS:
this.filterColumns.push(
{key: 'messagesProcessed', title: 'event.messages-processed'},
{key: 'errorsOccurred', title: 'event.errors-occurred'}
);
break;
case DebugEventType.DEBUG_RULE_NODE:
case DebugEventType.DEBUG_RULE_CHAIN:
this.filterColumns.push(
{key: 'msgDirectionType', title: 'event.type'},
{key: 'entityId', title: 'event.entity-id'},
{key: 'entityName', title: 'event.entity-type'},
{key: 'msgType', title: 'event.message-type'},
{key: 'relationType', title: 'event.relation-type'},
{key: 'dataSearch', title: 'event.data'},
{key: 'metadataSearch', title: 'event.metadata'},
{key: 'isError', title: 'event.error'},
{key: 'error', title: 'event.error'}
);
break;
}
}
private editEventFilter($event: MouseEvent) {
if ($event) {
$event.stopPropagation();
}
const target = $event.target || $event.srcElement || $event.currentTarget;
const config = new OverlayConfig();
config.backdropClass = 'cdk-overlay-transparent-backdrop';
config.hasBackdrop = true;
const connectedPosition: ConnectedPosition = {
originX: 'end',
originY: 'bottom',
overlayX: 'end',
overlayY: 'top'
};
config.positionStrategy = this.overlay.position().flexibleConnectedTo(target as HTMLElement)
.withPositions([connectedPosition]);
const overlayRef = this.overlay.create(config);
overlayRef.backdropClick().subscribe(() => {
overlayRef.dispose();
});
const providers: StaticProvider[] = [
{
provide: EVENT_FILTER_PANEL_DATA,
useValue: {
columns: this.filterColumns,
filterParams: this.filterParams
} as EventFilterPanelData
},
{
provide: OverlayRef,
useValue: overlayRef
}
];
const injector = Injector.create({parent: this.viewContainerRef.injector, providers});
const componentRef = overlayRef.attach(new ComponentPortal(EventFilterPanelComponent,
this.viewContainerRef, injector));
componentRef.onDestroy(() => {
if (componentRef.instance.result && !isEqual(this.filterParams, componentRef.instance.result.filterParams)) {
this.filterParams = componentRef.instance.result.filterParams;
this.table.updateData();
}
});
this.cd.detectChanges();
}
}

13
ui-ngx/src/app/modules/home/components/event/event-table.component.ts

@ -14,7 +14,7 @@
/// limitations under the License.
///
import { Component, Input, OnInit, ViewChild } from '@angular/core';
import { ChangeDetectorRef, Component, Input, OnInit, ViewChild, ViewContainerRef } from '@angular/core';
import { TranslateService } from '@ngx-translate/core';
import { DatePipe } from '@angular/common';
import { MatDialog } from '@angular/material/dialog';
@ -24,6 +24,7 @@ import { EventTableConfig } from './event-table-config';
import { EventService } from '@core/http/event.service';
import { DialogService } from '@core/services/dialog.service';
import { DebugEventType, EventType } from '@shared/models/event.models';
import { Overlay } from '@angular/cdk/overlay';
@Component({
selector: 'tb-event-table',
@ -80,7 +81,10 @@ export class EventTableComponent implements OnInit {
private dialogService: DialogService,
private translate: TranslateService,
private datePipe: DatePipe,
private dialog: MatDialog) {
private dialog: MatDialog,
private overlay: Overlay,
private viewContainerRef: ViewContainerRef,
private cd: ChangeDetectorRef) {
}
ngOnInit() {
@ -95,7 +99,10 @@ export class EventTableComponent implements OnInit {
this.tenantId,
this.defaultEventType,
this.disabledEventTypes,
this.debugEventTypes
this.debugEventTypes,
this.overlay,
this.viewContainerRef,
this.cd
);
}

2
ui-ngx/src/app/modules/home/components/home-components.module.ts

@ -25,6 +25,7 @@ import { AuditLogDetailsDialogComponent } from '@home/components/audit-log/audit
import { AuditLogTableComponent } from '@home/components/audit-log/audit-log-table.component';
import { EventTableHeaderComponent } from '@home/components/event/event-table-header.component';
import { EventTableComponent } from '@home/components/event/event-table.component';
import { EventFilterPanelComponent } from '@home/components/event/event-filter-panel.component';
import { RelationTableComponent } from '@home/components/relation/relation-table.component';
import { RelationDialogComponent } from '@home/components/relation/relation-dialog.component';
import { AlarmTableHeaderComponent } from '@home/components/alarm/alarm-table-header.component';
@ -149,6 +150,7 @@ import { DisplayWidgetTypesPanelComponent } from '@home/components/dashboard-pag
EventContentDialogComponent,
EventTableHeaderComponent,
EventTableComponent,
EventFilterPanelComponent,
EdgeDownlinkTableHeaderComponent,
EdgeDownlinkTableComponent,
RelationTableComponent,

34
ui-ngx/src/app/shared/models/event.models.ts

@ -19,6 +19,7 @@ import { TenantId } from '@shared/models/id/tenant-id';
import { EntityId } from '@shared/models/id/entity-id';
import { EventId } from './id/event-id';
import { ContentType } from '@shared/models/constants';
import { EntityType } from '@shared/models/entity-type.models';
export enum EventType {
ERROR = 'ERROR',
@ -83,3 +84,36 @@ export interface Event extends BaseData<EventId> {
uid: string;
body: EventBody;
}
export interface BaseFilterEventBody {
server?: string;
}
export interface ErrorFilterEventBody extends BaseFilterEventBody {
method?: string;
}
export interface LcFilterEventEventBody extends BaseFilterEventBody {
method?: string;
status?: string;
isError?: boolean;
}
export interface StatsFilterEventBody extends BaseFilterEventBody {
messagesProcessed?: string;
errorsOccurred?: string;
}
export interface DebugFilterRuleNodeEventBody extends BaseFilterEventBody {
msgDirectionType?: string;
entityId?: string;
entityName?: EntityType;
msgId?: string;
msgType?: string;
relationType?: string;
dataSearch?: string;
metadataSearch?: string;
isError?: boolean;
}
export type FilterEventBody = ErrorFilterEventBody & LcFilterEventEventBody & StatsFilterEventBody & DebugFilterRuleNodeEventBody;

5
ui-ngx/src/assets/locale/locale.constant-cs_CZ.json

@ -1332,7 +1332,6 @@
"body": "Tělo",
"method": "Metoda",
"type": "Typ",
"entity": "Entita",
"message-id": "Id zprávy",
"message-type": "Typ zprávy",
"data-type": "Typ dat",
@ -1344,7 +1343,9 @@
"success": "Úspěch",
"failed": "Neúspěch",
"messages-processed": "Zpracované zprávy",
"errors-occurred": "Vyskytly se chyby"
"errors-occurred": "Vyskytly se chyby",
"all-events": "Vše",
"entity-type": "Typ entity"
},
"extension": {
"extensions": "Rozšíření",

5
ui-ngx/src/assets/locale/locale.constant-de_DE.json

@ -1094,7 +1094,6 @@
"body": "Inhalt",
"method": "Methode",
"type": "Typ",
"entity": "Entität",
"message-id": "Nachrichten-Id",
"message-type": "Nachrichten-Typ",
"data-type": "Datentyp",
@ -1106,7 +1105,9 @@
"success": "Erfolg",
"failed": "Fehlgeschlagen",
"messages-processed": "Nachrichten verarbeitet",
"errors-occurred": "Fehler aufgetreten"
"errors-occurred": "Fehler aufgetreten",
"all-events": "Alle",
"entity-type": "Entitätstyp"
},
"extension": {
"extensions": "Erweiterungen",

5
ui-ngx/src/assets/locale/locale.constant-el_GR.json

@ -1247,7 +1247,6 @@
"out": "Έξοδος",
"metadata": "Μεταδεδομένα",
"message": "Μήνυμα",
"entity": "Οντότητα",
"message-id": "ID Μηνύματος",
"message-type": "Τύπος Μηνύματος",
"data-type": "Τύπος Δεδομένων",
@ -1258,7 +1257,9 @@
"success": "Επιτυχία",
"failed": "Απέτυχε",
"messages-processed": "Επεξεργασμένα μηνύματα",
"errors-occurred": "Παρουσιάστηκαν σφάλματα"
"errors-occurred": "Παρουσιάστηκαν σφάλματα",
"all-events": "Όλοι",
"entity-type": "Τύπος οντοτήτων"
},
"extension": {
"extensions": "Επεκτάσεις",

8
ui-ngx/src/assets/locale/locale.constant-en_US.json

@ -1616,6 +1616,7 @@
},
"event": {
"event-type": "Event type",
"events-filter": "Events Filter",
"type-error": "Error",
"type-lc-event": "Lifecycle event",
"type-stats": "Statistics",
@ -1629,7 +1630,6 @@
"body": "Body",
"method": "Method",
"type": "Type",
"entity": "Entity",
"message-id": "Message Id",
"message-type": "Message Type",
"data-type": "Data Type",
@ -1641,7 +1641,11 @@
"success": "Success",
"failed": "Failed",
"messages-processed": "Messages processed",
"errors-occurred": "Errors occurred"
"errors-occurred": "Errors occurred",
"all-events": "All",
"has-error": "Has error",
"entity-id": "Entity Id",
"entity-type": "Entity type"
},
"extension": {
"extensions": "Extensions",

5
ui-ngx/src/assets/locale/locale.constant-es_ES.json

@ -1521,7 +1521,6 @@
"body": "Cuerpo",
"method": "Método",
"type": "Tipo",
"entity": "Entidad",
"message-id": "Id Mensaje",
"message-type": "Tipo Mensaje",
"data-type": "Tipo de Datos",
@ -1533,7 +1532,9 @@
"success": "Éxito",
"failed": "Fallo",
"messages-processed": "Mensajes procesados",
"errors-occurred": "Ocurrieron errores"
"errors-occurred": "Ocurrieron errores",
"all-events": "Todos",
"entity-type": "Tipo de entidad"
},
"extension": {
"extensions": "Extensiones",

5
ui-ngx/src/assets/locale/locale.constant-fa_IR.json

@ -881,7 +881,6 @@
"body": "بدنه",
"method": "روش",
"type": "نوع",
"entity": "موجودي",
"message-id": "پيام ID",
"message-type": "نوع پيام",
"data-type": "نوع داده",
@ -893,7 +892,9 @@
"success": "موفقيت",
"failed": "عدم موفقيت",
"messages-processed": "پيام پردازش شد",
"errors-occurred": "خطاها رخ دادند"
"errors-occurred": "خطاها رخ دادند",
"all-events": "همه",
"entity-type": "نوع موجودي"
},
"extension": {
"extensions": "دنباله ها",

5
ui-ngx/src/assets/locale/locale.constant-fr_FR.json

@ -1115,7 +1115,6 @@
"body": "Corps",
"data": "Données",
"data-type": "Type de données",
"entity": "Entité",
"error": "erreur",
"type-edge-event": "Downlink",
"errors-occurred": "Des erreurs sont survenues",
@ -1138,7 +1137,9 @@
"type-debug-rule-node": "Debug",
"type-error": "Erreur",
"type-lc-event": "Evénement du cycle de vie",
"type-stats": "Statistiques"
"type-stats": "Statistiques",
"all-events": "Tout",
"entity-type": "Type d'entité"
},
"extension": {
"add": "Ajouter une extension",

5
ui-ngx/src/assets/locale/locale.constant-it_IT.json

@ -915,7 +915,6 @@
"body": "Body",
"method": "Metodo",
"type": "Tipo",
"entity": "Entità",
"message-id": "Id Messaggio",
"message-type": "Tipo Messaggio",
"data-type": "Tipo di dato",
@ -927,7 +926,9 @@
"success": "Success",
"failed": "Failed",
"messages-processed": "Messaggi elaborati",
"errors-occurred": "Si sono verificati degli errori"
"errors-occurred": "Si sono verificati degli errori",
"all-events": "Tutte",
"entity-type": "Tipo entità"
},
"extension": {
"extensions": "Estensioni",

5
ui-ngx/src/assets/locale/locale.constant-ja_JA.json

@ -768,7 +768,6 @@
"body": "体",
"method": "方法",
"type": "タイプ",
"entity": "エンティティ",
"message-id": "メッセージID",
"message-type": "メッセージタイプ",
"data-type": "データ・タイプ",
@ -780,7 +779,9 @@
"success": "成功",
"failed": "失敗",
"messages-processed": "処理されたメッセージ",
"errors-occurred": "エラーが発生しました"
"errors-occurred": "エラーが発生しました",
"all-events": "すべて",
"entity-type": "エンティティタイプ"
},
"extension": {
"extensions": "拡張機能",

5
ui-ngx/src/assets/locale/locale.constant-ka_GE.json

@ -959,7 +959,6 @@
"body": "სხეული",
"method": "მეთოდი",
"type": "ტიპი",
"entity": "ობიექტი",
"message-id": "მესიჯის-ID",
"message-type": "მესიჯის ტიპი",
"data-type": "მონაცემთა ტიპი",
@ -971,7 +970,9 @@
"success": "წარმატება",
"failed": "ვერ მოხერხდა",
"messages-processed": "შეტყობინებების დამუშავება",
"errors-occurred": "შეცდომები მოხდა"
"errors-occurred": "შეცდომები მოხდა",
"all-events": "ყველა",
"entity-type": "ობიექტის ტიპი"
},
"extension": {
"extensions": "დამატებითი აპი",

5
ui-ngx/src/assets/locale/locale.constant-ko_KR.json

@ -1329,7 +1329,6 @@
"body": "Body",
"method": "방법",
"type": "유형",
"entity": "개체",
"message-id": "메시지 ID",
"message-type": "메시지 유형",
"data-type": "데이터 유형",
@ -1341,7 +1340,9 @@
"success": "성공",
"failed": "실패",
"messages-processed": "처리된 메시지",
"errors-occurred": "오류가 발생했습니다"
"errors-occurred": "오류가 발생했습니다",
"all-events": "모두",
"entity-type": "개체 유형"
},
"extension": {
"extensions": "확장",

5
ui-ngx/src/assets/locale/locale.constant-lv_LV.json

@ -894,7 +894,6 @@
"body": "Galvenā daļa",
"method": "Metode",
"type": "Tips",
"entity": "Vienība",
"message-id": "Ziņojuma Id",
"message-type": "Ziņojuma tips",
"data-type": "Datu tips",
@ -906,7 +905,9 @@
"success": "Sekmīgi",
"failed": "Kļūda",
"messages-processed": "Ziņojumi apstrādāti",
"errors-occurred": "Kļūdas konstatētas"
"errors-occurred": "Kļūdas konstatētas",
"all-events": "Visi",
"entity-type": "Vienības tips"
},
"extension": {
"extensions": "Paplašinājumi",

5
ui-ngx/src/assets/locale/locale.constant-pt_BR.json

@ -997,7 +997,6 @@
"body": "Corpo",
"method": "Método",
"type": "Tipo",
"entity": "Entidade",
"message-id": "ID de mensagem",
"message-type": "Tipo de Mensagem",
"data-type": "Tipo de Dados",
@ -1009,7 +1008,9 @@
"success": "Êxito",
"failed": "Falhou",
"messages-processed": "Mensagens processadas",
"errors-occurred": "Erros"
"errors-occurred": "Erros",
"all-events": "Tudo",
"entity-type": "Tipo de entidade"
},
"extension": {
"extensions": "Extensões",

9
ui-ngx/src/assets/locale/locale.constant-ro_RO.json

@ -945,7 +945,6 @@
"body": "Corp",
"method": "Metodă",
"type": "Tip",
"entity": "Entitate",
"message-id": "ID Mesaj",
"message-type": "Tip Mesaj",
"data-type": "Tip Date",
@ -957,7 +956,9 @@
"success": "Succes",
"failed": "Eşuat",
"messages-processed": "Mesaje procesate",
"errors-occurred": "Au apărut erori"
"errors-occurred": "Au apărut erori",
"all-events": "Toate",
"entity-type": "Tip Entitate"
},
"extension": {
"extensions": "Extensii",
@ -1174,7 +1175,7 @@
"entity-field": "Câmp Entitate",
"access-token": "Token De Acces"
},
"stepper-text":{
"stepper-text": {
"select-file": "Selectează un fişier",
"configuration": "Importă configurație",
"column-type": "Selectează tipul de coloane",
@ -1796,4 +1797,4 @@
"language": {
"language": "Limba"
}
}
}

5
ui-ngx/src/assets/locale/locale.constant-ru_RU.json

@ -948,7 +948,6 @@
"body": "Тело",
"method": "Метод",
"type": "Тип",
"entity": "Объект",
"message-id": "ИД сообщения",
"message-type": "Тип сообщения",
"data-type": "Тип данных",
@ -960,7 +959,9 @@
"success": "Успех",
"failed": "Неудача",
"messages-processed": "Сообщения обработаны",
"errors-occurred": "Возникли ошибки"
"errors-occurred": "Возникли ошибки",
"all-events": "Все",
"entity-type": "Тип объекта"
},
"extension": {
"extensions": "Расширение",

5
ui-ngx/src/assets/locale/locale.constant-sl_SI.json

@ -1329,7 +1329,6 @@
"body": "Vsebina",
"method": "Metoda",
"type": "Vrsta",
"entity": "Entiteta",
"message-id": "ID sporočila",
"message-type": "Vrsta sporočila",
"data-type": "Vrsta podatkov",
@ -1341,7 +1340,9 @@
"success": "Uspeh",
"failed": "Ni uspelo",
"messages-processed": "Obdelana sporočila",
"errors-occurred": "Prišlo je do napak"
"errors-occurred": "Prišlo je do napak",
"all-events": "Vse",
"entity-type": "Vrsta entitete"
},
"extension": {
"extensions": "Razširitve",

5
ui-ngx/src/assets/locale/locale.constant-tr_TR.json

@ -915,7 +915,6 @@
"body": "İçerik //(Body)",
"method": "Yöntem",
"type": "Tür",
"entity": "Varlık",
"message-id": "Mesaj Kimliği",
"message-type": "Mesaj tipi",
"data-type": "Veri tipi",
@ -927,7 +926,9 @@
"success": "Başarı",
"failed": "Başarısız oldu",
"messages-processed": "Mesajlar işlendi",
"errors-occurred": "Hatalar oluştu"
"errors-occurred": "Hatalar oluştu",
"all-events": "Tümü",
"entity-type": "Öğe türü"
},
"extension": {
"extensions": "Uzantılar",

5
ui-ngx/src/assets/locale/locale.constant-uk_UA.json

@ -1201,7 +1201,6 @@
"out": "Out",
"metadata": "Метадані",
"message": "Повідомлення",
"entity": "Сутність",
"message-id": "Id повідомлення",
"message-type": "Тип повідомлення",
"data-type": "Тип даних",
@ -1212,7 +1211,9 @@
"success": "Успіх",
"failed": "Невдача",
"messages-processed": "Повідомлення опрацьовані",
"errors-occurred": "Виникли помилки"
"errors-occurred": "Виникли помилки",
"all-events": "Всі",
"entity-type": "Тип сутності"
},
"extension": {
"extensions": "Розширення",

5
ui-ngx/src/assets/locale/locale.constant-zh_CN.json

@ -1334,7 +1334,6 @@
"body": "整体",
"data": "数据",
"data-type": "数据类型",
"entity": "实体",
"error": "错误",
"errors-occurred": "错误发生",
"event": "事件",
@ -1356,7 +1355,9 @@
"type-debug-rule-node": "调试",
"type-error": "错误",
"type-lc-event": "生命周期事件",
"type-stats": "类型统计"
"type-stats": "类型统计",
"all-events": "全部",
"entity-type": "实体类型"
},
"extension": {
"add": "添加扩展",

5
ui-ngx/src/assets/locale/locale.constant-zh_TW.json

@ -865,7 +865,6 @@
"body": "整體",
"method": "方法",
"type": "類型",
"entity": "實體",
"message-id": "消息ID",
"message-type": "消息類型",
"data-type": "資料類型",
@ -877,7 +876,9 @@
"success": "成功",
"failed": "失敗",
"messages-processed": "消息處理",
"errors-occurred": "錯誤發生"
"errors-occurred": "錯誤發生",
"all-events": "所有",
"entity-type": "實體類型"
},
"extension": {
"extensions": "擴展",

Loading…
Cancel
Save