feat(envelope): Envelopes-Datenquelle und Initialisierung angepasst

- `EnvelopeTypeService` und `EnvelopeTypeController` hinzugefügt, um Envelope-Typen im Cache zu speichern und bereitzustellen.
- `ConfigurationService` erstellt und mit `APP_INITIALIZER` konfiguriert, um Envelope-Typen bei der Anwendungserstellung zu laden.
- `EnvelopeTableComponent` aktualisiert, um Envelope-Typen aus der Konfiguration zu verwenden, anstatt hartkodierte Werte zu nutzen.
This commit is contained in:
Developer 02 2024-09-02 14:48:20 +02:00
parent e9d686a4c1
commit 7444eeba2a
6 changed files with 122 additions and 6 deletions

View File

@ -1,19 +1,31 @@
using AutoMapper; using AutoMapper;
using DigitalData.Core.Application; using DigitalData.Core.Application;
using Microsoft.Extensions.Localization;
using EnvelopeGenerator.Application.Contracts; using EnvelopeGenerator.Application.Contracts;
using EnvelopeGenerator.Application.DTOs; using EnvelopeGenerator.Application.DTOs;
using EnvelopeGenerator.Domain.Entities; using EnvelopeGenerator.Domain.Entities;
using EnvelopeGenerator.Infrastructure.Contracts; using EnvelopeGenerator.Infrastructure.Contracts;
using EnvelopeGenerator.Application.Resources; using Microsoft.Extensions.Caching.Memory;
using DigitalData.Core.DTO;
using System.Collections.Generic;
using Microsoft.Extensions.Logging;
namespace EnvelopeGenerator.Application.Services namespace EnvelopeGenerator.Application.Services
{ {
public class EnvelopeTypeService : BasicCRUDService<IEnvelopeTypeRepository, EnvelopeTypeDto, EnvelopeType, int>, IEnvelopeTypeService public class EnvelopeTypeService : BasicCRUDService<IEnvelopeTypeRepository, EnvelopeTypeDto, EnvelopeType, int>, IEnvelopeTypeService
{ {
public EnvelopeTypeService(IEnvelopeTypeRepository repository, IMapper mapper) private static readonly Guid CacheKey = Guid.NewGuid();
private readonly IMemoryCache _cache;
public EnvelopeTypeService(IEnvelopeTypeRepository repository, IMapper mapper, IMemoryCache cache)
: base(repository, mapper) : base(repository, mapper)
{ {
_cache = cache;
} }
public override async Task<DataResult<IEnumerable<EnvelopeTypeDto>>> ReadAllAsync()
=> await _cache.GetOrCreateAsync(CacheKey, async entry => await base.ReadAllAsync())
?? Result.Fail<IEnumerable<EnvelopeTypeDto>>().Notice(LogLevel.Error, Flag.NotFound, "No cached envelope types are available in the database. If you have added any envelope types after the server started, please restart the server.");
} }
} }

View File

@ -1,4 +1,4 @@
import { ApplicationConfig } from '@angular/core'; import { ApplicationConfig, APP_INITIALIZER } from '@angular/core';
import { provideRouter } from '@angular/router'; import { provideRouter } from '@angular/router';
import { routes } from './app.routes'; import { routes } from './app.routes';
import { provideClientHydration } from '@angular/platform-browser'; import { provideClientHydration } from '@angular/platform-browser';
@ -8,6 +8,7 @@ import { UrlService } from './services/url.service';
import { API_URL } from './tokens/index' import { API_URL } from './tokens/index'
import { HTTP_INTERCEPTORS, provideHttpClient, withFetch } from '@angular/common/http'; import { HTTP_INTERCEPTORS, provideHttpClient, withFetch } from '@angular/common/http';
import { HttpRequestInterceptor } from './http.interceptor'; import { HttpRequestInterceptor } from './http.interceptor';
import { ConfigurationService } from './services/configuration.service';
export const appConfig: ApplicationConfig = { export const appConfig: ApplicationConfig = {
providers: [ providers: [
@ -29,6 +30,12 @@ export const appConfig: ApplicationConfig = {
provide: HTTP_INTERCEPTORS, provide: HTTP_INTERCEPTORS,
useClass: HttpRequestInterceptor, useClass: HttpRequestInterceptor,
multi: true multi: true
},
{
provide: APP_INITIALIZER,
useFactory: (configService: ConfigurationService) => async () => await configService.ngOnInit(),
deps: [ConfigurationService],
multi: true
} }
] ]
}; };

View File

@ -9,6 +9,7 @@ import { MatInputModule } from '@angular/material/input';
import { MatFormFieldModule } from '@angular/material/form-field'; import { MatFormFieldModule } from '@angular/material/form-field';
import {MatPaginator, MatPaginatorModule} from '@angular/material/paginator'; import {MatPaginator, MatPaginatorModule} from '@angular/material/paginator';
import {MatSort, MatSortModule} from '@angular/material/sort'; import {MatSort, MatSortModule} from '@angular/material/sort';
import { ConfigurationService } from '../../services/configuration.service';
@Component({ @Component({
selector: 'app-envelope-table', selector: 'app-envelope-table',
@ -41,7 +42,7 @@ export class EnvelopeTableComponent implements AfterViewInit {
}, },
'type': { 'type': {
header: 'Type', header: 'Type',
field: (element: any) => element.envelope.contractType field: (element: any) => this.config.envelopeTypeTitles[element.envelope.contractType - 1]
}, },
'privateMessage': { 'privateMessage': {
header: 'Private Message', header: 'Private Message',
@ -65,7 +66,7 @@ export class EnvelopeTableComponent implements AfterViewInit {
dataSource = new MatTableDataSource(); dataSource = new MatTableDataSource();
constructor(private erService: EnvelopeReceiverService) { constructor(private erService: EnvelopeReceiverService, private config: ConfigurationService) {
this.dataSource.filterPredicate = (data: any, filter: string): boolean => { this.dataSource.filterPredicate = (data: any, filter: string): boolean => {
const dataStr = Object.values(data as { [key: string]: any }).reduce((currentTerm, key) => { const dataStr = Object.values(data as { [key: string]: any }).reduce((currentTerm, key) => {
return currentTerm + (key ? key.toString().toLowerCase() : ''); return currentTerm + (key ? key.toString().toLowerCase() : '');

View File

@ -0,0 +1,16 @@
import { TestBed } from '@angular/core/testing';
import { ConfigurationService } from './configuration.service';
describe('ConfigurationService', () => {
let service: ConfigurationService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(ConfigurationService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});

View File

@ -0,0 +1,37 @@
import { Injectable, OnInit, inject } from '@angular/core';
import { HttpClient, HttpParams } from '@angular/common/http';
import { Observable, firstValueFrom } from 'rxjs';
import { API_URL } from '../tokens/index';
@Injectable({
providedIn: 'root'
})
export class ConfigurationService implements OnInit {
protected url: string;
private _envelopeTypes! : any[];
private _envelopeTypeTitles! : any[];
constructor(private http: HttpClient) {
const api_url = inject(API_URL);
this.url = `${api_url}`;
}
async ngOnInit(): Promise<void> {
const envelopeTypes$: Observable<any[]> = this.http.get<any[]>(`${this.url}/EnvelopeType`)
envelopeTypes$.subscribe({next: res => {
this._envelopeTypes = res;
this._envelopeTypeTitles = res.map(e => e.title);
}});
}
public get envelopeTypes() {
return this._envelopeTypes;
}
public get envelopeTypeTitles() {
return this._envelopeTypeTitles;
}
}

View File

@ -0,0 +1,43 @@
using DigitalData.Core.DTO;
using EnvelopeGenerator.Application.Contracts;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using System.Net.Mail;
using System.Security.Cryptography.Xml;
namespace EnvelopeGenerator.GeneratorAPI.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class EnvelopeTypeController : ControllerBase
{
private readonly ILogger<EnvelopeTypeController> _logger;
private readonly IEnvelopeTypeService _service;
public EnvelopeTypeController(ILogger<EnvelopeTypeController> logger, IEnvelopeTypeService service)
{
_logger = logger;
_service = service;
}
[HttpGet]
public async Task<IActionResult> GetAllAsync()
{
try
{
return await _service.ReadAllAsync().ThenAsync(
Success: Ok,
Fail: IActionResult (msg, ntc) =>
{
_logger.LogNotice(ntc);
return ntc.HasFlag(Flag.NotFound) ? NotFound() : StatusCode(StatusCodes.Status500InternalServerError);
});
}
catch (Exception ex)
{
_logger.LogError(ex, "{Message}", ex.Message);
return StatusCode(StatusCodes.Status500InternalServerError);
}
}
}
}