Skip to content

Commit a5ab360

Browse files
authored
Merge pull request #175 from OS2iot/feature/IOT-1530_datatarget_log
Feature/iot 1530 datatarget log
2 parents 7ed576b + 865c5a2 commit a5ab360

File tree

50 files changed

+1040
-665
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

50 files changed

+1040
-665
lines changed

src/app/applications/applications-routing.module.ts

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,28 @@
11
import { NgModule } from "@angular/core";
22
import { RouterModule, Routes } from "@angular/router";
3+
import { DatatargetTabComponent } from "@applications/datatarget/datatarget-tab/datatarget-tab.component";
4+
import { IotDeviceDataPacketsTabComponent } from "@applications/iot-devices/iot-device-detail/iot-device-data-packets-tab/iot-device-data-packets-tab.component";
5+
import { IotDeviceDetailsTabComponent } from "@applications/iot-devices/iot-device-detail/iot-device-details-tab/iot-device-details-tab.component";
6+
import { IotDeviceDownlinkTabComponent } from "@applications/iot-devices/iot-device-detail/iot-device-downlink-tab/iot-device-downlink-tab.component";
7+
import { IotDeviceHistoryTabComponent } from "@applications/iot-devices/iot-device-detail/iot-device-history-tab/iot-device-history-tab.component";
8+
import { IotDevicesTabComponent } from "@applications/iot-devices/iot-devices-tab/iot-devices-tab.component";
9+
import { MulticastTabComponent } from "@applications/multicast/multicast-tab/multicast-tab.component";
310
import { ApplicationDetailComponent } from "./application-detail/application-detail.component";
411
import { ApplicationEditComponent } from "./application-edit/application-edit.component";
512
import { ApplicationsListComponent } from "./applications-list/applications-list.component";
613
import { ApplicationsComponent } from "./applications.component";
14+
import { BulkImportComponent } from "./bulk-import/bulk-import.component";
15+
import { DatatargetDetailComponent } from "./datatarget/datatarget-detail/datatarget-detail.component";
16+
import { DatatargetEditComponent } from "./datatarget/datatarget-edit/datatarget-edit.component";
17+
import { DatatargetLogComponent } from "./datatarget/datatarget-log/datatarget-log.component";
18+
import { DatatargetNewComponent } from "./datatarget/datatarget-new/datatarget-new.component";
19+
import { FiwareDetailComponent } from "./datatarget/fiware/fiware-detail/fiware-detail.component";
20+
import { HttppushDetailComponent } from "./datatarget/httppush/httppush-detail/httppush-detail.component";
21+
import { MqttDetailComponent } from "./datatarget/mqtt/mqtt-detail/mqtt-detail.component";
722
import { IoTDeviceDetailComponent } from "./iot-devices/iot-device-detail/iot-device-detail.component";
823
import { IotDeviceEditComponent } from "./iot-devices/iot-device-edit/iot-device-edit.component";
9-
import { DatatargetEditComponent } from "./datatarget/datatarget-edit/datatarget-edit.component";
10-
import { DatatargetDetailComponent } from "./datatarget/datatarget-detail/datatarget-detail.component";
11-
import { BulkImportComponent } from "./bulk-import/bulk-import.component";
12-
import { MulticastEditComponent } from "./multicast/multicast-edit/multicast-edit.component";
1324
import { MulticastDetailComponent } from "./multicast/multicast-detail/multicast-detail.component";
14-
import { DatatargetNewComponent } from "./datatarget/datatarget-new/datatarget-new.component";
15-
import { IotDevicesTabComponent } from "@applications/iot-devices/iot-devices-tab/iot-devices-tab.component";
16-
import { MulticastTabComponent } from "@applications/multicast/multicast-tab/multicast-tab.component";
17-
import { DatatargetTabComponent } from "@applications/datatarget/datatarget-tab/datatarget-tab.component";
18-
import { IotDeviceDetailsTabComponent } from "@applications/iot-devices/iot-device-detail/iot-device-details-tab/iot-device-details-tab.component";
19-
import { IotDeviceHistoryTabComponent } from "@applications/iot-devices/iot-device-detail/iot-device-history-tab/iot-device-history-tab.component";
20-
import { IotDeviceDataPacketsTabComponent } from "@applications/iot-devices/iot-device-detail/iot-device-data-packets-tab/iot-device-data-packets-tab.component";
21-
import { IotDeviceDownlinkTabComponent } from "@applications/iot-devices/iot-device-detail/iot-device-downlink-tab/iot-device-downlink-tab.component";
25+
import { MulticastEditComponent } from "./multicast/multicast-edit/multicast-edit.component";
2226

2327
const applicationRoutes: Routes = [
2428
{
@@ -70,6 +74,12 @@ const applicationRoutes: Routes = [
7074
{
7175
path: "datatarget/:datatargetId",
7276
component: DatatargetDetailComponent,
77+
children: [
78+
{ path: "datatarget-log", component: DatatargetLogComponent },
79+
{ path: "httppush-detail", component: HttppushDetailComponent },
80+
{ path: "fiware-detail", component: FiwareDetailComponent },
81+
{ path: "mqtt-detail", component: MqttDetailComponent },
82+
],
7383
},
7484
{ path: "multicast-edit", component: MulticastEditComponent },
7585
{
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
import { Location } from "@angular/common";
2+
import { HttpResponse } from "@angular/common/http";
3+
import { ActivatedRoute, Router } from "@angular/router";
4+
import { DatatargetDetail } from "@applications/datatarget/datatarget-detail/datatarget-detail";
5+
import { Datatarget } from "@applications/datatarget/datatarget.model";
6+
import { DatatargetService } from "@applications/datatarget/datatarget.service";
7+
import { faExclamationTriangle } from "@fortawesome/free-solid-svg-icons";
8+
import { TranslateService } from "@ngx-translate/core";
9+
import { DeleteDialogService } from "@shared/components/delete-dialog/delete-dialog.service";
10+
import { OrganizationAccessScope } from "@shared/enums/access-scopes";
11+
import { BackButton } from "@shared/models/back-button.model";
12+
import { DropdownButton } from "@shared/models/dropdown-button.model";
13+
import { MeService } from "@shared/services/me.service";
14+
import { NavTab } from "@shared/types/nav-tabs.type";
15+
import { Subscription } from "rxjs";
16+
17+
export abstract class DatatargetDetailTabsCommon implements DatatargetDetail {
18+
protected abstract getDetailsLink(): string;
19+
20+
faExclamationTriangle = faExclamationTriangle;
21+
22+
logLink: "datatarget-log" = "datatarget-log";
23+
navTabs: NavTab[] = [
24+
{
25+
label: "APPLICATION.DETAILS",
26+
link: this.getDetailsLink(),
27+
index: 0,
28+
},
29+
{
30+
label: "GEN.LOG",
31+
link: this.logLink,
32+
index: 1,
33+
},
34+
];
35+
36+
datatarget: Datatarget;
37+
backButton: BackButton = { label: "", routerLink: undefined };
38+
dropdownButton: DropdownButton;
39+
canEdit: boolean;
40+
41+
private subscriptions: Subscription[] = [];
42+
private deleteDialogSubscription: Subscription;
43+
44+
constructor(
45+
route: ActivatedRoute,
46+
router: Router,
47+
translate: TranslateService,
48+
meService: MeService,
49+
private location: Location,
50+
private datatargetService: DatatargetService,
51+
public deleteDialogService: DeleteDialogService
52+
) {
53+
// Load first tab if none was selected
54+
const path = this.location.path();
55+
if (!this.navTabs.some(tab => path.includes(tab.link))) {
56+
router.navigate([path, this.navTabs[0].link], { replaceUrl: true });
57+
}
58+
// URL params
59+
const paramMap = route.snapshot.paramMap;
60+
const id: number = +paramMap.get("datatargetId");
61+
const appId: number = +paramMap.get("id");
62+
if (id) {
63+
// Fetch datatarget info
64+
this.subscriptions.push(this.getDatatarget(id));
65+
this.dropdownButton = {
66+
label: "",
67+
editRouterLink: "../../datatarget-edit/" + id,
68+
isErasable: true,
69+
};
70+
}
71+
// Translate button labels
72+
this.subscriptions.push(
73+
translate.get(["NAV.MY-DATATARGET", "DATATARGET.SHOW-OPTIONS"]).subscribe(translations => {
74+
this.backButton.label = translations["NAV.MY-DATATARGET"];
75+
this.dropdownButton.label = translations["DATATARGET.SHOW-OPTIONS"];
76+
})
77+
);
78+
// Check user permissions
79+
this.canEdit = meService.hasAccessToTargetOrganization(OrganizationAccessScope.ApplicationWrite, undefined, appId);
80+
}
81+
82+
protected onDestroy(): void {
83+
this.subscriptions?.forEach(s => s?.unsubscribe());
84+
this.deleteDialogSubscription?.unsubscribe();
85+
}
86+
87+
private getDatatarget = (id: number) =>
88+
this.datatargetService.get(id).subscribe((dataTarget: Datatarget) => {
89+
this.datatarget = dataTarget;
90+
});
91+
92+
onDeleteDatatarget() {
93+
this.deleteDialogSubscription?.unsubscribe();
94+
this.deleteDialogSubscription = this.deleteDialogService.showSimpleDialog().subscribe(response => {
95+
// Do nothing if user cancels
96+
if (!response) return;
97+
this.subscriptions.push(
98+
this.datatargetService.delete(this.datatarget.id).subscribe((deleteResponse: HttpResponse<any>) => {
99+
if (deleteResponse?.ok) {
100+
this.location.back();
101+
} else {
102+
// TODO: Show error / snackbar??
103+
console.log("Delete failed", deleteResponse);
104+
}
105+
})
106+
);
107+
});
108+
}
109+
}
Lines changed: 22 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
import { Component, ComponentFactoryResolver, OnDestroy, OnInit, Type, ViewChild } from "@angular/core";
1+
import { Component, OnDestroy, Type, ViewChild } from "@angular/core";
22
import { ActivatedRoute } from "@angular/router";
33
import { DataTargetType } from "@shared/enums/datatarget-type";
4+
import { Subscription } from "rxjs";
45
import { DatatargetTypesService } from "../datatarget-types.service";
56
import { Datatarget } from "../datatarget.model";
67
import { DatatargetService } from "../datatarget.service";
@@ -12,18 +13,31 @@ import { DatatargetDetailTypeSelectorDirective } from "./datatarget-detail-type-
1213
templateUrl: "./datatarget-detail.component.html",
1314
styleUrls: ["./datatarget-detail.component.scss"],
1415
})
15-
export class DatatargetDetailComponent implements OnInit, OnDestroy {
16+
export class DatatargetDetailComponent implements OnDestroy {
1617
@ViewChild(DatatargetDetailTypeSelectorDirective, { static: true })
1718
adHost!: DatatargetDetailTypeSelectorDirective;
1819

1920
public datatarget: Datatarget;
2021
private datatargetType: DataTargetType;
2122

23+
private datatargetSubscription: Subscription;
24+
2225
constructor(
23-
private datatargetService: DatatargetService,
24-
private route: ActivatedRoute,
25-
private datatargetTypesService: DatatargetTypesService
26-
) {}
26+
datatargetService: DatatargetService,
27+
route: ActivatedRoute,
28+
datatargetTypesService: DatatargetTypesService
29+
) {
30+
const id: number = +route.snapshot.paramMap.get("datatargetId");
31+
32+
this.datatargetSubscription = datatargetService.get(id).subscribe((dataTarget: Datatarget) => {
33+
this.datatarget = dataTarget;
34+
this.datatargetType = dataTarget.type;
35+
36+
const component = datatargetTypesService.getDetailComponent(this.datatargetType);
37+
38+
this.loadComponent(component);
39+
});
40+
}
2741

2842
loadComponent(componentType: Type<any>) {
2943
const viewContainerRef = this.adHost.viewContainerRef;
@@ -33,18 +47,7 @@ export class DatatargetDetailComponent implements OnInit, OnDestroy {
3347
viewContainerRef.createComponent<DatatargetDetail>(componentType);
3448
}
3549

36-
ngOnInit(): void {
37-
const id: number = +this.route.snapshot.paramMap.get("datatargetId");
38-
39-
this.datatargetService.get(id).subscribe((dataTarget: Datatarget) => {
40-
this.datatarget = dataTarget;
41-
this.datatargetType = dataTarget.type;
42-
43-
const component = this.datatargetTypesService.getDetailComponent(this.datatargetType);
44-
45-
this.loadComponent(component);
46-
});
50+
ngOnDestroy() {
51+
this.datatargetSubscription?.unsubscribe();
4752
}
48-
49-
ngOnDestroy() {}
5053
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<div class="mat-elevation-z8 datatarget-log-container">
2+
<div class="loading-shade" *ngIf="isLoadingResults">
3+
<mat-spinner></mat-spinner>
4+
</div>
5+
6+
<table mat-table [dataSource]="dataSource" matSort matSortActive="createdAt" matSortDirection="desc">
7+
<!-- Timestamp column -->
8+
<ng-container matColumnDef="createdAt">
9+
<th mat-header-cell *matHeaderCellDef mat-sort-header>{{ "GATEWAY.STATS-TIMESTAMP" | translate }}</th>
10+
<td mat-cell *matCellDef="let element">{{ element.createdAt | date:'dd-MM-yyyy - HH:mm:ss' }}</td>
11+
</ng-container>
12+
13+
<!-- Type column -->
14+
<ng-container matColumnDef="type">
15+
<th mat-header-cell *matHeaderCellDef mat-sort-header>{{ "DATATARGET-TABLE.TYPE" | translate }}</th>
16+
<td mat-cell *matCellDef="let element">
17+
<fa-icon *ngIf="element.type === 'OK'" [icon]="faCheckCircle" class="fa-ok"></fa-icon>
18+
<fa-icon *ngIf="element.type === 'ERROR'" [icon]="faExclamationTriangle" class="fa-error"></fa-icon>
19+
{{ ('DATATARGET.RESPONSE_TYPE.' + element.type) | translate }}
20+
</td>
21+
</ng-container>
22+
23+
<!-- Message column -->
24+
<ng-container matColumnDef="message">
25+
<th mat-header-cell *matHeaderCellDef mat-sort-header>{{ "GEN.MESSAGE" | translate }}</th>
26+
<td mat-cell *matCellDef="let element">{{ element.statusCode }} {{ element.message }}</td>
27+
</ng-container>
28+
29+
<!-- Device column -->
30+
<ng-container matColumnDef="device">
31+
<th mat-header-cell *matHeaderCellDef mat-sort-header>{{ "GEN.DEVICE" | translate }}</th>
32+
<td mat-cell *matCellDef="let element">{{ element.iotDevice?.name }}</td>
33+
</ng-container>
34+
35+
<!-- Actually showing the data -->
36+
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
37+
<tr mat-row *matRowDef="let row; columns: displayedColumns"></tr>
38+
</table>
39+
40+
<!-- Pagination -->
41+
<mat-paginator [pageSizeOptions]="pageSizeOptions" [pageSize]="pageSize" showFirstLastButtons>
42+
</mat-paginator>
43+
</div>
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
:host {
2+
.datatarget-log-container {
3+
margin: 2em;
4+
}
5+
.fa-ok {
6+
color: green !important;
7+
}
8+
.fa-error {
9+
color: red !important;
10+
}
11+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { ComponentFixture, TestBed } from '@angular/core/testing';
2+
3+
import { DatatargetLogComponent } from './datatarget-log.component';
4+
5+
describe('DatatargetLogComponent', () => {
6+
let component: DatatargetLogComponent;
7+
let fixture: ComponentFixture<DatatargetLogComponent>;
8+
9+
beforeEach(async () => {
10+
await TestBed.configureTestingModule({
11+
imports: [DatatargetLogComponent]
12+
})
13+
.compileComponents();
14+
15+
fixture = TestBed.createComponent(DatatargetLogComponent);
16+
component = fixture.componentInstance;
17+
fixture.detectChanges();
18+
});
19+
20+
it('should create', () => {
21+
expect(component).toBeTruthy();
22+
});
23+
});
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import { AfterViewInit, Component, OnDestroy, ViewChild } from "@angular/core";
2+
import { MatPaginator } from "@angular/material/paginator";
3+
import { MatSort } from "@angular/material/sort";
4+
import { MatTableDataSource } from "@angular/material/table";
5+
import { ActivatedRoute } from "@angular/router";
6+
import { environment } from "@environments/environment";
7+
import { faCheckCircle, faExclamationTriangle } from "@fortawesome/free-solid-svg-icons";
8+
import { DefaultPageSizeOptions } from "@shared/constants/page.constants";
9+
import { Subscription } from "rxjs";
10+
import { DatatargetLog } from "./datatarget-log.model";
11+
import { DatatargetLogService } from "./datatarget-log.service";
12+
13+
@Component({
14+
selector: "app-datatarget-log",
15+
templateUrl: "./datatarget-log.component.html",
16+
styleUrl: "./datatarget-log.component.scss",
17+
})
18+
export class DatatargetLogComponent implements OnDestroy, AfterViewInit {
19+
@ViewChild(MatPaginator) paginator: MatPaginator;
20+
@ViewChild(MatSort) sort: MatSort;
21+
22+
displayedColumns: string[] = ["createdAt", "type", "message", "device"];
23+
pageSizeOptions = DefaultPageSizeOptions;
24+
pageSize = environment.tablePageSize;
25+
faExclamationTriangle = faExclamationTriangle;
26+
faCheckCircle = faCheckCircle;
27+
28+
dataSource = new MatTableDataSource<DatatargetLog>();
29+
isLoadingResults = true;
30+
31+
private datatargetLogSubscription: Subscription;
32+
33+
constructor(datatargetLogService: DatatargetLogService, route: ActivatedRoute) {
34+
const id: number = +route.parent.snapshot.paramMap.get("datatargetId");
35+
36+
this.datatargetLogSubscription = datatargetLogService.get(id).subscribe(logs => {
37+
this.dataSource = new MatTableDataSource<DatatargetLog>(logs);
38+
this.setViewChildren();
39+
this.isLoadingResults = false;
40+
});
41+
}
42+
43+
ngAfterViewInit(): void {
44+
this.setViewChildren();
45+
}
46+
47+
ngOnDestroy(): void {
48+
this.datatargetLogSubscription?.unsubscribe();
49+
}
50+
51+
private setViewChildren = () => {
52+
if (this.dataSource) {
53+
this.dataSource.paginator = this.paginator;
54+
this.dataSource.sort = this.sort;
55+
}
56+
};
57+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { IotDevice } from "@applications/iot-devices/iot-device.model";
2+
3+
export class DatatargetLog {
4+
createdAt: Date;
5+
6+
type: string;
7+
message: string;
8+
statusCode?: number;
9+
iotDevice?: IotDevice;
10+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { Injectable } from "@angular/core";
2+
import { RestService } from "@shared/services/rest.service";
3+
import { Observable } from "rxjs";
4+
import { DatatargetLog } from "./datatarget-log.model";
5+
6+
const baseUrl = "datatarget-log";
7+
8+
@Injectable({
9+
providedIn: "root",
10+
})
11+
export class DatatargetLogService {
12+
constructor(private restService: RestService) {}
13+
14+
get(id: number): Observable<DatatargetLog[]> {
15+
return this.restService.get(baseUrl, null, id);
16+
}
17+
}

0 commit comments

Comments
 (0)