400 lines
15 KiB
TypeScript
Raw Normal View History

2019-08-22 18:44:48 +03:00
///
2021-01-11 13:42:16 +02:00
/// Copyright © 2016-2021 The Thingsboard Authors
2019-08-22 18:44:48 +03:00
///
/// 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 {
DateEntityTableColumn,
EntityActionTableColumn,
EntityTableColumn,
EntityTableConfig
} from '@home/models/entity/entities-table-config.models';
2021-11-08 19:21:26 +02:00
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';
import { MatDialog } from '@angular/material/dialog';
import { EntityId } from '@shared/models/id/entity-id';
import { EventService } from '@app/core/http/event.service';
import { EventTableHeaderComponent } from '@home/components/event/event-table-header.component';
import { EntityTypeResource } from '@shared/models/entity-type.models';
import { Observable } from 'rxjs';
import { PageData } from '@shared/models/page/page-data';
import { Direction } from '@shared/models/page/sort-order';
import { DialogService } from '@core/services/dialog.service';
import { ContentType } from '@shared/models/constants';
import {
EventContentDialogComponent,
EventContentDialogData
} from '@home/components/event/event-content-dialog.component';
2021-11-08 19:21:26 +02:00
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';
2019-08-22 18:44:48 +03:00
export class EventTableConfig extends EntityTableConfig<Event, TimePageLink> {
eventTypeValue: EventType | DebugEventType;
private filterParams: FilterEventBody = {};
private filterColumns: FilterEntityColumn[] = [];
2019-08-22 18:44:48 +03:00
set eventType(eventType: EventType | DebugEventType) {
if (this.eventTypeValue !== eventType) {
this.eventTypeValue = eventType;
this.updateColumns(true);
this.updateFilterColumns();
2019-08-22 18:44:48 +03:00
}
}
get eventType(): EventType | DebugEventType {
return this.eventTypeValue;
}
eventTypes: Array<EventType | DebugEventType>;
constructor(private eventService: EventService,
private dialogService: DialogService,
private translate: TranslateService,
private datePipe: DatePipe,
private dialog: MatDialog,
public entityId: EntityId,
public tenantId: string,
private defaultEventType: EventType | DebugEventType,
private disabledEventTypes: Array<EventType | DebugEventType> = null,
private debugEventTypes: Array<DebugEventType> = null,
private overlay: Overlay,
private viewContainerRef: ViewContainerRef,
2021-11-08 12:39:44 +02:00
private cd: ChangeDetectorRef) {
2019-08-22 18:44:48 +03:00
super();
this.loadDataOnInit = false;
this.tableTitle = '';
this.useTimePageLink = true;
this.detailsPanelEnabled = false;
this.selectionEnabled = false;
this.searchEnabled = false;
this.addEnabled = false;
this.entitiesDeleteEnabled = false;
this.persistentPageLinkMode = false;
2019-08-22 18:44:48 +03:00
this.headerComponent = EventTableHeaderComponent;
this.eventTypes = Object.keys(EventType).map(type => EventType[type]);
if (disabledEventTypes && disabledEventTypes.length) {
this.eventTypes = this.eventTypes.filter(type => disabledEventTypes.indexOf(type) === -1);
}
if (debugEventTypes && debugEventTypes.length) {
this.eventTypes = [...this.eventTypes, ...debugEventTypes];
}
this.eventTypeValue = defaultEventType;
this.entityTranslations = {
noEntities: 'event.no-events-prompt'
};
this.entityResources = {
2020-04-07 17:06:04 +03:00
} as EntityTypeResource<Event>;
2019-08-22 18:44:48 +03:00
this.entitiesFetchFunction = pageLink => this.fetchEvents(pageLink);
this.defaultSortOrder = {property: 'createdTime', direction: Direction.DESC};
this.updateColumns();
this.updateFilterColumns();
this.headerActionDescriptors.push({
name: this.translate.instant('event.clear-filter'),
icon: 'mdi:filter-variant-remove',
isMdiIcon: true,
isEnabled: () => !isEqual(this.filterParams, {}),
onAction: ($event) => {
this.clearFiter($event);
}
},
{
name: this.translate.instant('event.events-filter'),
icon: 'filter_list',
isEnabled: () => true,
onAction: ($event) => {
this.editEventFilter($event);
}
},
{
name: this.translate.instant('event.clean-events'),
icon: 'delete',
isEnabled: () => true,
2021-11-08 19:21:26 +02:00
onAction: ($event) => {
this.eventService.clearEvents(this.entityId, this.eventType, this.filterParams, this.tenantId, this.table.pageLink as TimePageLink).subscribe(
2021-11-08 14:19:44 +02:00
() => {
this.table.paginator.pageIndex = 0;
this.table.updateData();
}
);
}
});
2019-08-22 18:44:48 +03:00
}
fetchEvents(pageLink: TimePageLink): Observable<PageData<Event>> {
return this.eventService.getFilterEvents(this.entityId, this.eventType, this.tenantId, this.filterParams, pageLink);
2019-08-22 18:44:48 +03:00
}
updateColumns(updateTableColumns: boolean = false): void {
this.columns = [];
this.columns.push(
new DateEntityTableColumn<Event>('createdTime', 'event.event-time', this.datePipe, '120px'),
2020-12-22 19:21:06 +02:00
new EntityTableColumn<Event>('server', 'event.server', '100px',
(entity) => entity.body.server, entity => ({}), false));
2019-08-22 18:44:48 +03:00
switch (this.eventType) {
case EventType.ERROR:
this.columns.push(
new EntityTableColumn<Event>('method', 'event.method', '100%',
(entity) => entity.body.method, entity => ({}), false),
new EntityActionTableColumn<Event>('error', 'event.error',
{
name: this.translate.instant('action.view'),
icon: 'more_horiz',
isEnabled: (entity) => entity.body.error && entity.body.error.length > 0,
onAction: ($event, entity) => this.showContent($event, entity.body.error, 'event.error')
},
'100px')
);
break;
case EventType.LC_EVENT:
this.columns.push(
new EntityTableColumn<Event>('method', 'event.event', '100%',
(entity) => entity.body.event, entity => ({}), false),
new EntityTableColumn<Event>('status', 'event.status', '100%',
(entity) =>
this.translate.instant(entity.body.success ? 'event.success' : 'event.failed'), entity => ({}), false),
new EntityActionTableColumn<Event>('error', 'event.error',
{
name: this.translate.instant('action.view'),
icon: 'more_horiz',
isEnabled: (entity) => entity.body.error && entity.body.error.length > 0,
onAction: ($event, entity) => this.showContent($event, entity.body.error, 'event.error')
},
'100px')
);
break;
case EventType.STATS:
this.columns.push(
new EntityTableColumn<Event>('messagesProcessed', 'event.messages-processed', '50%',
2019-08-22 18:44:48 +03:00
(entity) => entity.body.messagesProcessed + '',
() => ({}),
2019-08-22 18:44:48 +03:00
false,
() => ({}), () => undefined, true),
new EntityTableColumn<Event>('errorsOccurred', 'event.errors-occurred', '50%',
2019-08-22 18:44:48 +03:00
(entity) => entity.body.errorsOccurred + '',
() => ({}),
2019-08-22 18:44:48 +03:00
false,
() => ({}), () => undefined, true)
2019-08-22 18:44:48 +03:00
);
break;
case DebugEventType.DEBUG_RULE_NODE:
case DebugEventType.DEBUG_RULE_CHAIN:
this.columns[0].width = '100px';
2019-08-22 18:44:48 +03:00
this.columns.push(
new EntityTableColumn<Event>('type', 'event.type', '40px',
(entity) => entity.body.type, entity => ({
padding: '0 12px 0 0',
}), false, key => ({
padding: '0 12px 0 0'
})),
new EntityTableColumn<Event>('entityName', 'event.entity-type', '100px',
(entity) => entity.body.entityName, entity => ({
padding: '0 12px 0 0',
}), false, key => ({
padding: '0 12px 0 0'
})),
new EntityTableColumn<Event>('msgId', 'event.message-id', '100px',
(entity) => entity.body.msgId, entity => ({
whiteSpace: 'nowrap',
padding: '0 12px 0 0'
}), false, key => ({
padding: '0 12px 0 0'
}),
entity => entity.body.msgId),
new EntityTableColumn<Event>('msgType', 'event.message-type', '100px',
(entity) => entity.body.msgType, entity => ({
whiteSpace: 'nowrap',
padding: '0 12px 0 0'
}), false, key => ({
padding: '0 12px 0 0'
}),
entity => entity.body.msgType),
new EntityTableColumn<Event>('relationType', 'event.relation-type', '100px',
(entity) => entity.body.relationType, entity => ({padding: '0 12px 0 0', }), false, key => ({
padding: '0 12px 0 0'
})),
2019-08-22 18:44:48 +03:00
new EntityActionTableColumn<Event>('data', 'event.data',
{
name: this.translate.instant('action.view'),
icon: 'more_horiz',
isEnabled: (entity) => entity.body.data ? entity.body.data.length > 0 : false,
2019-08-22 18:44:48 +03:00
onAction: ($event, entity) => this.showContent($event, entity.body.data,
'event.data', entity.body.dataType)
},
'40px'),
2019-08-22 18:44:48 +03:00
new EntityActionTableColumn<Event>('metadata', 'event.metadata',
{
name: this.translate.instant('action.view'),
icon: 'more_horiz',
isEnabled: (entity) => entity.body.metadata ? entity.body.metadata.length > 0 : false,
2019-08-22 18:44:48 +03:00
onAction: ($event, entity) => this.showContent($event, entity.body.metadata,
2020-08-11 17:43:19 +03:00
'event.metadata', ContentType.JSON, true)
2019-08-22 18:44:48 +03:00
},
'40px'),
2019-08-22 18:44:48 +03:00
new EntityActionTableColumn<Event>('error', 'event.error',
{
name: this.translate.instant('action.view'),
icon: 'more_horiz',
isEnabled: (entity) => entity.body.error && entity.body.error.length > 0,
onAction: ($event, entity) => this.showContent($event, entity.body.error,
'event.error')
},
'40px')
2019-08-22 18:44:48 +03:00
);
break;
}
if (updateTableColumns) {
this.table.columnsUpdated(true);
}
}
2020-08-11 17:43:19 +03:00
showContent($event: MouseEvent, content: string, title: string, contentType: ContentType = null, sortKeys = false): void {
2019-08-22 18:44:48 +03:00
if ($event) {
$event.stopPropagation();
}
2020-08-11 17:43:19 +03:00
if (contentType === ContentType.JSON && sortKeys) {
try {
content = JSON.stringify(sortObjectKeys(JSON.parse(content)));
} catch (e) {}
2020-08-11 13:43:37 +03:00
}
this.dialog.open<EventContentDialogComponent, EventContentDialogData>(EventContentDialogComponent, {
disableClose: true,
panelClass: ['tb-dialog', 'tb-fullscreen-dialog'],
data: {
2020-08-11 17:43:19 +03:00
content,
title,
contentType
}
});
2019-08-22 18:44:48 +03:00
}
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'},
2021-10-14 21:30:50 +03:00
{key: 'errorStr', title: 'event.error'}
);
break;
case EventType.LC_EVENT:
this.filterColumns.push(
{key: 'event', title: 'event.event'},
{key: 'status', title: 'event.status'},
2021-10-14 21:30:50 +03:00
{key: 'errorStr', title: 'event.error'}
);
break;
case EventType.STATS:
this.filterColumns.push(
{key: 'messagesProcessed', title: 'event.min-messages-processed'},
{key: 'errorsOccurred', title: 'event.min-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'},
2021-10-14 21:30:50 +03:00
{key: 'errorStr', title: 'event.error'}
);
break;
}
}
private clearFiter($event) {
if ($event) {
$event.stopPropagation();
}
this.filterParams = {};
this.table.paginator.pageIndex = 0;
this.table.updateData();
}
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.paginator.pageIndex = 0;
this.table.updateData();
}
});
this.cd.detectChanges();
}
2019-08-22 18:44:48 +03:00
}
2020-10-23 15:18:34 +03:00