Commit /logs function dirs. not commited before, because of .gitignore.

This commit is contained in:
Orlando M Guerreiro 2025-05-23 09:59:37 +01:00
parent c155508d25
commit 7d1d5a213c
10 changed files with 477 additions and 0 deletions

View file

@ -0,0 +1,18 @@
export type Level = 'TRACE' | 'DEBUG' | 'INFO' | 'WARN' | 'ERROR' | 'OFF';
export interface Logger {
configuredLevel: Level | null;
effectiveLevel: Level;
}
export interface LoggersResponse {
levels: Level[];
loggers: { [key: string]: Logger };
}
export class Log {
constructor(
public name: string,
public level: Level,
) {}
}

View file

@ -0,0 +1,82 @@
@defer (when loggers() && !isLoading()) {
<div class="table-responsive">
<h2 id="logs-page-heading" data-cy="logsPageHeading" jhiTranslate="logs.title">Logs</h2>
<p jhiTranslate="logs.nbloggers" [translateValues]="{ total: loggers()?.length }">Existem {{ loggers()?.length }} loggers.</p>
<span jhiTranslate="logs.filter">Filtro</span>
<input type="text" [ngModel]="filter()" (ngModelChange)="filter.set($event)" class="form-control" />
<table class="table table-sm table-striped table-bordered" aria-describedby="logs-page-heading">
<thead>
<tr jhiSort [sortState]="sortState" (sortChange)="sortState.set($event)">
<th jhiSortBy="name" scope="col"><span jhiTranslate="logs.table.name">Nome</span> <fa-icon icon="sort"></fa-icon></th>
<th jhiSortBy="level" scope="col"><span jhiTranslate="logs.table.level">Nível</span> <fa-icon icon="sort"></fa-icon></th>
</tr>
</thead>
<tbody>
@for (logger of filteredAndOrderedLoggers(); track $index) {
<tr>
<td>
<small>{{ logger.name | slice: 0 : 140 }}</small>
</td>
<td>
<button
(click)="changeLevel(logger.name, 'TRACE')"
[ngClass]="logger.level === 'TRACE' ? 'btn-primary' : 'btn-light'"
class="btn btn-sm"
>
TRACE
</button>
<button
(click)="changeLevel(logger.name, 'DEBUG')"
[ngClass]="logger.level === 'DEBUG' ? 'btn-success' : 'btn-light'"
class="btn btn-sm"
>
DEBUG
</button>
<button
(click)="changeLevel(logger.name, 'INFO')"
[ngClass]="logger.level === 'INFO' ? 'btn-info' : 'btn-light'"
class="btn btn-sm"
>
INFO
</button>
<button
(click)="changeLevel(logger.name, 'WARN')"
[ngClass]="logger.level === 'WARN' ? 'btn-warning' : 'btn-light'"
class="btn btn-sm"
>
WARN
</button>
<button
(click)="changeLevel(logger.name, 'ERROR')"
[ngClass]="logger.level === 'ERROR' ? 'btn-danger' : 'btn-light'"
class="btn btn-sm"
>
ERROR
</button>
<button
(click)="changeLevel(logger.name, 'OFF')"
[ngClass]="logger.level === 'OFF' ? 'btn-secondary' : 'btn-light'"
class="btn btn-sm"
>
OFF
</button>
</td>
</tr>
}
</tbody>
</table>
</div>
} @loading {
<div class="d-flex justify-content-center me-3">
<div class="spinner-border"></div>
</div>
}

View file

@ -0,0 +1,82 @@
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
import { HttpClientTestingModule } from '@angular/common/http/testing';
import { of } from 'rxjs';
import LogsComponent from './logs.component';
import { LogsService } from './logs.service';
import { Log, LoggersResponse } from './log.model';
describe('LogsComponent', () => {
let comp: LogsComponent;
let fixture: ComponentFixture<LogsComponent>;
let service: LogsService;
beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
imports: [HttpClientTestingModule, LogsComponent],
providers: [LogsService],
})
.overrideTemplate(LogsComponent, '')
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(LogsComponent);
comp = fixture.componentInstance;
service = TestBed.inject(LogsService);
});
describe('OnInit', () => {
it('should set all default values correctly', () => {
expect(comp.filter()).toBe('');
expect(comp.sortState().predicate).toBe('name');
expect(comp.sortState().order).toBe('asc');
});
it('Should call load all on init', () => {
// GIVEN
const log = new Log('main', 'WARN');
jest.spyOn(service, 'findAll').mockReturnValue(
of({
loggers: {
main: {
effectiveLevel: 'WARN',
},
},
} as unknown as LoggersResponse),
);
// WHEN
comp.ngOnInit();
// THEN
expect(service.findAll).toHaveBeenCalled();
expect(comp.loggers()?.[0]).toEqual(expect.objectContaining(log));
});
});
describe('change log level', () => {
it('should change log level correctly', () => {
// GIVEN
const log = new Log('main', 'ERROR');
jest.spyOn(service, 'changeLevel').mockReturnValue(of({}));
jest.spyOn(service, 'findAll').mockReturnValue(
of({
loggers: {
main: {
effectiveLevel: 'ERROR',
},
},
} as unknown as LoggersResponse),
);
// WHEN
comp.changeLevel('main', 'ERROR');
// THEN
expect(service.changeLevel).toHaveBeenCalled();
expect(service.findAll).toHaveBeenCalled();
expect(comp.loggers()?.[0]).toEqual(expect.objectContaining(log));
});
});
});

View file

@ -0,0 +1,61 @@
import { Component, computed, inject, OnInit, signal } from '@angular/core';
import { finalize } from 'rxjs/operators';
import SharedModule from 'app/shared/shared.module';
import { FormsModule } from '@angular/forms';
import { SortDirective, SortByDirective, sortStateSignal, SortService } from 'app/shared/sort';
import { Log, LoggersResponse, Level } from './log.model';
import { LogsService } from './logs.service';
@Component({
standalone: true,
selector: 'jhi-logs',
templateUrl: './logs.component.html',
imports: [SharedModule, FormsModule, SortDirective, SortByDirective],
})
export default class LogsComponent implements OnInit {
loggers = signal<Log[] | undefined>(undefined);
isLoading = signal(false);
filter = signal('');
sortState = sortStateSignal({ predicate: 'name', order: 'asc' });
filteredAndOrderedLoggers = computed<Log[] | undefined>(() => {
let data = this.loggers() ?? [];
const filter = this.filter();
if (filter) {
data = data.filter(logger => logger.name.toLowerCase().includes(filter.toLowerCase()));
}
const { order, predicate } = this.sortState();
if (order && predicate) {
data = data.sort(this.sortService.startSort({ order, predicate }, { predicate: 'name', order: 'asc' }));
}
return data;
});
private logsService = inject(LogsService);
private sortService = inject(SortService);
ngOnInit(): void {
this.findAndExtractLoggers();
}
changeLevel(name: string, level: Level): void {
this.logsService.changeLevel(name, level).subscribe(() => this.findAndExtractLoggers());
}
private findAndExtractLoggers(): void {
this.isLoading.set(true);
this.logsService
.findAll()
.pipe(
finalize(() => {
this.isLoading.set(false);
}),
)
.subscribe({
next: (response: LoggersResponse) =>
this.loggers.set(Object.entries(response.loggers).map(([key, logger]) => new Log(key, logger.effectiveLevel))),
error: () => this.loggers.set([]),
});
}
}

View file

@ -0,0 +1,31 @@
import { TestBed } from '@angular/core/testing';
import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
import { LogsService } from './logs.service';
describe('Logs Service', () => {
let service: LogsService;
let httpMock: HttpTestingController;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [HttpClientTestingModule],
});
service = TestBed.inject(LogsService);
httpMock = TestBed.inject(HttpTestingController);
});
afterEach(() => {
httpMock.verify();
});
describe('Service methods', () => {
it('should change log level', () => {
service.changeLevel('main', 'ERROR').subscribe();
const req = httpMock.expectOne({ method: 'POST' });
expect(req.request.body).toEqual({ configuredLevel: 'ERROR' });
});
});
});

View file

@ -0,0 +1,20 @@
import { inject, Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { ApplicationConfigService } from 'app/core/config/application-config.service';
import { LoggersResponse, Level } from './log.model';
@Injectable({ providedIn: 'root' })
export class LogsService {
private http = inject(HttpClient);
private applicationConfigService = inject(ApplicationConfigService);
changeLevel(name: string, configuredLevel: Level): Observable<{}> {
return this.http.post(this.applicationConfigService.getEndpointFor(`management/loggers/${name}`), { configuredLevel });
}
findAll(): Observable<LoggersResponse> {
return this.http.get<LoggersResponse>(this.applicationConfigService.getEndpointFor('management/loggers'));
}
}

View file

@ -0,0 +1,4 @@
:host .modal-body {
overflow-y: auto;
max-height: 500px;
}

View file

@ -0,0 +1,32 @@
<div class="modal-header">
<h4 class="modal-title" jhiTranslate="resilientApp.inputDataUploadLogLogsDialog.title">Ocorrências na integração de dados</h4>
</div>
<div class="modal-body">
@if (inputDataUploadLogs?.length === 0) {
<div class="alert alert-warning" id="no-result">
<span jhiTranslate="resilientApp.inputDataUploadLog.home.notFound">Nenhum Input Data Upload Logs encontrado</span>
</div>
}
@if (inputDataUploadLogs && inputDataUploadLogs.length > 0) {
<div class="table-responsive table-entities" id="entities">
<table class="table table-striped" aria-describedby="page-heading">
<tbody>
@for (inputDataUploadLog of inputDataUploadLogs; track trackId) {
<tr data-cy="entityTable">
<td>{{ inputDataUploadLog.logMessage }}</td>
</tr>
}
</tbody>
</table>
</div>
}
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal" (click)="cancel()">
<fa-icon icon="ban"></fa-icon>&nbsp;<span jhiTranslate="resilientApp.inputDataUploadLogLogsDialog.action.close">Fechar</span>
</button>
</div>

View file

@ -0,0 +1,64 @@
jest.mock('@ng-bootstrap/ng-bootstrap');
import { ComponentFixture, TestBed, inject, fakeAsync, tick } from '@angular/core/testing';
import { HttpResponse } from '@angular/common/http';
import { HttpClientTestingModule } from '@angular/common/http/testing';
import { of } from 'rxjs';
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
import { InputDataUploadLogService } from '../service/input-data-upload-log.service';
import { InputDataUploadLogLogsDialogComponent } from './input-data-upload-log-logs-dialog.component';
describe('InputDataUploadLog Management Logs Component', () => {
let comp: InputDataUploadLogLogsDialogComponent;
let fixture: ComponentFixture<InputDataUploadLogLogsDialogComponent>;
let service: InputDataUploadLogService;
let mockActiveModal: NgbActiveModal;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [HttpClientTestingModule, InputDataUploadLoglogsDialogComponent],
providers: [NgbActiveModal],
})
.overrideTemplate(InputDataUploadLogLogsDialogComponent, '')
.compileComponents();
fixture = TestBed.createComponent(InputDataUploadLogLogsDialogComponent);
comp = fixture.componentInstance;
service = TestBed.inject(InputDataUploadLogService);
mockActiveModal = TestBed.inject(NgbActiveModal);
});
/*
describe('confirmDelete', () => {
it('Should call delete service on confirmDelete', inject(
[],
fakeAsync(() => {
// GIVEN
jest.spyOn(service, 'delete').mockReturnValue(of(new HttpResponse({ body: {} })));
// WHEN
comp.confirmDelete(123);
tick();
// THEN
expect(service.delete).toHaveBeenCalledWith(123);
expect(mockActiveModal.close).toHaveBeenCalledWith('deleted');
}),
));
it('Should not call delete service on clear', () => {
// GIVEN
jest.spyOn(service, 'delete');
// WHEN
comp.cancel();
// THEN
expect(service.delete).not.toHaveBeenCalled();
expect(mockActiveModal.close).not.toHaveBeenCalled();
expect(mockActiveModal.dismiss).toHaveBeenCalled();
});
});
*/
});

View file

@ -0,0 +1,83 @@
import { Component, inject, OnInit } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
import { sortStateSignal, SortDirective, SortByDirective, type SortState, SortService } from 'app/shared/sort';
import { combineLatest, filter, Observable, Subscription, tap } from 'rxjs';
import SharedModule from 'app/shared/shared.module';
import { DurationPipe, FormatMediumDatetimePipe, FormatMediumDatePipe } from 'app/shared/date';
import { ITEM_DELETED_EVENT } from 'app/config/navigation.constants';
import { IInputDataUpload } from 'app/entities/input-data-upload/input-data-upload.model';
import { IInputDataUploadLog } from '../input-data-upload-log.model';
import { EntityArrayResponseType, InputDataUploadLogService } from '../service/input-data-upload-log.service';
@Component({
standalone: true,
templateUrl: './input-data-upload-log-logs-dialog.component.html',
styleUrl: './input-data-upload-log-logs-dialog.component.css',
imports: [
SharedModule,
FormsModule,
SortDirective,
SortByDirective,
DurationPipe,
FormatMediumDatetimePipe,
FormatMediumDatePipe,
],
})
export class InputDataUploadLogLogsDialogComponent implements OnInit {
inputDataUpload?: IInputDataUpload;
inputDataUploadLogs?: IInputDataUploadLog[];
isLoading = false;
sortState = sortStateSignal({});
protected sortService = inject(SortService);
protected inputDataUploadLogService = inject(InputDataUploadLogService);
protected activeModal = inject(NgbActiveModal);
trackId = (_index: number, item: IInputDataUploadLog): number => this.inputDataUploadLogService.getEntityIdentifier(item);
setInputDataUpload(inputDataUpload: IInputDataUpload) {
this.inputDataUpload = inputDataUpload;
}
ngOnInit(): void {
if (this.inputDataUpload) {
this.load();
}
}
cancel(): void {
this.activeModal.dismiss();
}
load(): void {
this.queryBackend().subscribe({
next: (res: EntityArrayResponseType) => {
this.onResponseSuccess(res);
},
});
}
protected onResponseSuccess(response: EntityArrayResponseType): void {
const dataFromBody = this.fillComponentAttributesFromResponseBody(response.body);
this.inputDataUploadLogs = this.refineData(dataFromBody);
}
protected refineData(data: IInputDataUploadLog[]): IInputDataUploadLog[] {
const { predicate, order } = this.sortState();
return predicate && order ? data.sort(this.sortService.startSort({ predicate, order })) : data;
}
protected fillComponentAttributesFromResponseBody(data: IInputDataUploadLog[] | null): IInputDataUploadLog[] {
return data ?? [];
}
protected queryBackend(): Observable<EntityArrayResponseType> {
this.isLoading = true;
return this.inputDataUploadLogService.findByOwner(this.inputDataUpload?.id ?? 0).pipe(tap(() => (this.isLoading = false)));
}
}