Simuladores IPF
La plataforma IPF se sitúa entre los sistemas bancarios y los CSM para proporcionar instant servicios de pago.
Como ayuda para las pruebas manuales y exploratorias, así como para fines de aprendizaje y demostración, están disponibles simuladores tanto para sistemas bancarios como para CSMs.
Descripción general
Banco y CSM los simuladores proporcionan puntos finales de reemplazo para esos sistemas. En la figura anterior, podemos ver cómo un simulador bancario proporciona tanto iniciadores como respondedores.
Los iniciadores típicos pueden incluir los siguientes puntos finales:
-
payment initiation(el Canal)
-
cancelación de pago, devolución de pago, resolución de investigación
Los iniciadores de pago también ofrecen funciones básicas real-time request counters, y apoyar adicional implementation-specific status counters.
Además, el simulador ofrece automático request load generation. Esto puede producirse de manera continua o puede limitarse en el tiempo y/o en la cantidad total de solicitudes.
Los respondedores imitarán el comportamiento esperado de los sistemas bancarios y dependerán estrictamente de la implementación del banco.
CSM Los simuladores también proporcionan iniciadores para pagos entrantes, así como respondedores para pagos salientes y otros mensajes: cancelaciones, devoluciones, investigaciones, etc.
Los respondedores también son conocidos como manejadores de solicitudes y su response latency can be configured. Cada respondedor es identificado por un handler Id único para este propósito, y el list of available responderstambién se puede obtener.
Simulador Genérico IPF NG API
Descripción general
Todos los simuladores IPF implementan el Simulador Genérico IPF. API para apoyar un conjunto común de funcionalidades. Este documento define los elementos comunes de la simulación API. Además, separe más especializado APIs se definirá para casos de uso específicos.
Recursos
Solicitud
Genere y envíe solicitudes individuales/cargadas
Envía un mensaje en bruto que simplemente necesita ser reenviado
PUT /sendRawRequest
Descripción
Normalmente esto representa XML mensaje que queremos transmitir. En este caso, el Simulador IPF no genera ni enriquece la solicitud porque la solicitud en sí es un mensaje completamente contenido.
Parámetros
| Tipo | Nombre | Descripción | Esquema |
|---|---|---|---|
Cuerpo |
fullRequest |
Describe la solicitud que debe enviarse al sistema en prueba. |
cadena |
Encabezado |
Metadatos |
Metadatos adicionales que podrían utilizarse para proporcionar más detalles sobre la solicitud. |
cadena |
Respuestas
| Código HTTP | Descripción | Esquema |
|---|---|---|
200 |
Se envió con éxito una única solicitud al sistema en prueba. |
|
400 |
Normalmente el resultado de una solicitud malformada. |
No Content |
Envía un único mensaje de solicitud al sistema en prueba
PUT /sendRequest
Descripción
Envía una única carga de mensaje de solicitud al sistema en prueba, tal como se describe en los Detalles de Solicitud.
Parámetros
| Tipo | Nombre | Descripción | Esquema |
|---|---|---|---|
Cuerpo |
detallesDeSolicitud |
Describe la solicitud que debe enviarse al sistema en prueba. Esto se convierte en un formato especializado. |
Respuestas
| Código HTTP | Descripción | Esquema |
|---|---|---|
200 |
Se envió con éxito una única solicitud al sistema en prueba. |
|
400 |
Normalmente el resultado de un RequestDetails mal formado json carga útil. |
No Content |
Ejemplo HTTP solicitud
{
"transactionId": "fb495cf0d88a11ea87d00242ac130003",
"amount": {
"amount": 100,
"currency": "EUR"
},
"originatorName": "John Maynard Keynes",
"originatorAccount": {
"iban": "GB29NWBK60161331926819",
"bic": "DEUTDEFF",
"accountId": "7928629",
"ukBankAccount": "object",
"proxy": "object"
},
"beneficiaryName": "Benedetto Cotrugli",
"beneficiaryAccount": {
"iban": "GB29NWBK60161331926819",
"bic": "DEUTDEFF",
"accountId": "7928629",
"ukBankAccount": "object",
"proxy": "object"
},
"remittanceInfo": "Remittance information",
"additionalInfo": {
"Cdtr. PstlAdr. PstCd": "12820",
"CdtTrfTxInf[0]. Cdtr. Nm": "Benedetto Cotrugli"
}
}
Envía un mensaje de Solicitud de Retiro
PUT /bankRecall
Descripción
Permite que se envíe una única carga útil de mensaje de solicitud de recuperación al sistema.
Parámetros
| Tipo | Nombre | Descripción | Esquema |
|---|---|---|---|
Cuerpo |
solicitudDeRetiro |
Detalles de la solicitud de retiro |
Envía un mensaje de Solicitud de Devolución
PUT /bankReturn
Descripción
Permite que se envíe una única carga útil de mensaje de solicitud de devolución al sistema.
Parámetros
| Tipo | Nombre | Descripción | Esquema |
|---|---|---|---|
Cuerpo |
solicitud De Devolución |
Detalles de la Solicitud de Devolución |
Envía un mensaje de Solicitud de Resolución de Investigación
PUT /bankRoi
Parámetros
| Tipo | Nombre | Descripción | Esquema |
|---|---|---|---|
Cuerpo |
roiRequest |
Detalles de la solicitud de ROI |
Recupera la carga de solicitudes en curso simulada en el sistema
GET /requestLoad
Establece la carga de solicitud en curso simulada en el sistema bajo prueba
POST /requestLoad
Descripción
Genera transacciones financieras a la tasa dada, de manera continua o en un lote limitado. Se puede utilizar el tamaño y/o la duración para el modo por lotes, omítase para el modo continuo. Establezca la tasa en cero para detenerse.
Parámetros
| Tipo | Nombre | Descripción | Esquema |
|---|---|---|---|
Cuerpo |
cuerpo |
Los ajustes de carga de solicitud deseados |
Respuestas
| Código HTTP | Descripción | Esquema |
|---|---|---|
200 |
Las solicitudes actualizadas por segundo |
|
400 |
Error general. |
No Content |
Configuración
Configure el simulador
Lea todas las configuraciones de los respondedores
GET /responseConfiguration
Descripción
Proporciona configuraciones de respuesta para todos los respondedores. Es útil tener la lista completa de controladores disponibles.
Vea cómo un respondedor específico responde a las solicitudes
GET /responseConfiguration/{handlerId}
Descripción
Proporciona detalles sobre la tasa de rechazo / aciertos, la tasa de mensajes a los que no debe responder y que, por lo tanto, causan un tiempo de espera, así como la latencia que debe aplicarse antes de responder.
Parámetros
| Tipo | Nombre | Descripción | Esquema |
|---|---|---|---|
Ruta |
handlerId |
El identificador del controlador de solicitudes para obtener la configuración. Esto es definido por la implementación. |
cadena |
Cambie cómo un respondedor responde a las solicitudes
PUT /responseConfiguration/{handlerId}
Descripción
Establece los detalles de la tasa de rechazo / aciertos, la tasa de mensajes a los que no debe responder y, por lo tanto, causa un tiempo de espera, así como la latencia que debe aplicarse antes de responder.
Parámetros
| Tipo | Nombre | Descripción | Esquema |
|---|---|---|---|
Ruta |
handlerId |
El identificador del controlador de solicitudes a configurar. Esto es definido por la implementación. |
cadena |
Cuerpo |
nueva Configuración |
La nueva configuración a utilizar incluye el ID del controlador. |
Respuestas
| Código HTTP | Descripción | Esquema |
|---|---|---|
200 |
La configuración de respuesta del simulador fue recibida con éxito. |
No Content |
400 |
Normalmente el resultado de una ResponseConfiguration mal formada.json carga útil. |
No Content |
404 |
No se pudo encontrar el controlador de solicitudes con el handlerId proporcionado. |
No Content |
Estadísticas
Consulte el estado del simulador.
Resumen De Transacción Enviada
Consulte las transacciones para el simulador
OBTENER /transacciones
Respuestas
| Código HTTP | Descripción | Esquema |
|---|---|---|
200 |
Proporciona información detallada sobre transacciones. |
Ejemplo HTTP respuesta
{
"totalSent": 1000,
"averageTransactionTimeMs": 64,
"startEventTime": "2020-10-23T16:30:50. 000+0000",
"lastEventTime": "2020-10-23T17:30:50. 000+0000",
"transactions": [ {
"id": "TestFlow|nQqyDbhqWrwJysoDVJrRVPTOfPhicTKgkDE",
"status": "Completed",
"startTime": "020-10-23T17:30:00Z",
"completedTime": "2020-10-23T17:30:05. 000+0000",
"timeTakeMs": 5000
}, {
"id": "TestFlow|nQqyDbhqWrwJysoDVJrRVPTOfPhicTKgkAD",
"startTime": "020-10-23T17:30:00Z",
"status": "Processing"
} ]
}
Definiciones
Cuenta
Una representación de una cuenta
| Nombre | Descripción | Esquema |
|---|---|---|
iban |
Un IBAN que identifica la cuenta |
cadena |
bic |
Un BIC que identifica la cuenta |
cadena |
accountId |
Un identificador genérico para la cuenta |
cadena |
cuentaBancariaUk |
Una representación de una cuenta bancaria del Reino Unido |
|
proxy |
Una representación de la identificación de cuentas proxy |
cuentaBancariaUk
| Nombre | Descripción | Esquema |
|---|---|---|
código de clasificación |
Identificador de sucursal |
cadena |
número De Cuenta |
Identificador de cuenta |
cadena |
proxy
| Nombre | Descripción | Esquema |
|---|---|---|
tipo |
Una indicación del tipo de este id de proxy |
cadena |
id |
Id de proxy |
cadena |
Código De Moneda
El código de moneda ISO 4217
Tipo: enum (GBP, USD, EUR, CHF, CAD, AUD, NZD, HKD, JPY, CNY)
CantidadFinanciera
Una representación de un monto financiero con la moneda involucrada
| Nombre | Descripción | Esquema |
|---|---|---|
cantidad |
La cantidad de dinero en la denominación más baja que no necesita fracciones, por ejemplo, peniques para GBP y centavos para USD. |
entero (int64) |
moneda |
Ejemplo:Código De Moneda |
MagicValue
Valor mágico para el simulador utilizado para producir la respuesta.
| Nombre | Descripción | Esquema |
|---|---|---|
descripción |
Describa el valor mágico y su impacto. |
cadena |
ruta |
La ruta en la solicitud para obtener el valor. |
cadena |
valor |
El valor en la solicitud que se considerará valor mágico. |
cadena |
RecallRequestInitiator
Carga útil de solicitud de recall
| Nombre | Descripción | Esquema |
|---|---|---|
transactionId |
Un identificador único para la transacción financiera generada |
cadena |
cancellationId |
Un valor generado aleatoriamente utilizado para fines de prueba y para hacer coincidir los identificadores alternativos. |
cadena |
cdtrNm |
Nombre del acreedor |
cadena |
DetallesDeSolicitud
Una descripción de una solicitud para enviar al sistema en prueba.
| Nombre | Descripción | Esquema |
|---|---|---|
transactionId |
Un identificador único para la transacción financiera generada |
cadena |
cantidad |
Cantidad a transferir |
|
originatorName |
Nombre de la persona que envía fondos |
cadena |
cuentaOriginadora |
Cuenta del deudor |
|
nombreDelBeneficiario |
Nombre de la persona que recibe los fondos |
cadena |
cuentaBeneficiario |
Cuenta del acreedor |
|
información De Remesas |
Información de remesas |
cadena |
información Adicional |
Reservado para características extendidas de simuladores individuales |
objeto |
RequestLoadConfiguration
Una solicitud para generar transacciones financieras a la tasa dada, de manera continua o en un lote limitado. El tamaño y/o la duración pueden utilizarse para el modo por lotes, omítase para el modo continuo. Establezca la tasa en cero para detener.
| Nombre | Descripción | Esquema |
|---|---|---|
solicitudesPorSegundo |
El número de mensajes de solicitud que se deben enviar cada segundo. Utilice 0 para desactivar el tráfico de carga. |
entero |
tamaño |
El número máximo de solicitudes a producir. Ilimitado si no se establece, debe ser positivo en caso contrario. |
entero (int64) |
duración |
El tiempo máximo para producir solicitudes es. Formato de duración ISO-8601 PnDTnHnMn.nS, considerando que los días son exactamente 24 horas. Ilimitado si no se establece, debe ser positivo en caso contrario. |
cadena (iso8601) |
RequestLoadResponse
Una solicitud para generar transacciones financieras a la tasa dada, de manera continua o en un lote limitado. El tamaño y/o la duración pueden utilizarse para el modo por lotes, omítase para el modo continuo. Establezca la tasa en cero para detener.
| Nombre | Descripción | Esquema |
|---|---|---|
solicitudesPorSegundo |
La tasa actual de mensajes de solicitud a enviar por segundo. Cero significa que no se está generando tráfico. |
entero |
requestsLeft |
El número de solicitudes a producir antes de detenerse. No se establece para solicitudes ilimitadas. |
entero (int64) |
horaCorte |
El instant cuando el simulador dejará de producir solicitudes. No está configurado para un tiempo ilimitado. |
cadena (fecha-hora) |
ResultadoDeLaSolicitud
Si esta solicitud fue aceptada o rechazada
| Nombre | Descripción | Esquema |
|---|---|---|
resultado |
ACSP para aceptado, RJCT para rechazado |
enum (ACSP, RJCT) |
información Adicional |
información adicional específica de la implementación |
objeto |
Resolución Del Solicitante De Investigación
Resolución de la carga útil de la solicitud de investigación
| Nombre | Descripción | Esquema |
|---|---|---|
transactionId |
Un identificador único para la transacción financiera generada |
cadena |
orgtrNm |
Nombre de la organización |
cadena |
rsnCd |
código de razón |
cadena |
rsnPrtry |
razón prtry |
cadena |
addtlInf |
Ejemplo:`[ "string" ]` |
< string > array |
Configuración De Respuesta
Configuración de cómo el simulador maneja las respuestas.
| Nombre | Descripción | Esquema |
|---|---|---|
handlerId |
El identificador del controlador |
cadena |
latencia |
Cuánto tiempo esperar antes de responder. Definido en milisegundos. |
entero |
ConfiguracionesDeRespuesta
Una lista de todas las configuraciones de respuesta en el simulador.
Type: <ResponseConfiguration> matriz
ReturnRequestInitiator
Carga útil de solicitud de devolución
| Nombre | Descripción | Esquema |
|---|---|---|
transactionId |
Un identificador único para la transacción financiera generada |
cadena |
cdtrNm |
Nombre del acreedor |
cadena |
cantidad |
Monto original de liquidación bancaria |
entero (int64) |
Detalles De Transacción Enviada
Detalles de todas las transacciones enviadas
| Nombre | Descripción | Esquema |
|---|---|---|
id |
Ejemplo:`"TestFlow|nQqyDbhqWrwJysoDVJrRVPTOfPhicTKgkAD"` |
cadena |
estado |
Ejemplo:`"Accepted"` |
cadena |
horaDeInicio |
El instant cuando el simulador emitió la solicitud. |
cadena (fecha-hora) |
tiempoCompletado |
El instant cuando el simulador recibió la respuesta completada. |
cadena (fecha-hora) |
timeTakeMs |
tiempo requerido para que las transacciones se completen (completedTime-startTime). |
entero |
Resumen De Transacción Enviada
Detalles sobre transacciones
| Nombre | Descripción | Esquema |
|---|---|---|
totalSent |
Número total de transacciones recibidas a través de pagos salientes. |
entero |
tiempo Promedio Transacción Ms |
Tiempo promedio requerido para completar las transacciones. |
entero |
horaInicioEvento |
El instant cuando el simulador envió su primera solicitud. |
cadena (fecha-hora) |
hora DelÚltimo Evento |
El instant cuando el simulador recibió su última respuesta. |
cadena (fecha-hora) |
transacciones |
Contadores específicos de implementación, incrementados al inspeccionar el mensaje y derivar un estado. |
<SentTransactionDetails> matriz |
Estadísticas
Información estadística sobre el procesamiento de transacciones y sus resultados.
| Nombre | Descripción | Esquema |
|---|---|---|
pendiente |
Número total de transacciones iniciadas pero sin un resultado determinado. |
entero |
totalSent |
Número total de transacciones recibidas a través de pagos salientes. |
entero |
exitoso |
Número total de transacciones recibidas a través de pagos salientes, excluyendo las que failed por razones no relacionadas con el negocio. |
entero |
totalRecibido |
Número total de transacciones recibidas a través de pagos entrantes. |
entero |
tiempoAgotado |
Número total de transacciones para las cuales no se recibió una respuesta oportuna. |
entero |
failed |
Número total de transacciones que failed. |
entero |
latenciaReciente |
Latencia promedio reciente observada para que las respuestas lleguen a las solicitudes en ms. |
entero |
contadoresDeEstado |
Contadores específicos de implementación, incrementados al inspeccionar el mensaje y derivar un estado. |
<StatusCounter> matriz |
Explorando el Simulador API
El simulador api puede ser explorado y triggered usando swagger-ui.
Se sirve mediante el simulador bajo la ruta /apidocs.
Puede activar el simulador utilizando el swagger ui Try it out botones.
Implementación de un Simulador
Como se indicó anteriormente, podemos implementar tres tipos de simuladores: . Simuladores bancarios. CSM simuladores. Simuladores de respuesta (es decir, controladores de solicitudes)
El marco del simulador viene como un Spring Boot starter, con una API módulo.
Para implementar un simulador, usted debe:
-
Configurar project dependencies
-
Crear un Spring Boot Simulator application
-
Defina how to handle raw requests
-
Tenga la opción de documentar el simulator magic values
Dependencias del proyecto
Utilice lo siguiente maven configuración:
<dependencies>
<dependency>
<groupId>com.iconsolutions.test</groupId>
<artifactId>ipf-simulator-ng-api</artifactId>
<version>${simulator.version}</version>
</dependency>
<dependency>
<groupId>com.iconsolutions.test</groupId>
<artifactId>ipf-simulator-ng-core</artifactId>
<version>${simulator.version}</version>
</dependency>
</dependencies>
Aplicación del Simulador
Una aplicación de simulador es simplemente una Spring Boot aplicación que auto-configurará el marco del simulador gracias a la dependencia declared before.
@SpringBootApplication
class ExampleSimulatorApplication {
public static void main(final String[] args) {
final var simulator = new SpringApplication(ExampleSimulatorApplication.class);
simulator.setWebApplicationType(NONE);
simulator.run(args);
}
}
Message definition s
El iniciador de la solicitud de pago generará un mensaje de solicitud,enrich lo con el payload of the initiation commandy finalmente envíelo al sistema de destino utilizando el apropiado transport. Para que esto ocurra, el simulador necesita saber cómo generar un template, cómo aplicar el comando de inicio a este y dónde enviarlo.
Tales características se definen utilizando el MessageDefinition característica del Test Framework de Icon.
@SuppressWarnings("unused")
enum TestBusinessDomainMessageType implements MessageType {
MY_REQUEST,
PAIN001_RAW_REQUEST,
PACS008_RAW_REQUEST;
public String getName() {
return name();
}
public Set<String> getAliases() {
return Set.of();
}
}
@Bean
MessageDefinition<MyRequest> requestMessageDefinition(final WireMockServer wireMockServer) {
return new DefaultMessageDefinition.Builder<MyRequest>()
.withType(MY_REQUEST) (1)
.withGenerator(myRequestGenerator()) (2)
.withDocumentTypeClass(MyRequest.class) (3)
.withDestination(wireMockServer.url("/endpoint")) (4)
.withToStringMapper(this::messageAsJson)
.build();
}
private String messageAsJson(Object document) {
try {
return objectMapper.writeValueAsString(document);
} catch (JsonProcessingException e) {
throw new IconRuntimeException("Could not serialize " + document, e);
}
}
@Bean
RequestInitiationGenerator<MyRequest> myRequestGenerator() {
return properties -> new MyRequest();
}
| 1 | Tipo simbólico |
| 2 | Genere una solicitud, tal vez complete con valores aleatorios |
| 3 | Java tipo de la solicitud |
| 4 | Representación específica del transporte del destino |
| Algunos transportes pueden no requerir que se especifique un destino en el message definition |
Para aplicar el comando de iniciación de pago a la solicitud generada, también necesitamos definir un enricher:
@Bean
RequestInitiationEnricher<MyRequest> myRequestEnricher() {
return (myRequest, parameters) -> {
Optional.ofNullable(parameters.getTransactionId())
.ifPresent(myRequest::setTxnId);
Optional.ofNullable(parameters.getAmount())
.map(RequestAmount::getAmount)
.ifPresent(myRequest::setAmount);
return myRequest;
};
}
La solicitud enriquecida será enviada ahora al destino configurado arriba, con el transporte correspondiente.MY_REQUEST como un simbólico message type.
@Bean
SpelRequestInitiationEnricher<MyRequest, MyRequest> mySpelRequestEnricher() {
var CUSTOMISED_TRANSACTION_ID = "myCustomisedTransactionId";
var legacyFields = List.of(CUSTOMISED_TRANSACTION_ID);
var spelParserConfiguration = new SpelParserConfiguration(
true,
true,
10
);
return new SpelRequestInitiationEnricher<>(
legacyFields, (1)
spelParserConfiguration
) {
@Override
protected void enrichFromInitiateRequestParameters(MyRequest myRequest,
InitiateRequestParameters parameters
) { (2)
Optional.ofNullable(parameters.getTransactionId())
.ifPresent(myRequest::setTxnId);
Optional.ofNullable(parameters.getAmount())
.map(RequestAmount::getAmount)
.ifPresent(myRequest::setAmount);
}
@Override
protected MyRequest getObjectToEnrichFromAdditionalInfo(MyRequest request) {
(3)
return request;
}
@Override
protected void enrichUsingLegacyFields(MyRequest request,
Map.Entry<String, Object> entry
) { (4)
if (CUSTOMISED_TRANSACTION_ID.equals(entry.getKey())) {
request.setTxnId(entry.getValue().toString());
}
}
};
}
El SpelRequestInitiationEnricher proporciona un método enrichFromAdditionalInfo para enriquecer la solicitud de additionalInfo field. La clase hija puede anular el método obsoleto enrichUsingLegacyFields para enriquecer los campos heredados en customized De esta manera, esto debe evitarse ya que no será compatible en un futuro cercano.
| 1 | (Obsoleto) Opcional: especifique los campos heredados de additionalInfo para enriquecer la solicitud. |
| 2 | Especifique cómo enriquecer la solicitud a partir de initiateRequestParameters |
| 3 | Especifique el objeto objetivo para enriquecer a partir de additionalInfo |
| 4 | (Obsoleto) Opcional: sobrescriba este método para especificar cómo utilizar los campos heredados de additionalInfo para enriquecer la solicitud. |
Solicitud de Envoltura en Crudo
El envoltorio de solicitud en bruto se utiliza para casos en los que deseamos que el simulador simplemente transmita el mensaje al destino.
en lugar de generar y enriquecer el mensaje. Para enviar el mensaje en bruto /sendRawRequest API se utiliza donde el
raw request (mensaje completo) debe ser proporcionado. Se puede utilizar un encabezado Metadata opcional para los casos en los que esto API se utiliza para
soporte para enviar diferentes mensajes en bruto a diferentes destinos.
Al igual que para enviar una única solicitud con el enriquecedor, necesitamos definir un message definition y lo más importante raw request wrapper.
Para esto comenzaremos desde la implementación raw request wrapper implementando com.iconsolutions.simulator.core.interface. Raw Request Wrapper servicio.
@Bean
RawRequestWrapper<MyRawPain001Request> rawPain001RequestWrapper() {
return new RawRequestWrapper<MyRawPain001Request>() {
@Override
public MyRawPain001Request map(InitiateWithRawRequest initiateWithRawRequest) { (1)
return MyRawPain001Request.builder()
.rawXml(initiateWithRawRequest.getRawRequest())
.build();
}
@Override
public boolean supports(InitiateWithRawRequest initiateWithRawRequest) { (2)
return "this is pain001 message".equalsIgnoreCase(initiateWithRawRequest.getMetadata());
}
};
}
En ese ejemplo:
| 1 | Estamos transformando InitiateWithRawRequest que sostiene rawRequest y encabezado metadata en nuestro custom Java tipo.
Esto se debe simplemente a que nos gustaría crear un separado message definition para esta solicitud de datos en bruto en particular. |
| 2 | Verifica el encabezado de metadatos para determinar si este contenedor puede manejar este mensaje. Este metadato es importante en casos donde nos gustaría manejar diferentes solicitudes en bruto. |
Después de envolver la solicitud en bruto a nuestro custom Java tipo que necesitamos definir message definition para eso tipo.
Un ejemplo de definir message definition es:
@Bean
MessageDefinition<MyRawPain001Request> rawPain001RequestMessageDefinition(final WireMockServer wireMockServer) {
return new DefaultMessageDefinition.Builder<MyRawPain001Request>()
.withType(PAIN001_RAW_REQUEST) (1)
.withDocumentTypeClass(MyRawPain001Request.class) (2)
.withDestination(wireMockServer.url("/pain001")) (3)
.withToStringMapper(MyRawPain001Request::getRawXml) (4)
.build();
}
| 1 | Tipo simbólico |
| 2 | Nuestro custom Java escriba simplemente para que podamos definir diferentes destinos y/o manejar de manera diferente esta solicitud en bruto particular |
| 3 | Destino al que se debe enviar el mensaje. Esto también puede ser gestionado únicamente por los asociados.message transport. |
| 4 | Dado que esto representa una solicitud en bruto, podríamos utilizar una cadena.mapper así y utilícelo más tarde en message transport as is. |
Message transport
Para llevar el mensaje a su destino, necesitamos definir un MessageTransport.
Esto define una relación entre un simbólico message type y los medios para dirigirlo a su destino, denominado un Transporter.
En este ejemplo estamos configurando un Akka-basado HTTP transporter.
@Bean
MessageTransport channelMessageTransport() {
final ActorSystem actorSystem = ActorSystem.create("test-implementation");
/* This transporter is asynchronous and does not add messages to the message bucket,
* therefore it is not suitable for use with JBehave feature tests */
final var transporter = new AkkaHttpMessageTransporter<>(
Http.get(actorSystem),
actorSystem
);
return MessageTransportImpl.MessageTransportImplBuilder.aMessageTransportImpl()
.withSendingSupplier(transporter)
.withSupportedMessageTypes(Set.of(
MY_REQUEST,
PAIN001_RAW_REQUEST,
PACS008_RAW_REQUEST))
.build();
}
Al observar la implementación, podemos ver cómo estamos aprovechando el message definitions:
final class AkkaHttpMessageTransporter<T> implements TransporterWithAckSupport<HttpResponse> {
@Override
public CompletionStage<HttpResponse> sendMessageWithAck(MessageDefinition messageDefinition, Message message, String destination) {
HttpRequest request = httpRequestFor(messageDefinition, message, destination);
LOGGER.debug("Sending request {}", request);
return akkaHttp.singleRequest(request)
// Consume response entity
.thenCompose(httpResponse -> httpResponse
.toStrict(TOSTRICT_TIMEOUT_MILLIS, classicActorSystemProvider)
.thenApply(HttpResponse.class::cast));
}
private HttpRequest httpRequestFor(
MessageDefinition<T> messageDefinition, Message<T> message, String destination) {
return HttpRequest.create(uriFor(destination, messageDefinition))
.withMethod(httpMethodFor(messageDefinition))
.withHeaders(headersFor(message))
.withEntity(HttpEntities.create(
ContentTypes.APPLICATION_JSON,
messageDefinition.asString(message.getDocument())
));
}
private String uriFor(String destination, MessageDefinition<T> messageDefinition) {
return StringUtils.defaultIfBlank(destination, messageDefinition.getDestination()); (1)
}
//..
}
| 1 | Recupere la URL objetivo de la relevante message definition |
Manejador de solicitudes
Los controladores de solicitudes están ahí para responder a algunas solicitudes. Por ejemplo, cuando nuestro sistema envía una solicitud al endpoint de CUENTAS del banco, queremos simular lo que el banco envía de vuelta a nuestro sistema. Algunos simuladores solo pueden tener un manejador de solicitudes, ya que estos simuladores no generan ninguna solicitud.
Cada controlador de solicitudes debe implementar com.iconsolutions.simulator.api. Interfaz Request Handler. Un ejemplo de manejador de solicitudes ficticias:
final class ExampleSimulatorHandler implements RequestHandler {
ExampleSimulatorHandler(final WireMockServer wireMockServer) {
this.wireMockServer = wireMockServer;
this.latency = 0;
this.wireMockServer.stubFor(post(urlEqualTo("/endpoint"))
.withRequestBody(containing("this-is-a-magic-txnId-and-will-cause-a-rejection"))
.willReturn(aResponse()
.withStatus(418)
.withBody("FAILED")));
this.wireMockServer.stubFor(post(urlEqualTo("/endpoint"))
.withRequestBody(containing("this-is-a-magic-txnId-and-will-be-accepted"))
.willReturn(aResponse()
.withStatus(201)
.withBody("SUCCESS")));
}
}
Gestiona todas las solicitudes que llegan a la ruta "/endpoint" de WireMock. En lugar de WireMock, un manejador real probablemente escucharía en algún Kafka tema o JMS cola, etc.
A continuación, deberá registrarlo con Spring.
@SpringBootApplication
class ExampleSimulatorApplication {
@Bean
RequestHandler testSimulatorHandler(final WireMockServer wireMockServer) {
return new ExampleSimulatorHandler(wireMockServer);
}
}
Después de eso, este controlador de solicitudes ficticio simplemente devolverá una respuesta ficticia si usted invoca la ruta /endpoint con la carga útil esperada.
Configuración
Toda la configuración externalizada sigue el Spring Boot convenciones.
En resumen, SpringApplication carga propiedades de application.properties archivos en las siguientes ubicaciones y los añade al Entorno de Spring:
-
A
/configsubdirectorio del directorio actual -
El directorio actual
-
Un classpath
/configpaquete -
La raíz del classpath
La lista está ordenada por precedencia (las propiedades definidas en ubicaciones más altas en la lista anulan las definidas en ubicaciones más bajas).
| También puede utilizar YAML Archivos ('.yml') como una alternativa a '.properties'. |
Esta es una lista de las claves de configuración disponibles:
| Clave | Predeterminado | Descripción |
|---|---|---|
simulador.http.host |
0. 0. 0. 0 |
Interfaz para exponer el Simulador API on |
simulador.http.port |
55555 |
Puerto TCP para exponer el Simulador API on |
Magic values
El _Manejador de Solicitudes_API expone un método para recuperar una lista de Magic Values para la implementación específica del simulador.
Esta funcionalidad es opcional, y la implementación predeterminada está devolviendo una lista vacía.
La lista de simulador magic values es puramente para fines de documentación. No se supone que proporcionen ninguna funcionalidad más que informativa.
Al observar la implementación, podemos ver cómo definimos magic values:
@Override
public List<MagicValue> getMagicValues() {
return List.of(new MagicValue(
"Rejects requests with this txnId",
"txnId",
"this-is-a-magic-txnId-and-will-cause-a-rejection"));
}
Métricas
Las métricas deben habilitarse explícitamente, para ello hay varias cosas que debe hacer.
-
Agregue una dependencia en
ipf-simulator-ng-metrics -
Configure cinnamon prometheus en su
application.conf -
Incluya la canela java agente al ejecutar su simulador:`-javaagent:/path/to/cinnamon-agent.jar`
Dependencia de métricas
<dependency>
<groupId>com.iconsolutions.test</groupId>
<artifactId>ipf-simulator-ng-metrics</artifactId>
<version>${project.version}</version>
</dependency>
Configure Cinnamon Prometheus
Ver developer.lightbend.com/docs/telemetry/current//plugins/prometheus/prometheus.html para más información
cinnamon.prometheus {
exporters += http-server
use-default-registry = on
}
Las métricas se exponen en un prometheus formato amigable en localhost:9001, a menos que se configure de manera diferente en su application.conf.
cinnamon.prometheus {
..
port = 9999
}
Hay varias métricas proporcionadas de forma predeterminada, y puede agregar las suyas propias con bastante facilidad.
Métricas fuera de la caja
| Métrica | Descripción |
|---|---|
solicitudes_de_aplicación_enviadas |
Un recuento de las solicitudes enviadas al sistema objetivo (pueden ser exitosas, pendientes o failed) |
solicitudes_de_aplicación_exitosas |
Un conteo de solicitudes que recibieron una respuesta del sistema objetivo (con éxito o no) |
solicitudes_de_aplicación_fallidas |
Un recuento de solicitudes que failed a ser enviado al sistema de destino (Generalmente un fallo de transporte) |
solicitudes_de_aplicación_pendientes |
Un recuento de solicitudes que aún no han recibido una respuesta del sistema objetivo. |
duraciones_de_respuesta_de_la_aplicación |
Un resumen del tiempo que tarda el sistema objetivo en responder a las solicitudes. |
Custom Métricas
Las métricas son proporcionadas por una instancia de com.lightbend.cinnamon.akka. CinnamonMetrics.
Puede declarar un bean dependencia de este tipo y haga lo que desee.
Un ejemplo donde podemos querer contar todas las solicitudes.events(requests and responses in this case)
@Bean
RequestEventHandler customMetrics(final CinnamonMetrics cinnamonMetrics) {
final var customCounter = cinnamonMetrics.createCounter(new Descriptor.Builder()
.withName("this is a demonstration of a custom metric")
.withKey("my_custom_counter_metric")
.build());
return requestEvent -> customCounter.increment();
}
Todas las métricas estarán precedidas por application_, por lo que la salida de esto se vería como
# HELP application_my_custom_counter_metric this is a demonstration of a custom metric
# TYPE application_my_custom_counter_metric gauge
application_my_custom_counter_metric{application=".. ExampleSimulatorApplication", host="..",} 60. 0