From 6e564de948ff6212b79834f50bb5a90bcaced557 Mon Sep 17 00:00:00 2001 From: Volodymyr Babak Date: Tue, 22 Dec 2020 19:21:06 +0200 Subject: [PATCH] Moved edge downlinks to a separate TAB --- ui-ngx/src/app/core/http/edge.service.ts | 7 +- ui-ngx/src/app/core/http/event.service.ts | 5 - .../edge/edge-downlink-table-config.ts | 253 ++++++++++++++++++ .../edge-downlink-table-header.component.html | 19 ++ .../edge-downlink-table-header.component.scss | 41 +++ .../edge-downlink-table-header.component.ts | 42 +++ .../edge/edge-downlink-table.component.html | 18 ++ .../edge/edge-downlink-table.component.scss | 22 ++ .../edge/edge-downlink-table.component.ts | 103 +++++++ .../components/event/event-table-config.ts | 164 ++---------- .../components/event/event-table.component.ts | 6 - .../home/components/home-components.module.ts | 3 + .../home/pages/edge/edge-tabs.component.html | 5 + .../modules/home/pages/edge/edge.component.ts | 1 + .../modules/home/pages/edge/edge.module.ts | 3 + ui-ngx/src/app/shared/models/edge.models.ts | 114 ++++++++ ui-ngx/src/app/shared/models/event.models.ts | 42 +-- .../assets/locale/locale.constant-en_US.json | 38 ++- 18 files changed, 684 insertions(+), 202 deletions(-) create mode 100644 ui-ngx/src/app/modules/home/components/edge/edge-downlink-table-config.ts create mode 100644 ui-ngx/src/app/modules/home/components/edge/edge-downlink-table-header.component.html create mode 100644 ui-ngx/src/app/modules/home/components/edge/edge-downlink-table-header.component.scss create mode 100644 ui-ngx/src/app/modules/home/components/edge/edge-downlink-table-header.component.ts create mode 100644 ui-ngx/src/app/modules/home/components/edge/edge-downlink-table.component.html create mode 100644 ui-ngx/src/app/modules/home/components/edge/edge-downlink-table.component.scss create mode 100644 ui-ngx/src/app/modules/home/components/edge/edge-downlink-table.component.ts diff --git a/ui-ngx/src/app/core/http/edge.service.ts b/ui-ngx/src/app/core/http/edge.service.ts index f02d09147b..f6335e1a3b 100644 --- a/ui-ngx/src/app/core/http/edge.service.ts +++ b/ui-ngx/src/app/core/http/edge.service.ts @@ -21,9 +21,8 @@ import { HttpClient } from '@angular/common/http'; import { PageLink, TimePageLink } from '@shared/models/page/page-link'; import { PageData } from '@shared/models/page/page-data'; import { EntitySubtype } from '@app/shared/models/entity-type.models'; -import { Edge, EdgeInfo, EdgeSearchQuery } from "@shared/models/edge.models"; +import { Edge, EdgeEvent, EdgeInfo, EdgeSearchQuery } from "@shared/models/edge.models"; import { EntityId } from "@shared/models/id/entity-id"; -import { Event } from "@shared/models/event.models"; @Injectable({ providedIn: 'root' }) @@ -92,8 +91,8 @@ export class EdgeService { } public getEdgeEvents(entityId: EntityId, pageLink: TimePageLink, - config?: RequestConfig): Observable> { - return this.http.get>(`/api/edge/${entityId.id}/events` + `${pageLink.toQuery()}`, + config?: RequestConfig): Observable> { + return this.http.get>(`/api/edge/${entityId.id}/events` + `${pageLink.toQuery()}`, defaultHttpOptionsFromConfig(config)); } } diff --git a/ui-ngx/src/app/core/http/event.service.ts b/ui-ngx/src/app/core/http/event.service.ts index fe5c064cb5..a3116c5ca8 100644 --- a/ui-ngx/src/app/core/http/event.service.ts +++ b/ui-ngx/src/app/core/http/event.service.ts @@ -39,9 +39,4 @@ export class EventService { defaultHttpOptionsFromConfig(config)); } - public getEdgeEvents(entityId: EntityId, pageLink: TimePageLink, - config?: RequestConfig): Observable> { - return this.http.get>(`/api/edge/${entityId.id}/events` + `${pageLink.toQuery()}`, - defaultHttpOptionsFromConfig(config)); - } } diff --git a/ui-ngx/src/app/modules/home/components/edge/edge-downlink-table-config.ts b/ui-ngx/src/app/modules/home/components/edge/edge-downlink-table-config.ts new file mode 100644 index 0000000000..9877d1c1f6 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/edge/edge-downlink-table-config.ts @@ -0,0 +1,253 @@ +/// +/// Copyright © 2016-2020 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 { + DateEntityTableColumn, + EntityActionTableColumn, + EntityTableColumn, + EntityTableConfig +} from '@home/models/entity/entities-table-config.models'; +import { + EdgeEvent, edgeEventActionTypeTranslations, + EdgeEventStatus, + edgeEventStatusColor, + EdgeEventType, + edgeEventTypeTranslations +} from '@shared/models/edge.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 { EntityTypeResource } from '@shared/models/entity-type.models'; +import { Observable, of } 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'; +import { sortObjectKeys } from '@core/utils'; +import { RuleChainService } from "@core/http/rule-chain.service"; +import { AttributeService } from "@core/http/attribute.service"; +import { AttributeScope } from "@shared/models/telemetry/telemetry.models"; +import { EdgeDownlinkTableHeaderComponent } from "@home/components/edge/edge-downlink-table-header.component"; +import { EdgeService } from "@core/http/edge.service"; +import { map } from "rxjs/operators"; +import { AssetService } from "@core/http/asset.service"; +import { DeviceService } from "@core/http/device.service"; +import { EntityViewService } from "@core/http/entity-view.service"; +import { actionTypeTranslations } from "@shared/models/audit-log.models"; + +export class EdgeDownlinkTableConfig extends EntityTableConfig { + + queueStartTs: number; + + constructor(private edgeService: EdgeService, + private dialogService: DialogService, + private translate: TranslateService, + private deviceService: DeviceService, + private assetService: AssetService, + private entityViewService: EntityViewService, + private ruleChainService: RuleChainService, + private attributeService: AttributeService, + private datePipe: DatePipe, + private dialog: MatDialog, + public entityId: EntityId, + public tenantId: string) { + 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.headerComponent = EdgeDownlinkTableHeaderComponent; + + this.entityTranslations = { + noEntities: 'edge.no-downlinks-prompt' + }; + this.entityResources = {} as EntityTypeResource; + this.entitiesFetchFunction = pageLink => this.fetchEvents(pageLink); + + this.defaultSortOrder = {property: 'createdTime', direction: Direction.DESC}; + + this.updateColumns(); + } + + fetchEvents(pageLink: TimePageLink): Observable> { + this.loadEdgeInfo(); + return this.edgeService.getEdgeEvents(this.entityId, pageLink); + } + + updateColumns(updateTableColumns: boolean = false): void { + this.columns = []; + this.columns.push( + new DateEntityTableColumn('createdTime', 'event.event-time', this.datePipe, '120px'), + new EntityTableColumn('type', 'event.type', '25%', + entity => this.translate.instant(edgeEventTypeTranslations.get(entity.type)), entity => ({}), false), + new EntityTableColumn('action', 'edge.event-action', '25%', + entity => this.translate.instant(edgeEventActionTypeTranslations.get(entity.action)), entity => ({}), false), + new EntityTableColumn('entityId', 'edge.entity-id', '40%', + (entity) => entity.entityId, entity => ({}), false), + new EntityTableColumn('status', 'event.status', '10%', + (entity) => this.updateEdgeEventStatus(entity.createdTime), + entity => ({ + color: this.isPending(entity.createdTime) ? edgeEventStatusColor.get(EdgeEventStatus.PENDING) : edgeEventStatusColor.get(EdgeEventStatus.DEPLOYED) + }), false), + new EntityActionTableColumn('data', 'event.data', + { + name: this.translate.instant('action.view'), + icon: 'more_horiz', + isEnabled: (entity) => this.isEdgeEventHasData(entity.type), + onAction: ($event, entity) => + { + this.prepareEdgeEventContent(entity).subscribe((content) => { + this.showEdgeEventContent($event, content,'event.data'); + }); + } + }, + '40px'), + ); + if (updateTableColumns) { + this.table.columnsUpdated(true); + } + } + + showContent($event: MouseEvent, content: string, title: string, contentType: ContentType = null, sortKeys = false): void { + if ($event) { + $event.stopPropagation(); + } + if (contentType === ContentType.JSON && sortKeys) { + try { + content = JSON.stringify(sortObjectKeys(JSON.parse(content))); + } catch (e) { + } + } + this.dialog.open(EventContentDialogComponent, { + disableClose: true, + panelClass: ['tb-dialog', 'tb-fullscreen-dialog'], + data: { + content, + title, + contentType + } + }); + } + + isEdgeEventHasData(edgeEventType: EdgeEventType) { + switch (edgeEventType) { + case EdgeEventType.WIDGET_TYPE: + case EdgeEventType.WIDGETS_BUNDLE: + case EdgeEventType.ADMIN_SETTINGS: + return false; + default: + return true; + } + } + + prepareEdgeEventContent(entity) { + // TODO: voba - extend this function with different cases based on action and entity type + switch (entity.type) { + case EdgeEventType.RELATION: + return of(JSON.stringify(entity.body)); + case EdgeEventType.ASSET: + return this.assetService.getAsset(entity.entityId, null).pipe( + map((asset) => { + return JSON.stringify(asset); + }) + ); + case EdgeEventType.DEVICE: + return this.deviceService.getDevice(entity.entityId, null).pipe( + map((device) => { + return JSON.stringify(device); + }) + ); + case EdgeEventType.ENTITY_VIEW: + return this.entityViewService.getEntityView(entity.entityId, null).pipe( + map((entityView) => { + return JSON.stringify(entityView); + }) + ); + case EdgeEventType.RULE_CHAIN_METADATA: + return this.ruleChainService.getRuleChainMetadata(entity.entityId, null).pipe( + map((ruleChainMetaData) => { + return JSON.stringify(ruleChainMetaData.nodes); + }) + ); + default: + return of(JSON.stringify(entity)); + } + } + + showEdgeEventContent($event: MouseEvent, content: string, title: string, sortKeys = false): void { + if ($event) { + $event.stopPropagation(); + } + var contentType = ContentType.JSON; + if (contentType === ContentType.JSON && sortKeys) { + try { + content = JSON.stringify(sortObjectKeys(JSON.parse(content))); + } catch (e) { + } + } + this.dialog.open(EventContentDialogComponent, { + disableClose: true, + panelClass: ['tb-dialog', 'tb-fullscreen-dialog'], + data: { + content, + title, + contentType + } + }); + } + + updateEdgeEventStatus(createdTime) { + if (this.queueStartTs && createdTime < this.queueStartTs) { + return this.translate.instant('edge.success'); + } else { + return this.translate.instant('edge.failed'); + } + } + + isPending(createdTime) { + return createdTime > this.queueStartTs; + } + + loadEdgeInfo() { + this.attributeService.getEntityAttributes(this.entityId, AttributeScope.SERVER_SCOPE, ['queueStartTs']) + .subscribe( + attributes => this.onUpdate(attributes) + ); + } + + onUpdate(attributes) { + this.queueStartTs = 0; + let edge = attributes.reduce(function (map, attribute) { + map[attribute.key] = attribute; + return map; + }, {}); + if (edge.queueStartTs) { + this.queueStartTs = edge.queueStartTs.lastUpdateTs; + } + } +} + diff --git a/ui-ngx/src/app/modules/home/components/edge/edge-downlink-table-header.component.html b/ui-ngx/src/app/modules/home/components/edge/edge-downlink-table-header.component.html new file mode 100644 index 0000000000..e074988e88 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/edge/edge-downlink-table-header.component.html @@ -0,0 +1,19 @@ + + + diff --git a/ui-ngx/src/app/modules/home/components/edge/edge-downlink-table-header.component.scss b/ui-ngx/src/app/modules/home/components/edge/edge-downlink-table-header.component.scss new file mode 100644 index 0000000000..a009dbbb88 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/edge/edge-downlink-table-header.component.scss @@ -0,0 +1,41 @@ +/** + * Copyright © 2016-2020 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 '../../../../../scss/constants'; + +:host { + padding-right: 8px; +} + +:host ::ng-deep { + mat-form-field { + + .mat-form-field-wrapper { + padding-bottom: 0; + } + + .mat-form-field-underline { + bottom: 0; + } + + @media #{$mat-xs} { + width: 100%; + + .mat-form-field-infix { + width: auto !important; + } + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/edge/edge-downlink-table-header.component.ts b/ui-ngx/src/app/modules/home/components/edge/edge-downlink-table-header.component.ts new file mode 100644 index 0000000000..e6d17491f1 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/edge/edge-downlink-table-header.component.ts @@ -0,0 +1,42 @@ +/// +/// Copyright © 2016-2020 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 } from '@angular/core'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { EntityTableHeaderComponent } from '../../components/entity/entity-table-header.component'; +import { EdgeEvent } from "@shared/models/edge.models"; +import { EdgeDownlinkTableConfig } from "@home/components/edge/edge-downlink-table-config"; + +@Component({ + selector: 'tb-edge-downlink-table-header', + templateUrl: './edge-downlink-table-header.component.html', + styleUrls: ['./edge-downlink-table-header.component.scss'] +}) +export class EdgeDownlinkTableHeaderComponent extends EntityTableHeaderComponent { + + get eventTableConfig(): EdgeDownlinkTableConfig { + return this.entitiesTableConfig as EdgeDownlinkTableConfig; + } + + constructor(protected store: Store) { + super(store); + } + + eventTypeChanged() { + this.eventTableConfig.table.resetSortAndFilter(true, true); + } +} diff --git a/ui-ngx/src/app/modules/home/components/edge/edge-downlink-table.component.html b/ui-ngx/src/app/modules/home/components/edge/edge-downlink-table.component.html new file mode 100644 index 0000000000..792765ba4d --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/edge/edge-downlink-table.component.html @@ -0,0 +1,18 @@ + + diff --git a/ui-ngx/src/app/modules/home/components/edge/edge-downlink-table.component.scss b/ui-ngx/src/app/modules/home/components/edge/edge-downlink-table.component.scss new file mode 100644 index 0000000000..719941ef46 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/edge/edge-downlink-table.component.scss @@ -0,0 +1,22 @@ +/** + * Copyright © 2016-2020 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 ::ng-deep { + tb-entities-table { + .mat-drawer-container { + background-color: white; + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/edge/edge-downlink-table.component.ts b/ui-ngx/src/app/modules/home/components/edge/edge-downlink-table.component.ts new file mode 100644 index 0000000000..373c95564c --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/edge/edge-downlink-table.component.ts @@ -0,0 +1,103 @@ +/// +/// Copyright © 2016-2020 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, Input, OnInit, ViewChild } from '@angular/core'; +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 { EntitiesTableComponent } from '@home/components/entity/entities-table.component'; +import { EdgeDownlinkTableConfig } from './edge-downlink-table-config'; +import { DialogService } from '@core/services/dialog.service'; +import { RuleChainService } from "@core/http/rule-chain.service"; +import { AttributeService } from "@core/http/attribute.service"; +import { EdgeService } from "@core/http/edge.service"; +import { DeviceService } from "@core/http/device.service"; +import { AssetService } from "@core/http/asset.service"; +import { EntityViewService } from "@core/http/entity-view.service"; + +@Component({ + selector: 'tb-edge-downlink-table', + templateUrl: './edge-downlink-table.component.html', + styleUrls: ['./edge-downlink-table.component.scss'] +}) +export class EdgeDownlinkTableComponent implements OnInit { + + @Input() + tenantId: string; + + activeValue = false; + dirtyValue = false; + entityIdValue: EntityId; + + @Input() + set active(active: boolean) { + if (this.activeValue !== active) { + this.activeValue = active; + if (this.activeValue && this.dirtyValue) { + this.dirtyValue = false; + this.entitiesTable.updateData(); + } + } + } + + @Input() + set entityId(entityId: EntityId) { + this.entityIdValue = entityId; + if (this.edgeDownlinkTableConfig && this.edgeDownlinkTableConfig.entityId !== entityId) { + this.edgeDownlinkTableConfig.entityId = entityId; + this.entitiesTable.resetSortAndFilter(this.activeValue); + if (!this.activeValue) { + this.dirtyValue = true; + } + } + } + + @ViewChild(EntitiesTableComponent, {static: true}) entitiesTable: EntitiesTableComponent; + + edgeDownlinkTableConfig: EdgeDownlinkTableConfig; + + constructor(private edgeService: EdgeService, + private deviceService: DeviceService, + private assetService: AssetService, + private entityViewService: EntityViewService, + private dialogService: DialogService, + private translate: TranslateService, + private attributeService: AttributeService, + private ruleChainService: RuleChainService, + private datePipe: DatePipe, + private dialog: MatDialog) { + } + + ngOnInit() { + this.dirtyValue = !this.activeValue; + this.edgeDownlinkTableConfig = new EdgeDownlinkTableConfig( + this.edgeService, + this.dialogService, + this.translate, + this.deviceService, + this.assetService, + this.entityViewService, + this.ruleChainService, + this.attributeService, + this.datePipe, + this.dialog, + this.entityIdValue, + this.tenantId + ); + } + +} 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 5b0b799e59..1caa392aa1 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 @@ -20,35 +20,25 @@ import { EntityTableColumn, EntityTableConfig } from '@home/models/entity/entities-table-config.models'; -import { - DebugEventType, - Event, - EventType, - EdgeEventType, - EdgeEventStatus, - edgeEventStatusColor -} 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 {EntityType, 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 { DebugEventType, Event, EventType } 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'; -import {sortObjectKeys} from '@core/utils'; -import {RuleChainService} from "@core/http/rule-chain.service"; -import {AttributeService} from "@core/http/attribute.service"; -import {AttributeScope} from "@shared/models/telemetry/telemetry.models"; +import { sortObjectKeys } from '@core/utils'; export class EventTableConfig extends EntityTableConfig { @@ -66,13 +56,10 @@ export class EventTableConfig extends EntityTableConfig { } eventTypes: Array; - queueStartTs: number; constructor(private eventService: EventService, private dialogService: DialogService, private translate: TranslateService, - private ruleChainService: RuleChainService, - private attributeService: AttributeService, private datePipe: DatePipe, private dialog: MatDialog, public entityId: EntityId, @@ -94,10 +81,6 @@ export class EventTableConfig extends EntityTableConfig { this.eventTypes = Object.keys(EventType).map(type => EventType[type]); - if (this.entityId.entityType !== EntityType.EDGE) { - this.eventTypes.pop(); - } - if (disabledEventTypes && disabledEventTypes.length) { this.eventTypes = this.eventTypes.filter(type => disabledEventTypes.indexOf(type) === -1); } @@ -121,25 +104,15 @@ export class EventTableConfig extends EntityTableConfig { } fetchEvents(pageLink: TimePageLink): Observable> { - if (this.eventTypeValue === EventType.EDGE_EVENT) { - this.loadEdgeInfo(); - return this.eventService.getEdgeEvents(this.entityId, pageLink); - } else { - return this.eventService.getEvents(this.entityId, this.eventType, this.tenantId, pageLink); - } + return this.eventService.getEvents(this.entityId, this.eventType, this.tenantId, pageLink); } updateColumns(updateTableColumns: boolean = false): void { this.columns = []; this.columns.push( new DateEntityTableColumn('createdTime', 'event.event-time', this.datePipe, '120px'), - ); - if (this.eventType !== EventType.EDGE_EVENT) { - this.columns.push( - new EntityTableColumn('server', 'event.server', '100px', - (entity) => entity.body.server, entity => ({}), false) - ); - } + new EntityTableColumn('server', 'event.server', '100px', + (entity) => entity.body.server, entity => ({}), false)); switch (this.eventType) { case EventType.ERROR: this.columns.push( @@ -186,30 +159,6 @@ export class EventTableConfig extends EntityTableConfig { () => ({}), () => undefined, true) ); break; - case EventType.EDGE_EVENT: - this.columns.push( - new EntityTableColumn('type', 'event.type', '100%', - (entity) => entity.type, entity => ({}), false), - new EntityTableColumn('action', 'edge.event-action', '100%', - (entity) => entity.action, entity => ({}), false), - new EntityTableColumn('entityId', 'edge.entity-id', '100%', - (entity) => entity.id.id, entity => ({}), false), //TODO: replace this to entity.entityId because of conflict wiht entityId model - new EntityTableColumn('status', 'event.status', '100%', - (entity) => this.updateEdgeEventStatus(entity.createdTime), - entity => ({ - color: this.isPending(entity.createdTime) ? edgeEventStatusColor.get(EdgeEventStatus.PENDING) : edgeEventStatusColor.get(EdgeEventStatus.DEPLOYED) - }), false), - new EntityActionTableColumn('data', 'event.data', - { - name: this.translate.instant('action.view'), - icon: 'more_horiz', - isEnabled: (entity) => this.checkEdgeEventType(entity), - onAction: ($event, entity) => this.showEdgeEventContent($event, this.manageEdgeEventContent(entity), - 'event.data') - }, - '40px'), - ); - break; case DebugEventType.DEBUG_RULE_NODE: case DebugEventType.DEBUG_RULE_CHAIN: this.columns[0].width = '100px'; @@ -300,82 +249,5 @@ export class EventTableConfig extends EntityTableConfig { } }); } - - checkEdgeEventType(entity) { - return !( entity.type === EdgeEventType.WIDGET_TYPE || - entity.type === EdgeEventType.ADMIN_SETTINGS || - entity.type === EdgeEventType.WIDGETS_BUNDLE ); - } - - manageEdgeEventContent(entity) { - var content: string; - switch (entity.type) { - case EdgeEventType.RELATION: - content = entity.body; - break; - // case EdgeEventType.RULE_CHAIN_METADATA: - // this.ruleChainService.getRuleChainMetadata(entity.entityId, null).pipe( - // map(ruleChainMetaData => content = ruleChainMetaData.nodes) - // ); - // break; - default: - content = entity; - break; - } - return JSON.stringify(content); - } - - showEdgeEventContent($event: MouseEvent, content: string, title: string, sortKeys = false): void { - if ($event) { - $event.stopPropagation(); - } - var contentType = ContentType.JSON; - if (contentType === ContentType.JSON && sortKeys) { - try { - content = JSON.stringify(sortObjectKeys(JSON.parse(content))); - } catch (e) {} - } - this.dialog.open(EventContentDialogComponent, { - disableClose: true, - panelClass: ['tb-dialog', 'tb-fullscreen-dialog'], - data: { - content, - title, - contentType - } - }); - } - - updateEdgeEventStatus(createdTime) { - if (this.queueStartTs && createdTime < this.queueStartTs) { - return this.translate.instant('edge.success'); - } else { - return this.translate.instant('edge.failed'); - } - } - - isPending(createdTime) { - return createdTime > this.queueStartTs; - } - - loadEdgeInfo() { - this.attributeService.getEntityAttributes(this.entityId, AttributeScope.SERVER_SCOPE, ['queueStartTs']) - .subscribe( - attributes => this.onUpdate(attributes) - ); - } - - onUpdate(attributes) { - this.queueStartTs = 0; - let edge = attributes.reduce(function (map, attribute) { - map[attribute.key] = attribute; - return map; - }, {}); - if (edge.queueStartTs) { - this.queueStartTs = edge.queueStartTs.lastUpdateTs; - } - } - - } diff --git a/ui-ngx/src/app/modules/home/components/event/event-table.component.ts b/ui-ngx/src/app/modules/home/components/event/event-table.component.ts index ac1ccd1e09..aa6ce5dd77 100644 --- a/ui-ngx/src/app/modules/home/components/event/event-table.component.ts +++ b/ui-ngx/src/app/modules/home/components/event/event-table.component.ts @@ -24,8 +24,6 @@ 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 { RuleChainService } from "@core/http/rule-chain.service"; -import { AttributeService } from "@core/http/attribute.service"; @Component({ selector: 'tb-event-table', @@ -81,8 +79,6 @@ export class EventTableComponent implements OnInit { constructor(private eventService: EventService, private dialogService: DialogService, private translate: TranslateService, - private attributeService: AttributeService, - private ruleChainService: RuleChainService, private datePipe: DatePipe, private dialog: MatDialog) { } @@ -93,8 +89,6 @@ export class EventTableComponent implements OnInit { this.eventService, this.dialogService, this.translate, - this.ruleChainService, - this.attributeService, this.datePipe, this.dialog, this.entityIdValue, diff --git a/ui-ngx/src/app/modules/home/components/home-components.module.ts b/ui-ngx/src/app/modules/home/components/home-components.module.ts index 113f4ae4bb..2412aca78f 100644 --- a/ui-ngx/src/app/modules/home/components/home-components.module.ts +++ b/ui-ngx/src/app/modules/home/components/home-components.module.ts @@ -119,6 +119,7 @@ import { SmsProviderConfigurationComponent } from '@home/components/sms/sms-prov import { AwsSnsProviderConfigurationComponent } from '@home/components/sms/aws-sns-provider-configuration.component'; import { TwilioSmsProviderConfigurationComponent } from '@home/components/sms/twilio-sms-provider-configuration.component'; import { CopyDeviceCredentialsComponent } from '@home/components/device/copy-device-credentials.component'; +import {EdgeDownlinkTableComponent} from "@home/components/edge/edge-downlink-table.component"; @NgModule({ declarations: @@ -132,6 +133,7 @@ import { CopyDeviceCredentialsComponent } from '@home/components/device/copy-dev EventContentDialogComponent, EventTableHeaderComponent, EventTableComponent, + EdgeDownlinkTableComponent, RelationTableComponent, RelationDialogComponent, RelationFiltersComponent, @@ -234,6 +236,7 @@ import { CopyDeviceCredentialsComponent } from '@home/components/device/copy-dev EntityDetailsPanelComponent, AuditLogTableComponent, EventTableComponent, + EdgeDownlinkTableComponent, RelationTableComponent, RelationFiltersComponent, AlarmTableComponent, diff --git a/ui-ngx/src/app/modules/home/pages/edge/edge-tabs.component.html b/ui-ngx/src/app/modules/home/pages/edge/edge-tabs.component.html index dc892aadbe..ff805df920 100644 --- a/ui-ngx/src/app/modules/home/pages/edge/edge-tabs.component.html +++ b/ui-ngx/src/app/modules/home/pages/edge/edge-tabs.component.html @@ -41,6 +41,11 @@ + + + diff --git a/ui-ngx/src/app/modules/home/pages/edge/edge.component.ts b/ui-ngx/src/app/modules/home/pages/edge/edge.component.ts index 009d5ee27c..bfa14e0e0f 100644 --- a/ui-ngx/src/app/modules/home/pages/edge/edge.component.ts +++ b/ui-ngx/src/app/modules/home/pages/edge/edge.component.ts @@ -44,6 +44,7 @@ export class EdgeComponent extends EntityComponent { @Inject('entity') protected entityValue: EdgeInfo, @Inject('entitiesTableConfig') protected entitiesTableConfigValue: EntityTableConfig, public fb: FormBuilder, + // TODO: voba - discuss with Vlad how properly add window to subclass @Inject(WINDOW) protected window: Window) { super(store, fb, entityValue, entitiesTableConfigValue, window); } diff --git a/ui-ngx/src/app/modules/home/pages/edge/edge.module.ts b/ui-ngx/src/app/modules/home/pages/edge/edge.module.ts index 89018a58b4..89562c2dd0 100644 --- a/ui-ngx/src/app/modules/home/pages/edge/edge.module.ts +++ b/ui-ngx/src/app/modules/home/pages/edge/edge.module.ts @@ -36,6 +36,9 @@ import { EdgeTabsComponent } from "@home/pages/edge/edge-tabs.component"; HomeDialogsModule, HomeComponentsModule, EdgeRoutingModule + ], + exports: [ + EdgeComponent ] }) diff --git a/ui-ngx/src/app/shared/models/edge.models.ts b/ui-ngx/src/app/shared/models/edge.models.ts index 102177a23b..78f2f3e261 100644 --- a/ui-ngx/src/app/shared/models/edge.models.ts +++ b/ui-ngx/src/app/shared/models/edge.models.ts @@ -20,6 +20,8 @@ import { CustomerId } from '@shared/models/id/customer-id'; import { EdgeId } from '@shared/models/id/edge-id'; import { EntitySearchQuery } from '@shared/models/relation.models'; import { RuleChainId } from "@shared/models/id/rule-chain-id"; +import { BaseEventBody } from "@shared/models/event.models"; +import { EventId } from "@shared/models/id/event-id"; export interface Edge extends BaseData { tenantId?: TenantId; @@ -43,3 +45,115 @@ export interface EdgeInfo extends Edge { export interface EdgeSearchQuery extends EntitySearchQuery { edgeTypes: Array; } + +export enum EdgeEventType { + DASHBOARD = "DASHBOARD", + ASSET = "ASSET", + DEVICE = "DEVICE", + DEVICE_PROFILE = "DEVICE_PROFILE", + ENTITY_VIEW = "ENTITY_VIEW", + ALARM = "ALARM", + RULE_CHAIN = "RULE_CHAIN", + RULE_CHAIN_METADATA = "RULE_CHAIN_METADATA", + EDGE = "EDGE", + USER = "USER", + CUSTOMER = "CUSTOMER", + RELATION = "RELATION", + WIDGETS_BUNDLE = "WIDGETS_BUNDLE", + WIDGET_TYPE = "WIDGET_TYPE", + ADMIN_SETTINGS = "ADMIN_SETTINGS" +} + +export enum EdgeEventActionType { + ADDED = "ADDED", + DELETED = "DELETED", + UPDATED = "UPDATED", + POST_ATTRIBUTES = "POST_ATTRIBUTES", + ATTRIBUTES_UPDATED = "ATTRIBUTES_UPDATED", + ATTRIBUTES_DELETED = "ATTRIBUTES_DELETED", + TIMESERIES_UPDATED = "TIMESERIES_UPDATED", + CREDENTIALS_UPDATED = "CREDENTIALS_UPDATED", + ASSIGNED_TO_CUSTOMER = "ASSIGNED_TO_CUSTOMER", + UNASSIGNED_FROM_CUSTOMER = "UNASSIGNED_FROM_CUSTOMER", + RELATION_ADD_OR_UPDATE = "RELATION_ADD_OR_UPDATE", + RELATION_DELETED = "RELATION_DELETED", + RPC_CALL = "RPC_CALL", + ALARM_ACK = "ALARM_ACK", + ALARM_CLEAR = "ALARM_CLEAR", + ASSIGNED_TO_EDGE = "ASSIGNED_TO_EDGE", + UNASSIGNED_FROM_EDGE = "UNASSIGNED_FROM_EDGE", + CREDENTIALS_REQUEST = "CREDENTIALS_REQUEST", + ENTITY_MERGE_REQUEST = "ENTITY_MERGE_REQUEST" +} + +export enum EdgeEventStatus { + DEPLOYED = "DEPLOYED", + PENDING = "PENDING" +} + +export const edgeEventTypeTranslations = new Map( + [ + [EdgeEventType.DASHBOARD, 'edge.edge-event-type-dashboard'], + [EdgeEventType.ASSET, 'edge.edge-event-type-asset'], + [EdgeEventType.DEVICE, 'edge.edge-event-type-device'], + [EdgeEventType.DEVICE_PROFILE, 'edge.edge-event-type-device-profile'], + [EdgeEventType.ENTITY_VIEW, 'edge.edge-event-type-entity-view'], + [EdgeEventType.ALARM, 'edge.edge-event-type-alarm'], + [EdgeEventType.RULE_CHAIN, 'edge.edge-event-type-rule-chain'], + [EdgeEventType.RULE_CHAIN_METADATA, 'edge.edge-event-type-rule-chain-metadata'], + [EdgeEventType.EDGE, 'edge.edge-event-type-edge'], + [EdgeEventType.USER, 'edge.edge-event-type-user'], + [EdgeEventType.CUSTOMER, 'edge.edge-event-type-customer'], + [EdgeEventType.RELATION, 'edge.edge-event-type-relation'], + [EdgeEventType.WIDGETS_BUNDLE, 'edge.edge-event-type-widgets-bundle'], + [EdgeEventType.WIDGET_TYPE, 'edge.edge-event-type-widgets-type'], + [EdgeEventType.ADMIN_SETTINGS, 'edge.edge-event-type-admin-settings'] + ] +); + +export const edgeEventActionTypeTranslations = new Map( + [ + [EdgeEventActionType.ADDED, 'edge.edge-event-action-type-added'], + [EdgeEventActionType.DELETED, 'edge.edge-event-action-type-deleted'], + [EdgeEventActionType.UPDATED, 'edge.edge-event-action-type-updated'], + [EdgeEventActionType.POST_ATTRIBUTES, 'edge.edge-event-action-type-post-attributes'], + [EdgeEventActionType.ATTRIBUTES_UPDATED, 'edge.edge-event-action-type-attributes-updated'], + [EdgeEventActionType.ATTRIBUTES_DELETED, 'edge.edge-event-action-type-attributes-deleted'], + [EdgeEventActionType.TIMESERIES_UPDATED, 'edge.edge-event-action-type-timeseries-updated'], + [EdgeEventActionType.CREDENTIALS_UPDATED, 'edge.edge-event-action-type-credentials-updated'], + [EdgeEventActionType.ASSIGNED_TO_CUSTOMER, 'edge.edge-event-action-type-assigned-to-customer'], + [EdgeEventActionType.UNASSIGNED_FROM_CUSTOMER, 'edge.edge-event-action-type-unassigned-from-customer'], + [EdgeEventActionType.RELATION_ADD_OR_UPDATE, 'edge.edge-event-action-type-relation-add-or-update'], + [EdgeEventActionType.RELATION_DELETED, 'edge.edge-event-action-type-relation-deleted'], + [EdgeEventActionType.RPC_CALL, 'edge.edge-event-action-type-rpc-call'], + [EdgeEventActionType.ALARM_ACK, 'edge.edge-event-action-type-alarm-ack'], + [EdgeEventActionType.ALARM_CLEAR, 'edge.edge-event-action-type-alarm-clear'], + [EdgeEventActionType.ASSIGNED_TO_EDGE, 'edge.edge-event-action-type-assigned-to-edge'], + [EdgeEventActionType.UNASSIGNED_FROM_EDGE, 'edge.edge-event-action-type-unassigned-from-edge'], + [EdgeEventActionType.CREDENTIALS_REQUEST, 'edge.edge-event-action-type-credentials-request'], + [EdgeEventActionType.ENTITY_MERGE_REQUEST, 'edge.edge-event-action-type-entity-merge-request'] + ] +); + +export const edgeEventStatusColor = new Map( + [ + [EdgeEventStatus.DEPLOYED, '#000000'], + [EdgeEventStatus.PENDING, '#9e9e9e'] + ] +); + +export interface EdgeEventBody extends BaseEventBody { + type: string; + action: string; + entityId: string; +} + +export interface EdgeEvent extends BaseData { + tenantId: TenantId; + entityId: string; + edgeId: EdgeId; + action: EdgeEventActionType; + type: EdgeEventType; + uid: string; + body: string; +} diff --git a/ui-ngx/src/app/shared/models/event.models.ts b/ui-ngx/src/app/shared/models/event.models.ts index 3e37b4027f..68b1eefd39 100644 --- a/ui-ngx/src/app/shared/models/event.models.ts +++ b/ui-ngx/src/app/shared/models/event.models.ts @@ -23,8 +23,7 @@ import { ContentType } from '@shared/models/constants'; export enum EventType { ERROR = 'ERROR', LC_EVENT = 'LC_EVENT', - STATS = 'STATS', - EDGE_EVENT = 'EDGE_EVENT' + STATS = 'STATS' } export enum DebugEventType { @@ -32,41 +31,11 @@ export enum DebugEventType { DEBUG_RULE_CHAIN = 'DEBUG_RULE_CHAIN' } -export enum EdgeEventType { - DASHBOARD = "DASHBOARD", - ASSET = "ASSET", - DEVICE = "DEVICE", - ENTITY_VIEW = "ENTITY_VIEW", - ALARM = "ALARM", - RULE_CHAIN = "RULE_CHAIN", - RULE_CHAIN_METADATA = "RULE_CHAIN_METADATA", - EDGE = "EDGE", - USER = "USER", - CUSTOMER = "CUSTOMER", - RELATION = "RELATION", - WIDGETS_BUNDLE = "WIDGETS_BUNDLE", - WIDGET_TYPE = "WIDGET_TYPE", - ADMIN_SETTINGS = "ADMIN_SETTINGS" -} - -export enum EdgeEventStatus { - DEPLOYED = "DEPLOYED", - PENDING = "PENDING" -} - -export const edgeEventStatusColor = new Map ( - [ - [EdgeEventStatus.DEPLOYED, '#000000'], - [EdgeEventStatus.PENDING, '#9e9e9e'] - ] -); - export const eventTypeTranslations = new Map( [ [EventType.ERROR, 'event.type-error'], [EventType.LC_EVENT, 'event.type-lc-event'], [EventType.STATS, 'event.type-stats'], - [EventType.EDGE_EVENT, 'event.type-edge-event'], [DebugEventType.DEBUG_RULE_NODE, 'event.type-debug-rule-node'], [DebugEventType.DEBUG_RULE_CHAIN, 'event.type-debug-rule-chain'], ] @@ -92,12 +61,6 @@ export interface StatsEventBody extends BaseEventBody { errorsOccurred: number; } -export interface EdgeEventBody extends BaseEventBody { - type: string; - action: string; - entityId: string; -} - export interface DebugRuleNodeEventBody extends BaseEventBody { type: string; entityId: string; @@ -111,7 +74,7 @@ export interface DebugRuleNodeEventBody extends BaseEventBody { error: string; } -export type EventBody = ErrorEventBody & LcEventEventBody & StatsEventBody & DebugRuleNodeEventBody & EdgeEventBody; +export type EventBody = ErrorEventBody & LcEventEventBody & StatsEventBody & DebugRuleNodeEventBody; export interface Event extends BaseData { tenantId: TenantId; @@ -119,5 +82,4 @@ export interface Event extends BaseData { type: string; uid: string; body: EventBody; - action: string; //TODO: refactor edgeEvents - move parameters to the entity.body } 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 f2350ad5ce..89c7d38226 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -1203,7 +1203,43 @@ "dashboard": "Edge dashboard", "enter-edge-type": "Enter edge type", "deployed": "Deployed", - "pending": "Pending" + "pending": "Pending", + "downlinks": "Downlinks", + "no-downlinks-prompt": "No downlinks found", + "edge-event-type-dashboard": "Dashboard", + "edge-event-type-asset": "Asset", + "edge-event-type-device": "Device", + "edge-event-type-device-profile": "Device Profile", + "edge-event-type-entity-view": "Entity View", + "edge-event-type-alarm": "Alar", + "edge-event-type-rule-chain": "Rule Chain", + "edge-event-type-rule-chain-metadata": "Rule Chain Metadata", + "edge-event-type-edge": "Edge", + "edge-event-type-user": "User", + "edge-event-type-customer": "Customer", + "edge-event-type-relation": "Relation", + "edge-event-type-widgets-bundle": "Widgets Bundle", + "edge-event-type-widgets-type": "Widgets Type", + "edge-event-type-admin-settings": "Admin Settings", + "edge-event-action-type-added": "Added", + "edge-event-action-type-deleted": "Deleted", + "edge-event-action-type-updated": "Updated", + "edge-event-action-type-post-attributes": "Post Attributes", + "edge-event-action-type-attributes-updated": "Attributes Updated", + "edge-event-action-type-attributes-deleted": "Attributes Deleted", + "edge-event-action-type-timeseries-updated": "Timeseries Updated", + "edge-event-action-type-credentials-updated": "Credentials Updated", + "edge-event-action-type-assigned-to-customer": "Assigned to Customer", + "edge-event-action-type-unassigned-from-customer": "Unassigned from Customer", + "edge-event-action-type-relation-add-or-update": "Relation Add or Update", + "edge-event-action-type-relation-deleted": "Relation Deleted", + "edge-event-action-type-rpc-call": "RPC Call", + "edge-event-action-type-alarm-ack": "Alarm Ack", + "edge-event-action-type-alarm-clear": "Alarm Clear", + "edge-event-action-type-assigned-to-edge": "Assigned to Edge", + "edge-event-action-type-unassigned-from-edge": "Unassigned from Edge", + "edge-event-action-type-credentials-request": "Credentials Request", + "edge-event-action-type-entity-merge-request": "Entity Merge Request" }, "error": { "unable-to-connect": "Unable to connect to the server! Please check your internet connection.",