Nombres de circuitos actualizados
controlador de datos de OPE estructurado
This commit is contained in:
@@ -336,7 +336,23 @@ public sealed class DenunciasController : ControllerBase
|
|||||||
SexoDenunciante: ResolveReportField(denuncia, denuncia.Sexo, "sexo"),
|
SexoDenunciante: ResolveReportField(denuncia, denuncia.Sexo, "sexo"),
|
||||||
AutorizaRemisionDenuncia: ResolveReportField(denuncia, denuncia.AutorizaRemision, "autorizacion para remitir su denuncia", "autoriza remision de la denuncia"),
|
AutorizaRemisionDenuncia: ResolveReportField(denuncia, denuncia.AutorizaRemision, "autorizacion para remitir su denuncia", "autoriza remision de la denuncia"),
|
||||||
AutorizaNotificacionesViaSms: sms,
|
AutorizaNotificacionesViaSms: sms,
|
||||||
PreferenciaNotificacionSeguimientoDenuncia: JoinDistinct(preferenciaNotificacion, seguimiento));
|
PreferenciaNotificacionSeguimientoDenuncia: JoinDistinct(preferenciaNotificacion, seguimiento),
|
||||||
|
DetalleDenunciado: ResolveReportField(denuncia, denuncia.DenunciadoDetalle, "especifique a quien denuncia", "detalle de la persona u organismo denunciado"),
|
||||||
|
PreferenciaNotificacion: preferenciaNotificacion,
|
||||||
|
AutorizaNotificacionesElectronicas: ResolveReportField(
|
||||||
|
denuncia,
|
||||||
|
denuncia.Notificacion_Electronica,
|
||||||
|
"notificaciones electronicas",
|
||||||
|
"notificacion electronica",
|
||||||
|
"autorizacion para notificaciones electronicas"),
|
||||||
|
PreferenciaNotificacionPostal: ResolveReportField(
|
||||||
|
denuncia,
|
||||||
|
denuncia.NotificacionPostal,
|
||||||
|
"autorizo recibir notificaciones via correo postal",
|
||||||
|
"notificaciones via correo postal",
|
||||||
|
"correo postal"),
|
||||||
|
PreferenciaSeguimientoOnline: seguimiento,
|
||||||
|
ComentariosComunicaciones: denuncia.Comments?.Trim() ?? string.Empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static GestionaExternalFieldsResponse ToExternalGestionaFields(GestionaComplaintFieldsResponse source)
|
private static GestionaExternalFieldsResponse ToExternalGestionaFields(GestionaComplaintFieldsResponse source)
|
||||||
@@ -355,7 +371,13 @@ public sealed class DenunciasController : ControllerBase
|
|||||||
["sexoDenunciante"] = StringField(source.SexoDenunciante),
|
["sexoDenunciante"] = StringField(source.SexoDenunciante),
|
||||||
["autorizaRemisionDenuncia"] = StringField(source.AutorizaRemisionDenuncia),
|
["autorizaRemisionDenuncia"] = StringField(source.AutorizaRemisionDenuncia),
|
||||||
["autorizaNotificacionesViaSms"] = StringField(source.AutorizaNotificacionesViaSms),
|
["autorizaNotificacionesViaSms"] = StringField(source.AutorizaNotificacionesViaSms),
|
||||||
["preferenciaNotificacionSeguimientoDenuncia"] = StringField(source.PreferenciaNotificacionSeguimientoDenuncia)
|
["preferenciaNotificacionSeguimientoDenuncia"] = StringField(source.PreferenciaNotificacionSeguimientoDenuncia),
|
||||||
|
["detalleDenunciado"] = StringField(source.DetalleDenunciado),
|
||||||
|
["preferenciaNotificacion"] = StringField(source.PreferenciaNotificacion),
|
||||||
|
["autorizaNotificacionesElectronicas"] = StringField(source.AutorizaNotificacionesElectronicas),
|
||||||
|
["preferenciaNotificacionPostal"] = StringField(source.PreferenciaNotificacionPostal),
|
||||||
|
["preferenciaSeguimientoOnline"] = StringField(source.PreferenciaSeguimientoOnline),
|
||||||
|
["comentariosComunicaciones"] = StringField(source.ComentariosComunicaciones)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -36,16 +36,16 @@
|
|||||||
"Gestiona": {
|
"Gestiona": {
|
||||||
"ApiBase": "https://02.g3stiona.com",
|
"ApiBase": "https://02.g3stiona.com",
|
||||||
"AccessToken": "_yr.xVvPOllsyd1TYZRxUxg__c",
|
"AccessToken": "_yr.xVvPOllsyd1TYZRxUxg__c",
|
||||||
"ProcedureName": "Procedimiento test 2",
|
"ProcedureName": "Procedimiento Genérico",
|
||||||
"ExternalProcedureName": "",
|
"ExternalProcedureName": "Denuncia Pruebas V2",
|
||||||
"ExternalProcedureSiaCode": "3109963",
|
"ExternalProcedureSiaCode": "",
|
||||||
"ManagementUnitGroupCode": "700",
|
"ManagementUnitGroupCode": "700",
|
||||||
"CircuitTemplateName": "Firma automatizada",
|
"CircuitTemplateName": "Firma automatizada",
|
||||||
"CircuitNewComplaintTemplateName": "CT-Nueva denuncia",
|
"CircuitNewComplaintTemplateName": "CT-Nueva denuncia buzon",
|
||||||
"CircuitUpdateSajTemplateName": "CT-Actualización denuncia SAJ",
|
"CircuitUpdateSajTemplateName": "CT-Actualización denuncia Buzon SAJ",
|
||||||
"CircuitUpdateSdiTemplateName": "CT-Actualización denuncia SDI",
|
"CircuitUpdateSdiTemplateName": "CT-Actualización denuncia buzon SDI",
|
||||||
"CircuitCommunicationSajTemplateName": "CT-Comunicación SAJ a denunciante",
|
"CircuitCommunicationSajTemplateName": "CT-Comunicación SAJ buzon a denunciante",
|
||||||
"CircuitCommunicationSdiTemplateName": "CT-Comunicación SDI a denunciante",
|
"CircuitCommunicationSdiTemplateName": "CT-Comunicación SDI buzon a denunciante",
|
||||||
"CircuitSignerStampTitle": "oaaf-complaints-tramit",
|
"CircuitSignerStampTitle": "oaaf-complaints-tramit",
|
||||||
"CircuitVersion": "2",
|
"CircuitVersion": "2",
|
||||||
"DocumentMetadataLanguage": "es",
|
"DocumentMetadataLanguage": "es",
|
||||||
|
|||||||
@@ -1,46 +1,63 @@
|
|||||||
using ApiOPE.Configuration;
|
|
||||||
using ApiOPE.Contracts;
|
using ApiOPE.Contracts;
|
||||||
using ApiOPE.Services;
|
using ApiOPE.Services;
|
||||||
using Microsoft.Extensions.Options;
|
|
||||||
|
|
||||||
namespace ApiOPE.Tests;
|
namespace ApiOPE.Tests;
|
||||||
|
|
||||||
public sealed class OpeFieldMapperTests
|
public sealed class OpeFieldMapperTests
|
||||||
{
|
{
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Map_UsesConfiguredOrderAndFieldNames()
|
public void MapBlock_MapsFirstBlockInAgreedOrder()
|
||||||
{
|
{
|
||||||
var options = Options.Create(new OpeOptions
|
var mapper = CreateMapper();
|
||||||
{
|
var source = CreateBlockSource();
|
||||||
OutputFields = ["numeroDenunciaCanal", "fechaDenuncia", "resumenDenuncia"]
|
|
||||||
});
|
|
||||||
var mapper = new OpeFieldMapper(options);
|
|
||||||
var source = new InternalGestionaFieldsResponse(
|
|
||||||
new Dictionary<string, OpeFieldValue>
|
|
||||||
{
|
|
||||||
["fechaDenuncia"] = new("STRING", "2026-07-10"),
|
|
||||||
["numeroDenunciaCanal"] = new("STRING", "116"),
|
|
||||||
["resumenDenuncia"] = new("STRING", "Prueba")
|
|
||||||
});
|
|
||||||
|
|
||||||
var result = mapper.Map(source);
|
var result = mapper.MapBlock(source, 1, belongsToComplaintsChannel: true);
|
||||||
|
|
||||||
Assert.Equal("116", result.Data["FIELD_0"].Value);
|
Assert.Equal("SI", result.Data["FIELD_0"].Value);
|
||||||
Assert.Equal("2026-07-10", result.Data["FIELD_1"].Value);
|
Assert.Equal("2026-09-11", result.Data["FIELD_1"].Value);
|
||||||
Assert.Equal("Prueba", result.Data["FIELD_2"].Value);
|
Assert.Equal("123", result.Data["FIELD_2"].Value);
|
||||||
|
Assert.Equal("Ambito", result.Data["FIELD_9"].Value);
|
||||||
|
Assert.Equal(10, result.Data.Count);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Map_RejectsMissingInternalField()
|
public void MapBlock_MapsSecondBlockAndLeavesLastTwoFieldsEmpty()
|
||||||
{
|
{
|
||||||
var mapper = new OpeFieldMapper(Options.Create(new OpeOptions
|
var mapper = CreateMapper();
|
||||||
{
|
var source = CreateBlockSource();
|
||||||
OutputFields = ["fechaDenuncia"]
|
|
||||||
}));
|
|
||||||
var source = new InternalGestionaFieldsResponse(
|
|
||||||
new Dictionary<string, OpeFieldValue>());
|
|
||||||
|
|
||||||
Assert.Throws<InvalidDataException>(() => mapper.Map(source));
|
var result = mapper.MapBlock(source, 2, belongsToComplaintsChannel: true);
|
||||||
|
|
||||||
|
Assert.Equal("Ayuntamiento", result.Data["FIELD_0"].Value);
|
||||||
|
Assert.Equal("Comentarios", result.Data["FIELD_7"].Value);
|
||||||
|
Assert.Equal(string.Empty, result.Data["FIELD_8"].Value);
|
||||||
|
Assert.Equal(string.Empty, result.Data["FIELD_9"].Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void MapBlock_ReturnsNoAndEmptyFieldsForUnknownExpedient()
|
||||||
|
{
|
||||||
|
var mapper = CreateMapper();
|
||||||
|
|
||||||
|
var firstBlock = mapper.MapBlock(null, 1, belongsToComplaintsChannel: false);
|
||||||
|
var secondBlock = mapper.MapBlock(null, 2, belongsToComplaintsChannel: false);
|
||||||
|
|
||||||
|
Assert.Equal("NO", firstBlock.Data["FIELD_0"].Value);
|
||||||
|
Assert.All(firstBlock.Data.Where(field => field.Key != "FIELD_0"), field => Assert.Empty(field.Value.Value));
|
||||||
|
Assert.All(secondBlock.Data, field => Assert.Empty(field.Value.Value));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void MapBlock_ReturnsYesAndEmptyDataForPurgedChannelExpedient()
|
||||||
|
{
|
||||||
|
var mapper = CreateMapper();
|
||||||
|
|
||||||
|
var firstBlock = mapper.MapBlock(null, 1, belongsToComplaintsChannel: true);
|
||||||
|
var secondBlock = mapper.MapBlock(null, 2, belongsToComplaintsChannel: true);
|
||||||
|
|
||||||
|
Assert.Equal("SI", firstBlock.Data["FIELD_0"].Value);
|
||||||
|
Assert.All(firstBlock.Data.Where(field => field.Key != "FIELD_0"), field => Assert.Empty(field.Value.Value));
|
||||||
|
Assert.All(secondBlock.Data, field => Assert.Empty(field.Value.Value));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
@@ -54,39 +71,28 @@ public sealed class OpeFieldMapperTests
|
|||||||
Assert.Equal(expectedPath, lookup!.RelativePath);
|
Assert.Equal(expectedPath, lookup!.RelativePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory]
|
private static OpeFieldMapper CreateMapper()
|
||||||
[InlineData(0)]
|
=> new();
|
||||||
[InlineData(11)]
|
|
||||||
public void OptionsValidator_RejectsInvalidOutputCount(int count)
|
|
||||||
{
|
|
||||||
var options = new OpeOptions
|
|
||||||
{
|
|
||||||
ClientToken = "client",
|
|
||||||
SecretKey = "test-secret-key-with-more-than-32-characters",
|
|
||||||
PublicBaseUrl = "https://ope.example.test",
|
|
||||||
OrganizationId = "organization",
|
|
||||||
OrganizationDir3 = "dir3",
|
|
||||||
OrganizationCif = "cif",
|
|
||||||
OutputFields = Enumerable.Repeat("fechaDenuncia", count).ToList()
|
|
||||||
};
|
|
||||||
|
|
||||||
Assert.False(OpeOptionsValidator.IsValid(options, isDevelopment: false));
|
private static InternalGestionaFieldsResponse CreateBlockSource()
|
||||||
}
|
=> new(new Dictionary<string, OpeFieldValue>
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void OptionsValidator_AcceptsTenUniqueConfiguredFields()
|
|
||||||
{
|
{
|
||||||
var options = new OpeOptions
|
["fechaDenuncia"] = new("STRING", "2026-09-11"),
|
||||||
{
|
["numeroDenunciaCanal"] = new("STRING", "123"),
|
||||||
ClientToken = "client",
|
["sexoDenunciante"] = new("STRING", "Mujer"),
|
||||||
SecretKey = "test-secret-key-with-more-than-32-characters",
|
["preferenciaNotificacion"] = new("STRING", "Electronica"),
|
||||||
PublicBaseUrl = "https://ope.example.test",
|
["autorizaNotificacionesElectronicas"] = new("STRING", "Si"),
|
||||||
OrganizationId = "organization",
|
["autorizaNotificacionesViaSms"] = new("STRING", "No"),
|
||||||
OrganizationDir3 = "dir3",
|
["preferenciaNotificacionPostal"] = new("STRING", "No"),
|
||||||
OrganizationCif = "cif",
|
["preferenciaSeguimientoOnline"] = new("STRING", "Si"),
|
||||||
OutputFields = OpeFieldMapper.SupportedInternalFields.Take(10).ToList()
|
["ambitoCompetencias"] = new("STRING", "Ambito"),
|
||||||
};
|
["aQuienDenuncia"] = new("STRING", "Ayuntamiento"),
|
||||||
|
["detalleDenunciado"] = new("STRING", "Detalle"),
|
||||||
Assert.True(OpeOptionsValidator.IsValid(options, isDevelopment: false));
|
["resumenDenuncia"] = new("STRING", "Resumen"),
|
||||||
}
|
["solicitaProteccion"] = new("STRING", "Si"),
|
||||||
|
["lugarHechos"] = new("STRING", "Cadiz"),
|
||||||
|
["fechaHechos"] = new("STRING", "10/09/2026"),
|
||||||
|
["autorizaRemisionDenuncia"] = new("STRING", "Si"),
|
||||||
|
["comentariosComunicaciones"] = new("STRING", "Comentarios")
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ public sealed class OpeProtocolTests
|
|||||||
var context = CreateHttpContext(
|
var context = CreateHttpContext(
|
||||||
"application/vnd.generic-operation-request+json; charset=utf-8",
|
"application/vnd.generic-operation-request+json; charset=utf-8",
|
||||||
"""
|
"""
|
||||||
{"data":{"FIELD_0":{"type":"STRING","value":"53/2026"}}}
|
{"data":{"FIELD_0":{"type":"STRING","value":"53/2026"},"FIELD_1":{"type":"STRING","value":"1"}}}
|
||||||
""");
|
""");
|
||||||
|
|
||||||
var result = await GenericOperationRequestReader.ReadAsync(
|
var result = await GenericOperationRequestReader.ReadAsync(
|
||||||
@@ -53,6 +53,7 @@ public sealed class OpeProtocolTests
|
|||||||
|
|
||||||
Assert.True(result.IsValid);
|
Assert.True(result.IsValid);
|
||||||
Assert.Equal("53/2026", result.Request!.Data["FIELD_0"].Value);
|
Assert.Equal("53/2026", result.Request!.Data["FIELD_0"].Value);
|
||||||
|
Assert.Equal("1", result.Request.Data["FIELD_1"].Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
@@ -61,7 +62,7 @@ public sealed class OpeProtocolTests
|
|||||||
var context = CreateHttpContext(
|
var context = CreateHttpContext(
|
||||||
OpeMediaTypes.GenericOperationRequest,
|
OpeMediaTypes.GenericOperationRequest,
|
||||||
"""
|
"""
|
||||||
{"data":{"FIELD_0":{"type":"STRING","value":"68/2026"}},"links":null}
|
{"data":{"FIELD_0":{"type":"STRING","value":"68/2026"},"FIELD_1":{"type":"STRING","value":"2"}},"links":null}
|
||||||
""");
|
""");
|
||||||
|
|
||||||
var result = await GenericOperationRequestReader.ReadAsync(
|
var result = await GenericOperationRequestReader.ReadAsync(
|
||||||
@@ -78,7 +79,7 @@ public sealed class OpeProtocolTests
|
|||||||
var context = CreateHttpContext(
|
var context = CreateHttpContext(
|
||||||
OpeMediaTypes.GenericOperationRequest,
|
OpeMediaTypes.GenericOperationRequest,
|
||||||
"""
|
"""
|
||||||
{"data":{"FIELD_1":{"type":"STRING","value":"unexpected"}}}
|
{"data":{"FIELD_2":{"type":"STRING","value":"unexpected"}}}
|
||||||
""");
|
""");
|
||||||
|
|
||||||
var result = await GenericOperationRequestReader.ReadAsync(
|
var result = await GenericOperationRequestReader.ReadAsync(
|
||||||
@@ -86,8 +87,26 @@ public sealed class OpeProtocolTests
|
|||||||
CancellationToken.None);
|
CancellationToken.None);
|
||||||
|
|
||||||
Assert.False(result.IsValid);
|
Assert.False(result.IsValid);
|
||||||
Assert.Equal(OpeFieldErrors.NotExpected, result.Errors["FIELD_1"]);
|
Assert.Equal(OpeFieldErrors.NotExpected, result.Errors["FIELD_2"]);
|
||||||
Assert.Equal(OpeFieldErrors.Expected, result.Errors["FIELD_0"]);
|
Assert.Equal(OpeFieldErrors.Expected, result.Errors["FIELD_0"]);
|
||||||
|
Assert.Equal(OpeFieldErrors.Expected, result.Errors["FIELD_1"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task RequestReader_RejectsUnknownBlock()
|
||||||
|
{
|
||||||
|
var context = CreateHttpContext(
|
||||||
|
OpeMediaTypes.GenericOperationRequest,
|
||||||
|
"""
|
||||||
|
{"data":{"FIELD_0":{"type":"STRING","value":"68/2026"},"FIELD_1":{"type":"STRING","value":"3"}}}
|
||||||
|
""");
|
||||||
|
|
||||||
|
var result = await GenericOperationRequestReader.ReadAsync(
|
||||||
|
context.Request,
|
||||||
|
CancellationToken.None);
|
||||||
|
|
||||||
|
Assert.False(result.IsValid);
|
||||||
|
Assert.Equal(OpeFieldErrors.UnexpectedFormat, result.Errors["FIELD_1"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static DefaultHttpContext CreateHttpContext(string contentType, string body)
|
private static DefaultHttpContext CreateHttpContext(string contentType, string body)
|
||||||
|
|||||||
@@ -34,6 +34,10 @@ X-Organization-CIF: {{OrganizationCif}}
|
|||||||
"FIELD_0": {
|
"FIELD_0": {
|
||||||
"type": "STRING",
|
"type": "STRING",
|
||||||
"value": "53/2026"
|
"value": "53/2026"
|
||||||
|
},
|
||||||
|
"FIELD_1": {
|
||||||
|
"type": "STRING",
|
||||||
|
"value": "1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
using ApiOPE.Services;
|
|
||||||
|
|
||||||
namespace ApiOPE.Configuration;
|
namespace ApiOPE.Configuration;
|
||||||
|
|
||||||
public sealed class OpeOptions
|
public sealed class OpeOptions
|
||||||
@@ -25,8 +23,6 @@ public sealed class OpeOptions
|
|||||||
public string OrganizationDir3 { get; set; } = string.Empty;
|
public string OrganizationDir3 { get; set; } = string.Empty;
|
||||||
|
|
||||||
public string OrganizationCif { get; set; } = string.Empty;
|
public string OrganizationCif { get; set; } = string.Empty;
|
||||||
|
|
||||||
public List<string> OutputFields { get; set; } = [];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class OpeOptionsValidator
|
public static class OpeOptionsValidator
|
||||||
@@ -41,11 +37,7 @@ public static class OpeOptionsValidator
|
|||||||
string.IsNullOrWhiteSpace(options.OrganizationDir3) ||
|
string.IsNullOrWhiteSpace(options.OrganizationDir3) ||
|
||||||
string.IsNullOrWhiteSpace(options.OrganizationCif) ||
|
string.IsNullOrWhiteSpace(options.OrganizationCif) ||
|
||||||
options.TokenMaxAgeSeconds is < 1 or > 900 ||
|
options.TokenMaxAgeSeconds is < 1 or > 900 ||
|
||||||
options.ClockSkewSeconds is < 0 or > 300 ||
|
options.ClockSkewSeconds is < 0 or > 300)
|
||||||
options.OutputFields is null ||
|
|
||||||
options.OutputFields.Count is < 1 or > 10 ||
|
|
||||||
options.OutputFields.Distinct(StringComparer.Ordinal).Count() != options.OutputFields.Count ||
|
|
||||||
options.OutputFields.Any(field => !OpeFieldMapper.SupportedInternalFields.Contains(field)))
|
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,13 +15,19 @@ public sealed class OpeController : ControllerBase
|
|||||||
private static readonly JsonSerializerOptions JsonOptions = new(JsonSerializerDefaults.Web);
|
private static readonly JsonSerializerOptions JsonOptions = new(JsonSerializerDefaults.Web);
|
||||||
|
|
||||||
private readonly OpeOptions _options;
|
private readonly OpeOptions _options;
|
||||||
|
private readonly InternalDenunciasClient _internalApi;
|
||||||
|
private readonly OpeFieldMapper _fieldMapper;
|
||||||
private readonly ILogger<OpeController> _logger;
|
private readonly ILogger<OpeController> _logger;
|
||||||
|
|
||||||
public OpeController(
|
public OpeController(
|
||||||
IOptions<OpeOptions> options,
|
IOptions<OpeOptions> options,
|
||||||
|
InternalDenunciasClient internalApi,
|
||||||
|
OpeFieldMapper fieldMapper,
|
||||||
ILogger<OpeController> logger)
|
ILogger<OpeController> logger)
|
||||||
{
|
{
|
||||||
_options = options.Value;
|
_options = options.Value;
|
||||||
|
_internalApi = internalApi;
|
||||||
|
_fieldMapper = fieldMapper;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,6 +71,7 @@ public sealed class OpeController : ControllerBase
|
|||||||
}
|
}
|
||||||
|
|
||||||
var identifier = parsedRequest.Request!.Data["FIELD_0"].Value;
|
var identifier = parsedRequest.Request!.Data["FIELD_0"].Value;
|
||||||
|
var block = int.Parse(parsedRequest.Request.Data["FIELD_1"].Value, System.Globalization.CultureInfo.InvariantCulture);
|
||||||
if (!DenunciaLookupParser.TryParse(identifier, out var lookup) || lookup is null)
|
if (!DenunciaLookupParser.TryParse(identifier, out var lookup) || lookup is null)
|
||||||
{
|
{
|
||||||
return Error(
|
return Error(
|
||||||
@@ -76,25 +83,72 @@ public sealed class OpeController : ControllerBase
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Respuesta temporal de pruebas: no consulta la API interna ni exige
|
try
|
||||||
// que el expediente exista. FIELD_0 conserva exactamente el valor
|
{
|
||||||
// recibido y el resto de campos devuelve un valor ficticio.
|
var result = await _internalApi.GetFieldsAsync(
|
||||||
|
lookup,
|
||||||
|
requestContext.TransactionId,
|
||||||
|
cancellationToken);
|
||||||
|
|
||||||
|
if (result.Status == InternalLookupStatus.NotFound)
|
||||||
|
{
|
||||||
_logger.LogInformation(
|
_logger.LogInformation(
|
||||||
"Respuesta OPE de prueba para el expediente solicitado {Expediente}. TransactionId={TransactionId}",
|
"El expediente {Expediente} no pertenece al canal de denuncias. Bloque={Block}; TransactionId={TransactionId}",
|
||||||
identifier,
|
identifier,
|
||||||
|
block,
|
||||||
requestContext.TransactionId);
|
requestContext.TransactionId);
|
||||||
|
return Json(
|
||||||
var testData = new Dictionary<string, OpeFieldValue>(StringComparer.Ordinal)
|
_fieldMapper.MapBlock(null, block, belongsToComplaintsChannel: false),
|
||||||
{
|
OpeMediaTypes.GenericOperationResponse);
|
||||||
["FIELD_0"] = new OpeFieldValue("STRING", identifier)
|
|
||||||
};
|
|
||||||
|
|
||||||
for (var index = 1; index < _options.OutputFields.Count; index++)
|
|
||||||
{
|
|
||||||
testData[$"FIELD_{index}"] = new OpeFieldValue("STRING", "dato de prueba");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return Json(new OpeDataEnvelope(testData), OpeMediaTypes.GenericOperationResponse);
|
if (result.Status == InternalLookupStatus.DataUnavailable || result.Fields is null)
|
||||||
|
{
|
||||||
|
_logger.LogInformation(
|
||||||
|
"El expediente {Expediente} pertenece al canal, pero sus datos ya no estan disponibles. Bloque={Block}; TransactionId={TransactionId}",
|
||||||
|
identifier,
|
||||||
|
block,
|
||||||
|
requestContext.TransactionId);
|
||||||
|
return Json(
|
||||||
|
_fieldMapper.MapBlock(null, block, belongsToComplaintsChannel: true),
|
||||||
|
OpeMediaTypes.GenericOperationResponse);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Json(
|
||||||
|
_fieldMapper.MapBlock(result.Fields, block, belongsToComplaintsChannel: true),
|
||||||
|
OpeMediaTypes.GenericOperationResponse);
|
||||||
|
}
|
||||||
|
catch (OperationCanceledException) when (!HttpContext.RequestAborted.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
_logger.LogWarning(
|
||||||
|
"Timeout consultando ApiDenuncias para una OPE. TransactionId={TransactionId}",
|
||||||
|
requestContext.TransactionId);
|
||||||
|
return Error("Error en el conector", OpeErrorCauses.ConnectorError, null);
|
||||||
|
}
|
||||||
|
catch (HttpRequestException exception)
|
||||||
|
{
|
||||||
|
_logger.LogWarning(
|
||||||
|
exception,
|
||||||
|
"No se ha podido conectar con ApiDenuncias. TransactionId={TransactionId}",
|
||||||
|
requestContext.TransactionId);
|
||||||
|
return Error("Error en el conector", OpeErrorCauses.ConnectorError, null);
|
||||||
|
}
|
||||||
|
catch (InternalApiException exception)
|
||||||
|
{
|
||||||
|
_logger.LogWarning(
|
||||||
|
exception,
|
||||||
|
"ApiDenuncias ha devuelto una respuesta no utilizable. TransactionId={TransactionId}",
|
||||||
|
requestContext.TransactionId);
|
||||||
|
return Error("Error en el conector", OpeErrorCauses.ConnectorError, null);
|
||||||
|
}
|
||||||
|
catch (InvalidDataException exception)
|
||||||
|
{
|
||||||
|
_logger.LogError(
|
||||||
|
exception,
|
||||||
|
"El contrato entre ApiOPE y ApiDenuncias no coincide. TransactionId={TransactionId}",
|
||||||
|
requestContext.TransactionId);
|
||||||
|
return Error("Error en el conector", OpeErrorCauses.ConnectorError, null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ContentResult Error(
|
private static ContentResult Error(
|
||||||
|
|||||||
@@ -43,6 +43,12 @@
|
|||||||
"value": "REEMPLAZAR_EXPEDIENTE_NO_PURGADO",
|
"value": "REEMPLAZAR_EXPEDIENTE_NO_PURGADO",
|
||||||
"type": "default",
|
"type": "default",
|
||||||
"enabled": true
|
"enabled": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "bloque_datos",
|
||||||
|
"value": "1",
|
||||||
|
"type": "default",
|
||||||
|
"enabled": true
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"_postman_variable_scope": "environment",
|
"_postman_variable_scope": "environment",
|
||||||
|
|||||||
@@ -125,6 +125,10 @@
|
|||||||
" if (!expediente || expediente.startsWith('REEMPLAZAR_')) {",
|
" if (!expediente || expediente.startsWith('REEMPLAZAR_')) {",
|
||||||
" throw new Error('Debes indicar un expediente no purgado en expediente_gestiona');",
|
" throw new Error('Debes indicar un expediente no purgado en expediente_gestiona');",
|
||||||
" }",
|
" }",
|
||||||
|
" const bloque = pm.variables.get('bloque_datos');",
|
||||||
|
" if (bloque !== '1' && bloque !== '2') {",
|
||||||
|
" throw new Error('bloque_datos debe valer 1 o 2');",
|
||||||
|
" }",
|
||||||
"}",
|
"}",
|
||||||
"",
|
"",
|
||||||
"if (current.resetTransaction || !pm.collectionVariables.get('ope_transaction_id')) {",
|
"if (current.resetTransaction || !pm.collectionVariables.get('ope_transaction_id')) {",
|
||||||
@@ -338,7 +342,7 @@
|
|||||||
],
|
],
|
||||||
"body": {
|
"body": {
|
||||||
"mode": "raw",
|
"mode": "raw",
|
||||||
"raw": "{\n \"data\": {\n \"FIELD_0\": {\n \"type\": \"STRING\",\n \"value\": \"{{expediente_gestiona}}\"\n }\n }\n}",
|
"raw": "{\n \"data\": {\n \"FIELD_0\": {\n \"type\": \"STRING\",\n \"value\": \"{{expediente_gestiona}}\"\n },\n \"FIELD_1\": {\n \"type\": \"STRING\",\n \"value\": \"{{bloque_datos}}\"\n }\n },\n \"links\": null\n}",
|
||||||
"options": {
|
"options": {
|
||||||
"raw": {
|
"raw": {
|
||||||
"language": "json"
|
"language": "json"
|
||||||
@@ -346,7 +350,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"url": "{{base_url}}/genericoperations",
|
"url": "{{base_url}}/genericoperations",
|
||||||
"description": "Tercera llamada de Gestiona. FIELD_0 contiene el numero de expediente, por ejemplo 53/2026."
|
"description": "Tercera llamada de Gestiona. FIELD_0 contiene el numero de expediente y FIELD_1 selecciona el bloque de datos 1 o 2."
|
||||||
},
|
},
|
||||||
"response": []
|
"response": []
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ Variables que debe rellenar cada probador
|
|||||||
- organization_dir3: codigo DIR3 de la organizacion remitente.
|
- organization_dir3: codigo DIR3 de la organizacion remitente.
|
||||||
- organization_cif: CIF de la organizacion remitente.
|
- organization_cif: CIF de la organizacion remitente.
|
||||||
- expediente_gestiona: numero de un expediente de Gestiona que exista y cuyos datos diarios sigan disponibles, por ejemplo 53/2026.
|
- expediente_gestiona: numero de un expediente de Gestiona que exista y cuyos datos diarios sigan disponibles, por ejemplo 53/2026.
|
||||||
|
- bloque_datos: bloque que se desea recuperar. Debe valer 1 o 2.
|
||||||
|
|
||||||
Las credenciales no se incluyen en este paquete. Deben facilitarse por un canal seguro y guardarse solo como valores locales del entorno de Postman.
|
Las credenciales no se incluyen en este paquete. Deben facilitarse por un canal seguro y guardarse solo como valores locales del entorno de Postman.
|
||||||
|
|
||||||
@@ -38,6 +39,6 @@ Resultado esperado
|
|||||||
------------------
|
------------------
|
||||||
- Las tres respuestas devuelven HTTP 200.
|
- Las tres respuestas devuelven HTTP 200.
|
||||||
- Los tests de Postman aparecen en verde.
|
- Los tests de Postman aparecen en verde.
|
||||||
- La tercera respuesta contiene los campos disponibles del expediente solicitado.
|
- La tercera respuesta contiene siempre FIELD_0 a FIELD_9 para el bloque solicitado.
|
||||||
|
|
||||||
Si la peticion no llega al servidor, comprobar que el equipo resuelve gesbuzpre.antifraudeandalucia.es y tiene acceso al puerto 7094.
|
Si la peticion no llega al servidor, comprobar que el equipo resuelve gesbuzpre.antifraudeandalucia.es y tiene acceso al puerto 7094.
|
||||||
|
|||||||
@@ -9,8 +9,7 @@ permanecer en la red interna.
|
|||||||
1. Gestiona consulta `GET /versions/current`.
|
1. Gestiona consulta `GET /versions/current`.
|
||||||
2. Gestiona consulta `GET /` y obtiene el bookmark de la operacion generica.
|
2. Gestiona consulta `GET /` y obtiene el bookmark de la operacion generica.
|
||||||
3. Gestiona llama a `POST /genericoperations` con el numero de expediente
|
3. Gestiona llama a `POST /genericoperations` con el numero de expediente
|
||||||
(`53/2026`) en `FIELD_0`. Para pruebas tambien se admite el identificador de
|
(`53/2026`) en `FIELD_0` y el bloque solicitado (`1` o `2`) en `FIELD_1`.
|
||||||
denuncia (`116`).
|
|
||||||
4. `ApiOPE` consulta el endpoint protegido correspondiente de `ApiDenuncias`.
|
4. `ApiOPE` consulta el endpoint protegido correspondiente de `ApiDenuncias`.
|
||||||
5. `ApiOPE` transforma la respuesta interna al formato `FIELD_0`...`FIELD_9` y
|
5. `ApiOPE` transforma la respuesta interna al formato `FIELD_0`...`FIELD_9` y
|
||||||
la devuelve a Gestiona.
|
la devuelve a Gestiona.
|
||||||
@@ -52,7 +51,6 @@ Ope__OrganizationDir3
|
|||||||
Ope__OrganizationCif
|
Ope__OrganizationCif
|
||||||
Ope__TokenMaxAgeSeconds
|
Ope__TokenMaxAgeSeconds
|
||||||
Ope__ClockSkewSeconds
|
Ope__ClockSkewSeconds
|
||||||
Ope__OutputFields__0 ... Ope__OutputFields__9
|
|
||||||
|
|
||||||
InternalApi__BaseUrl
|
InternalApi__BaseUrl
|
||||||
InternalApi__ApiKey
|
InternalApi__ApiKey
|
||||||
@@ -71,8 +69,8 @@ OpeBridge__ApiKeyHeaderName=X-ApiOPE-Key
|
|||||||
incluido cualquier path base. El bookmark se construye a partir de ese valor.
|
incluido cualquier path base. El bookmark se construye a partir de ese valor.
|
||||||
|
|
||||||
La aplicacion valida la configuracion al arrancar. Si faltan el secreto, los
|
La aplicacion valida la configuracion al arrancar. Si faltan el secreto, los
|
||||||
datos de organizacion, la URL publica o el mapa de salida, no inicia para evitar
|
datos de organizacion o la URL publica, no inicia para evitar publicar un
|
||||||
publicar un conector incompleto.
|
conector incompleto.
|
||||||
|
|
||||||
### Prueba temporal de preproduccion
|
### Prueba temporal de preproduccion
|
||||||
|
|
||||||
@@ -96,26 +94,15 @@ volver a usar un entorno de produccion.
|
|||||||
|
|
||||||
## Campos de salida
|
## Campos de salida
|
||||||
|
|
||||||
La API interna ofrece estos 12 campos:
|
La operacion siempre devuelve `FIELD_0` a `FIELD_9` como valores `STRING`.
|
||||||
|
`FIELD_1=1` solicita el primer bloque: pertenencia al canal (`SI` o `NO`), fecha,
|
||||||
|
numero de denuncia, sexo, preferencias de notificacion y ambito. `FIELD_1=2`
|
||||||
|
solicita el segundo bloque: denunciado, detalle, resumen, proteccion, lugar,
|
||||||
|
fecha de los hechos, autorizacion de remision y comentarios; sus dos ultimas
|
||||||
|
posiciones quedan vacias.
|
||||||
|
|
||||||
```text
|
Si el expediente no pertenece al canal, el primer bloque devuelve `NO` y el
|
||||||
fechaDenuncia
|
resto vacio. Para el segundo bloque se devuelven los diez campos vacios.
|
||||||
numeroDenunciaCanal
|
|
||||||
aQuienDenuncia
|
|
||||||
resumenDenuncia
|
|
||||||
fechaHechos
|
|
||||||
lugarHechos
|
|
||||||
ambitoCompetencias
|
|
||||||
solicitaProteccion
|
|
||||||
sexoDenunciante
|
|
||||||
autorizaRemisionDenuncia
|
|
||||||
autorizaNotificacionesViaSms
|
|
||||||
preferenciaNotificacionSeguimientoDenuncia
|
|
||||||
```
|
|
||||||
|
|
||||||
OPE 1.0 admite como maximo 10 campos por operacion. Antes de configurar PRE hay
|
|
||||||
que acordar cuales diez se publican o dividir la consulta en dos operaciones.
|
|
||||||
El orden de `Ope__OutputFields__N` determina la correspondencia con `FIELD_N`.
|
|
||||||
|
|
||||||
## Publicacion
|
## Publicacion
|
||||||
|
|
||||||
@@ -129,8 +116,8 @@ El orden de `Ope__OutputFields__N` determina la correspondencia con `FIELD_N`.
|
|||||||
5. Validar consecutivamente version, bookmarks y operacion generica, incluida
|
5. Validar consecutivamente version, bookmarks y operacion generica, incluida
|
||||||
la cabecera `Signature` de cada respuesta.
|
la cabecera `Signature` de cada respuesta.
|
||||||
|
|
||||||
Los logs identifican fallos mediante `transaction_id`, pero nunca registran el
|
El registro diagnostico de PRE incluye cabeceras y cuerpos de peticion y
|
||||||
JWT, el secreto compartido, la clave interna ni el contenido de la denuncia.
|
respuesta. El JWT, las cookies y los secretos se ocultan siempre.
|
||||||
|
|
||||||
## Simulacion desde Postman
|
## Simulacion desde Postman
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,8 @@ public sealed record GenericOperationRequestResult(
|
|||||||
|
|
||||||
public static class GenericOperationRequestReader
|
public static class GenericOperationRequestReader
|
||||||
{
|
{
|
||||||
private const string ExpectedField = "FIELD_0";
|
private const string ExpedienteField = "FIELD_0";
|
||||||
|
private const string BlockField = "FIELD_1";
|
||||||
|
|
||||||
private static readonly JsonSerializerOptions JsonOptions = new(JsonSerializerDefaults.Web)
|
private static readonly JsonSerializerOptions JsonOptions = new(JsonSerializerDefaults.Web)
|
||||||
{
|
{
|
||||||
@@ -35,7 +36,7 @@ public static class GenericOperationRequestReader
|
|||||||
OpeMediaTypes.GenericOperationRequest,
|
OpeMediaTypes.GenericOperationRequest,
|
||||||
StringComparison.OrdinalIgnoreCase))
|
StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
errors[ExpectedField] = OpeFieldErrors.UnexpectedFormat;
|
errors[ExpedienteField] = OpeFieldErrors.UnexpectedFormat;
|
||||||
return new GenericOperationRequestResult(null, errors);
|
return new GenericOperationRequestResult(null, errors);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -49,19 +50,21 @@ public static class GenericOperationRequestReader
|
|||||||
}
|
}
|
||||||
catch (JsonException)
|
catch (JsonException)
|
||||||
{
|
{
|
||||||
errors[ExpectedField] = OpeFieldErrors.UnexpectedFormat;
|
errors[ExpedienteField] = OpeFieldErrors.UnexpectedFormat;
|
||||||
return new GenericOperationRequestResult(null, errors);
|
return new GenericOperationRequestResult(null, errors);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (envelope?.Data is null)
|
if (envelope?.Data is null)
|
||||||
{
|
{
|
||||||
errors[ExpectedField] = OpeFieldErrors.Expected;
|
errors[ExpedienteField] = OpeFieldErrors.Expected;
|
||||||
|
errors[BlockField] = OpeFieldErrors.Expected;
|
||||||
return new GenericOperationRequestResult(null, errors);
|
return new GenericOperationRequestResult(null, errors);
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var field in envelope.Data)
|
foreach (var field in envelope.Data)
|
||||||
{
|
{
|
||||||
if (!string.Equals(field.Key, ExpectedField, StringComparison.Ordinal))
|
if (!string.Equals(field.Key, ExpedienteField, StringComparison.Ordinal) &&
|
||||||
|
!string.Equals(field.Key, BlockField, StringComparison.Ordinal))
|
||||||
{
|
{
|
||||||
errors[field.Key] = OpeFieldErrors.NotExpected;
|
errors[field.Key] = OpeFieldErrors.NotExpected;
|
||||||
continue;
|
continue;
|
||||||
@@ -70,22 +73,26 @@ public static class GenericOperationRequestReader
|
|||||||
if (field.Value is null ||
|
if (field.Value is null ||
|
||||||
!string.Equals(field.Value.Type, "STRING", StringComparison.Ordinal) ||
|
!string.Equals(field.Value.Type, "STRING", StringComparison.Ordinal) ||
|
||||||
string.IsNullOrWhiteSpace(field.Value.Value))
|
string.IsNullOrWhiteSpace(field.Value.Value))
|
||||||
|
{
|
||||||
|
errors[field.Key] = OpeFieldErrors.UnexpectedFormat;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.Equals(field.Key, BlockField, StringComparison.Ordinal) &&
|
||||||
|
field.Value.Value.Trim() is not ("1" or "2"))
|
||||||
{
|
{
|
||||||
errors[field.Key] = OpeFieldErrors.UnexpectedFormat;
|
errors[field.Key] = OpeFieldErrors.UnexpectedFormat;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!envelope.Data.ContainsKey(ExpectedField))
|
if (!envelope.Data.ContainsKey(ExpedienteField))
|
||||||
{
|
{
|
||||||
errors[ExpectedField] = OpeFieldErrors.Expected;
|
errors[ExpedienteField] = OpeFieldErrors.Expected;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (envelope.Data.Count > 10)
|
if (!envelope.Data.ContainsKey(BlockField))
|
||||||
{
|
{
|
||||||
foreach (var field in envelope.Data.Keys.Where(key => key != ExpectedField))
|
errors[BlockField] = OpeFieldErrors.Expected;
|
||||||
{
|
|
||||||
errors[field] = OpeFieldErrors.NotExpected;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return new GenericOperationRequestResult(envelope, errors);
|
return new GenericOperationRequestResult(envelope, errors);
|
||||||
|
|||||||
@@ -7,7 +7,8 @@ namespace ApiOPE.Services;
|
|||||||
public enum InternalLookupStatus
|
public enum InternalLookupStatus
|
||||||
{
|
{
|
||||||
Success,
|
Success,
|
||||||
NotFound
|
NotFound,
|
||||||
|
DataUnavailable
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed record InternalLookupResult(
|
public sealed record InternalLookupResult(
|
||||||
@@ -57,11 +58,16 @@ public sealed class InternalDenunciasClient
|
|||||||
HttpCompletionOption.ResponseHeadersRead,
|
HttpCompletionOption.ResponseHeadersRead,
|
||||||
cancellationToken);
|
cancellationToken);
|
||||||
|
|
||||||
if (response.StatusCode is HttpStatusCode.NotFound or HttpStatusCode.Gone)
|
if (response.StatusCode is HttpStatusCode.NotFound)
|
||||||
{
|
{
|
||||||
return new InternalLookupResult(InternalLookupStatus.NotFound, null);
|
return new InternalLookupResult(InternalLookupStatus.NotFound, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (response.StatusCode is HttpStatusCode.Gone)
|
||||||
|
{
|
||||||
|
return new InternalLookupResult(InternalLookupStatus.DataUnavailable, null);
|
||||||
|
}
|
||||||
|
|
||||||
if (!response.IsSuccessStatusCode)
|
if (!response.IsSuccessStatusCode)
|
||||||
{
|
{
|
||||||
_logger.LogWarning(
|
_logger.LogWarning(
|
||||||
|
|||||||
@@ -1,49 +1,71 @@
|
|||||||
using ApiOPE.Configuration;
|
|
||||||
using ApiOPE.Contracts;
|
using ApiOPE.Contracts;
|
||||||
using Microsoft.Extensions.Options;
|
|
||||||
|
|
||||||
namespace ApiOPE.Services;
|
namespace ApiOPE.Services;
|
||||||
|
|
||||||
public sealed class OpeFieldMapper
|
public sealed class OpeFieldMapper
|
||||||
{
|
{
|
||||||
public static readonly IReadOnlySet<string> SupportedInternalFields = new HashSet<string>(
|
public OpeDataEnvelope MapBlock(
|
||||||
[
|
InternalGestionaFieldsResponse? source,
|
||||||
"fechaDenuncia",
|
int block,
|
||||||
"numeroDenunciaCanal",
|
bool belongsToComplaintsChannel)
|
||||||
"aQuienDenuncia",
|
|
||||||
"resumenDenuncia",
|
|
||||||
"fechaHechos",
|
|
||||||
"lugarHechos",
|
|
||||||
"ambitoCompetencias",
|
|
||||||
"solicitaProteccion",
|
|
||||||
"sexoDenunciante",
|
|
||||||
"autorizaRemisionDenuncia",
|
|
||||||
"autorizaNotificacionesViaSms",
|
|
||||||
"preferenciaNotificacionSeguimientoDenuncia"
|
|
||||||
],
|
|
||||||
StringComparer.Ordinal);
|
|
||||||
|
|
||||||
private readonly string[] _outputFields;
|
|
||||||
|
|
||||||
public OpeFieldMapper(IOptions<OpeOptions> options)
|
|
||||||
{
|
{
|
||||||
_outputFields = options.Value.OutputFields.ToArray();
|
if (!belongsToComplaintsChannel || source is null)
|
||||||
|
{
|
||||||
|
return EmptyBlock(block, belongsToComplaintsChannel);
|
||||||
}
|
}
|
||||||
|
|
||||||
public OpeDataEnvelope Map(InternalGestionaFieldsResponse source)
|
return block switch
|
||||||
|
{
|
||||||
|
1 => Envelope(
|
||||||
|
"SI",
|
||||||
|
Value(source, "fechaDenuncia"),
|
||||||
|
Value(source, "numeroDenunciaCanal"),
|
||||||
|
Value(source, "sexoDenunciante"),
|
||||||
|
Value(source, "preferenciaNotificacion"),
|
||||||
|
Value(source, "autorizaNotificacionesElectronicas"),
|
||||||
|
Value(source, "autorizaNotificacionesViaSms"),
|
||||||
|
Value(source, "preferenciaNotificacionPostal"),
|
||||||
|
Value(source, "preferenciaSeguimientoOnline"),
|
||||||
|
Value(source, "ambitoCompetencias")),
|
||||||
|
2 => Envelope(
|
||||||
|
Value(source, "aQuienDenuncia"),
|
||||||
|
Value(source, "detalleDenunciado"),
|
||||||
|
Value(source, "resumenDenuncia"),
|
||||||
|
Value(source, "solicitaProteccion"),
|
||||||
|
Value(source, "lugarHechos"),
|
||||||
|
Value(source, "fechaHechos"),
|
||||||
|
Value(source, "autorizaRemisionDenuncia"),
|
||||||
|
Value(source, "comentariosComunicaciones"),
|
||||||
|
string.Empty,
|
||||||
|
string.Empty),
|
||||||
|
_ => throw new ArgumentOutOfRangeException(nameof(block))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static OpeDataEnvelope EmptyBlock(int block, bool belongsToComplaintsChannel)
|
||||||
|
{
|
||||||
|
var values = Enumerable.Repeat(string.Empty, 10).ToArray();
|
||||||
|
if (block == 1)
|
||||||
|
{
|
||||||
|
values[0] = belongsToComplaintsChannel ? "SI" : "NO";
|
||||||
|
}
|
||||||
|
|
||||||
|
return Envelope(values);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string Value(InternalGestionaFieldsResponse source, string name)
|
||||||
|
=> source.Data.TryGetValue(name, out var value)
|
||||||
|
? value.Value?.Trim() ?? string.Empty
|
||||||
|
: string.Empty;
|
||||||
|
|
||||||
|
private static OpeDataEnvelope Envelope(params string[] values)
|
||||||
{
|
{
|
||||||
var output = new Dictionary<string, OpeFieldValue>(StringComparer.Ordinal);
|
var output = new Dictionary<string, OpeFieldValue>(StringComparer.Ordinal);
|
||||||
|
for (var index = 0; index < 10; index++)
|
||||||
for (var index = 0; index < _outputFields.Length; index++)
|
|
||||||
{
|
{
|
||||||
var internalField = _outputFields[index];
|
output[$"FIELD_{index}"] = new OpeFieldValue(
|
||||||
if (!source.Data.TryGetValue(internalField, out var value))
|
"STRING",
|
||||||
{
|
index < values.Length ? values[index] : string.Empty);
|
||||||
throw new InvalidDataException(
|
|
||||||
$"La API interna no ha devuelto el campo configurado '{internalField}'.");
|
|
||||||
}
|
|
||||||
|
|
||||||
output[$"FIELD_{index}"] = new OpeFieldValue("STRING", value.Value?.Trim() ?? string.Empty);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return new OpeDataEnvelope(output);
|
return new OpeDataEnvelope(output);
|
||||||
|
|||||||
@@ -14,19 +14,7 @@
|
|||||||
"ClockSkewSeconds": 60,
|
"ClockSkewSeconds": 60,
|
||||||
"OrganizationId": "00000000-0000-0000-0000-000000000001",
|
"OrganizationId": "00000000-0000-0000-0000-000000000001",
|
||||||
"OrganizationDir3": "DEV000001",
|
"OrganizationDir3": "DEV000001",
|
||||||
"OrganizationCif": "DEV000001",
|
"OrganizationCif": "DEV000001"
|
||||||
"OutputFields": [
|
|
||||||
"fechaDenuncia",
|
|
||||||
"numeroDenunciaCanal",
|
|
||||||
"aQuienDenuncia",
|
|
||||||
"resumenDenuncia",
|
|
||||||
"fechaHechos",
|
|
||||||
"lugarHechos",
|
|
||||||
"ambitoCompetencias",
|
|
||||||
"solicitaProteccion",
|
|
||||||
"sexoDenunciante",
|
|
||||||
"autorizaRemisionDenuncia"
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"InternalApi": {
|
"InternalApi": {
|
||||||
"BaseUrl": "http://localhost:7093",
|
"BaseUrl": "http://localhost:7093",
|
||||||
|
|||||||
@@ -17,8 +17,7 @@
|
|||||||
"ClockSkewSeconds": 60,
|
"ClockSkewSeconds": 60,
|
||||||
"OrganizationId": "",
|
"OrganizationId": "",
|
||||||
"OrganizationDir3": "",
|
"OrganizationDir3": "",
|
||||||
"OrganizationCif": "",
|
"OrganizationCif": ""
|
||||||
"OutputFields": []
|
|
||||||
},
|
},
|
||||||
"InternalApi": {
|
"InternalApi": {
|
||||||
"BaseUrl": "http://localhost:7093",
|
"BaseUrl": "http://localhost:7093",
|
||||||
|
|||||||
@@ -145,7 +145,13 @@ public sealed record GestionaComplaintFieldsResponse(
|
|||||||
string SexoDenunciante,
|
string SexoDenunciante,
|
||||||
string AutorizaRemisionDenuncia,
|
string AutorizaRemisionDenuncia,
|
||||||
string AutorizaNotificacionesViaSms,
|
string AutorizaNotificacionesViaSms,
|
||||||
string PreferenciaNotificacionSeguimientoDenuncia);
|
string PreferenciaNotificacionSeguimientoDenuncia,
|
||||||
|
string DetalleDenunciado,
|
||||||
|
string PreferenciaNotificacion,
|
||||||
|
string AutorizaNotificacionesElectronicas,
|
||||||
|
string PreferenciaNotificacionPostal,
|
||||||
|
string PreferenciaSeguimientoOnline,
|
||||||
|
string ComentariosComunicaciones);
|
||||||
|
|
||||||
public sealed record GestionaExternalFieldValue(
|
public sealed record GestionaExternalFieldValue(
|
||||||
string Type,
|
string Type,
|
||||||
|
|||||||
@@ -914,7 +914,7 @@ else
|
|||||||
var createdFile = await ApiDenuncias.CreateGestionaFileAsync(
|
var createdFile = await ApiDenuncias.CreateGestionaFileAsync(
|
||||||
nuevoAsunto,
|
nuevoAsunto,
|
||||||
"RQ2ZLC - Expediente de Denuncias",
|
"RQ2ZLC - Expediente de Denuncias",
|
||||||
"3109963"
|
string.Empty
|
||||||
);
|
);
|
||||||
fileUrl = createdFile.FileUrl;
|
fileUrl = createdFile.FileUrl;
|
||||||
Busy.Update(message: "Abriendo expediente y asignando el grupo elegido.", detail: "Paso 2 de 8");
|
Busy.Update(message: "Abriendo expediente y asignando el grupo elegido.", detail: "Paso 2 de 8");
|
||||||
|
|||||||
@@ -1005,7 +1005,7 @@ else
|
|||||||
var createdFile = await ApiDenuncias.CreateGestionaFileAsync(
|
var createdFile = await ApiDenuncias.CreateGestionaFileAsync(
|
||||||
nuevoAsunto,
|
nuevoAsunto,
|
||||||
"RQ2ZLC - Expediente de Denuncias",
|
"RQ2ZLC - Expediente de Denuncias",
|
||||||
"3109963"
|
string.Empty
|
||||||
);
|
);
|
||||||
fileUrl = createdFile.FileUrl;
|
fileUrl = createdFile.FileUrl;
|
||||||
Busy.Update(message: "Abriendo expediente y asignando el grupo elegido.", detail: "Paso 2 de 7");
|
Busy.Update(message: "Abriendo expediente y asignando el grupo elegido.", detail: "Paso 2 de 7");
|
||||||
|
|||||||
Reference in New Issue
Block a user