Reescrita dos geradores: biblioteca IGenerator, minimal API e novo frontend
- GeradoresService: cada tipo passa a ser um IGenerator (NIF, NIPC, NISS, CC, passaporte, IBAN, NIB, cartões, referência MB, pessoa, telefone, código postal, email, matrícula, EAN, UUID) registado em GeneratorRegistry; remove DAL/EF - GeradoresWS: minimal APIs em /api/v1 com OpenAPI + Scalar e health check - UnitTest: testes de ida e volta, valores conhecidos, detecção e API - geradoresfe: UI construída a partir dos metadados da API, validador livre, exportação CSV/JSON/TXT, nginx com API_UPSTREAM configurável - Docker, docker-compose e Jenkinsfile actualizados Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
85
GeradoresService/GeneratorRegistry.cs
Normal file
85
GeradoresService/GeneratorRegistry.cs
Normal file
@@ -0,0 +1,85 @@
|
||||
using GeradoresService.Core;
|
||||
using GeradoresService.Generators.Contactos;
|
||||
using GeradoresService.Generators.Financeiro;
|
||||
using GeradoresService.Generators.Identificacao;
|
||||
using GeradoresService.Generators.Outros;
|
||||
|
||||
namespace GeradoresService;
|
||||
|
||||
public sealed record DetectionMatch(IGenerator Generator, ValidationResult Result);
|
||||
|
||||
public sealed class GeneratorRegistry
|
||||
{
|
||||
public const int MaxBatch = 1000;
|
||||
|
||||
public IReadOnlyList<IGenerator> All { get; }
|
||||
|
||||
public GeneratorRegistry() : this(
|
||||
[
|
||||
new NifGenerator(),
|
||||
new NipcGenerator(),
|
||||
new NissGenerator(),
|
||||
new CartaoCidadaoGenerator(),
|
||||
new PassaporteGenerator(),
|
||||
new IbanGenerator(),
|
||||
new NibGenerator(),
|
||||
new CartaoCreditoGenerator(),
|
||||
new ReferenciaMultibancoGenerator(),
|
||||
new PessoaGenerator(),
|
||||
new TelefoneGenerator(),
|
||||
new CodigoPostalGenerator(),
|
||||
new EmailGenerator(),
|
||||
new MatriculaGenerator(),
|
||||
new EanGenerator(),
|
||||
new UuidGenerator(),
|
||||
])
|
||||
{ }
|
||||
|
||||
public GeneratorRegistry(IReadOnlyList<IGenerator> generators) => All = generators;
|
||||
|
||||
public IGenerator? Find(string id) =>
|
||||
All.FirstOrDefault(g => string.Equals(g.Id, id, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
/// <summary>Gera <paramref name="count"/> valores. Um <paramref name="seed"/> torna o resultado reprodutível.</summary>
|
||||
public IReadOnlyList<GeneratedItem> Generate(IGenerator generator, int count, IReadOnlyDictionary<string, string?> options, int? seed = null)
|
||||
{
|
||||
if (count is < 1 or > MaxBatch)
|
||||
throw new ArgumentOutOfRangeException(nameof(count), $"A quantidade deve estar entre 1 e {MaxBatch}.");
|
||||
|
||||
var context = new GeneratorContext(seed is { } s ? new Random(s) : Random.Shared, options);
|
||||
var items = new GeneratedItem[count];
|
||||
for (var i = 0; i < count; i++)
|
||||
items[i] = generator.Generate(context);
|
||||
return items;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validador livre: tenta todos os tipos. Devolve primeiro os válidos, depois os que têm o formato
|
||||
/// certo mas falham o controlo (ex.: "parece um NIF mas o dígito está errado").
|
||||
/// </summary>
|
||||
public IReadOnlyList<DetectionMatch> Detect(string value)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(value))
|
||||
return [];
|
||||
|
||||
return All
|
||||
.Where(g => g.CanValidate)
|
||||
.Select(g => new DetectionMatch(g, SafeValidate(g, value)))
|
||||
.Where(m => m.Result.FormatMatch)
|
||||
.OrderByDescending(m => m.Result.IsValid)
|
||||
.ThenByDescending(m => m.Generator.HasChecksum)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
private static ValidationResult SafeValidate(IGenerator generator, string value)
|
||||
{
|
||||
try
|
||||
{
|
||||
return generator.Validate(value);
|
||||
}
|
||||
catch (Exception ex) when (ex is FormatException or ArgumentException or OverflowException)
|
||||
{
|
||||
return ValidationResult.WrongFormat(ex.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user