Simuladores IPF
La plataforma IPF se sitúa entre los sistemas bancarios y los CSM para proporcionar servicios de pago instantáneo.
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 CSM.
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 estar limitado 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.
CSMLos 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 único handler Id 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. Separe aún 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, como se describe en el RequestDetails.
Parámetros
| Tipo | Nombre | Descripción | Esquema |
|---|---|---|---|
Cuerpo |
requestDetails |
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 malformado RequestDetails carga útil json. |
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 Recordatorio
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 |
recallRequest |
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 |
returnRequest |
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
Descripción
Lee la configuración actual para la generación de carga de solicitudes masivas/continuas.
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 |
newConfiguration |
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 un malformado ResponseConfiguration json payload. |
No Content |
404 |
No se pudo encontrar el controlador de solicitudes con el proporcionado handlerId. |
No Contenido |
Estadísticas
Consulte el estado del simulador.
OBTENER /estadísticas
SentTransactionSummary
Consulte las transacciones para el simulador.
MagicValues
Recupera un informe de valor mágico para un controlador de solicitudes específico.
Recupera un informe de valor mágico para un controlador de solicitudes específico
GET /magicValues/{handlerId}
Parámetros
| Tipo | Nombre | Descripción | Esquema |
|---|---|---|---|
Ruta |
handlerId |
El identificador del controlador de solicitudes para obtener los valores mágicos. Esto está definido por la implementación. |
cadena |
Ejemplo HTTP respuesta
[ {
"description" : "it will reject the payment for accounts",
"path" : "request.CreditAccountIdentifier",
"value" : "PT2222666699988888"
}, {
"description" : "it will reject the payment for fraud",
"path" : "request.AccountIdentifier",
"value" : "PT2222444411166666"
}, {
"description" : "it will reject the payment for sanctions",
"path" : "request.AccountIdentifier",
"value" : "PT1111444455566666"
} ]
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 |
ukBankAccount |
Una representación de una cuenta bancaria del Reino Unido |
|
proxy |
Una representación de la identificación de cuentas proxy |
ukBankAccount
| Nombre | Descripción | Esquema |
|---|---|---|
código de clasificación |
Identificador de sucursal |
cadena |
accountNumber |
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 |
CurrencyCode
El código de moneda ISO 4217
Tipo: enum (GBP, USD, EUR, CHF, CAD, AUD, NZD, HKD, JPY, CNY)
FinancialAmount
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 requiere fracciones, por ejemplo, peniques para GBP y centavos para USD. |
entero (int64) |
moneda |
Ejemplo:CurrencyCode |
MagicValue
Valor mágico para el simulador utilizado para producir la respuesta.
| Nombre | Descripción | Esquema |
|---|---|---|
descripción |
Describa el valor mágico y cuál es el 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 |
assgneBic |
Asigne BIC |
cadena |
assgnrBic |
Asigne BIC |
cadena |
additionalTransportHeaders |
Encabezados de transporte opcionales para incluir con la solicitud. Estos encabezados se fusionarán y tendrán prioridad sobre cualquier encabezado generado por el global. |
objeto |
RequestDetails
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 |
originatorAccount |
Cuenta del deudor |
|
beneficiaryName |
Nombre de la persona que recibe los fondos |
cadena |
beneficiaryAccount |
Cuenta del acreedor |
|
remittanceInfo |
Información de remesas |
cadena |
additionalInfo |
Reservado para características extendidas de simuladores individuales |
objeto |
additionalTransportHeaders |
Encabezados de transporte opcionales para incluir con la solicitud. Estos encabezados se fusionarán con y tendrán prioridad sobre cualquier encabezado generado por el global. |
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 detenerse.
| Nombre | Descripción | Esquema |
|---|---|---|
requestsPerSecond |
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 en formato de duración ISO-8601.PnDTnHnMn.nS con días considerados exactamente de 24 horas. Ilimitado si no se establece, debe ser positivo de lo contrario. |
cadena (iso8601) |
transportHeaderDistributions |
Distribuciones para los encabezados de transporte generados. Las claves son los nombres de los encabezados, los valores son objetos.mapping valores de encabezado a la distribución porcentual deseada (que debe sumar 100 para cada encabezado). Estas distribuciones anularán cualquier distribución global para el mismo nombre de encabezado en el |
objeto |
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 |
|---|---|---|
requestsPerSecond |
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) |
cutOffTime |
El instante en el que el simulador dejará de producir solicitudes. No está configurado para un tiempo ilimitado. |
cadena (fecha-hora) |
RequestOutcome
Si esta solicitud fue aceptada o rechazada
| Nombre | Descripción | Esquema |
|---|---|---|
resultado |
ACSP para aceptado, RJCT para rechazado |
enum (ACSP, RJCT) |
additionalInfo |
información adicional específica de la implementación |
objeto |
ResolutionOfInvestigationRequestInitiator
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 |
additionalTransportHeaders |
Encabezados de transporte opcionales para incluir con la solicitud. Estos encabezados se fusionarán y tendrán prioridad sobre cualquier encabezado generado por el global. |
objeto |
ResponseConfiguration
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 |
ResponseConfigurations
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) |
additionalInfo |
información adicional específica de la implementación |
objeto |
additionalTransportHeaders |
Encabezados de transporte opcionales para incluir con la solicitud. Estos encabezados se fusionarán con y tendrán prioridad sobre cualquier encabezado generado por el global. |
objeto |
SentTransactionDetails
Detalles de todas las transacciones enviadas
| Nombre | Descripción | Esquema |
|---|---|---|
id |
Ejemplo:`"TestFlow|nQqyDbhqWrwJysoDVJrRVPTOfPhicTKgkAD"` |
cadena |
estado |
Ejemplo:`"Accepted"` |
cadena |
startTime |
El instante en que el simulador emitió la solicitud. |
cadena (fecha-hora) |
completedTime |
El instante en que el simulador recibió la respuesta completada. |
cadena (fecha-hora) |
timeTakeMs |
tiempo requerido para que las transacciones se completen completedTime-startTime). |
entero |
SentTransactionSummary
Detalles sobre transacciones
| Nombre | Descripción | Esquema |
|---|---|---|
totalSent |
Número total de transacciones recibidas a través de pagos salientes. |
entero |
accumulativeTimeMs |
Tiempo acumulativo requerido para completar las transacciones. |
número |
averageTransactionTimeMs |
Tiempo promedio requerido para completar las transacciones. |
entero |
startEventTime |
El instante en que el simulador envió su primera solicitud. |
cadena (fecha-hora) |
lastEventTime |
El instante en que 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 aquellas que fallaron por razones no relacionadas con el negocio. |
entero |
totalReceived |
Número total de transacciones recibidas a través de pagos entrantes. |
entero |
timedOut |
Número total de transacciones para las cuales no se recibió una respuesta oportuna. |
entero |
fallido |
Número total de transacciones que fallaron. |
entero |
recentLatency |
Latencia promedio reciente observada para que las respuestas lleguen a las solicitudes en ms. |
entero |
statusCounters |
Contadores específicos de implementación, incrementados al inspeccionar el mensaje y derivar un estado. |
<StatusCounter> matriz |
Explorando el Simulador API
La API del simulador puede ser explorada y triggered usando swagger-ui.
Se sirve por el simulador bajo la ruta /apidocs.
Puede activar el simulador utilizando la interfaz de usuario de Swagger.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 deberá:
-
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 la siguiente configuración de maven:
<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);
}
}
Definiciones de mensajes
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 la definición del mensaje. |
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 tipo de mensaje simbólico.
@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());
}
}
};
}
The 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 de legado 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 de initiateRequestParameters |
| 3 | Especifique el objeto objetivo para enriquecer desde additionalInfo |
| 4 | (Obsoleto) Opcional: sobrescriba este método para especificar cómo utilizar campos heredados de additionalInfo para enriquecer la solicitud |
Solicitud de Envase 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.service. Raw Request Wrapper interfaz.
@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 una definición de mensaje separada para esta solicitud en bruto particular. |
| 2 | Verifica el encabezado de metadatos para determinar si este envoltorio 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 necesitamos definir la definición del mensaje para eso tipo.
Un ejemplo de definir la definición del mensaje 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 solo para que podamos definir diferentes destinos y/o manejar de manera diferente esta solicitud en bruto particular |
| 3 | Destino al que debe enviarse el mensaje. Esto también puede ser gestionado únicamente por el transporte de mensajes asociado. |
| 4 | Dado que esto representa una solicitud en bruto, podríamos utilizar una cadena.mapper así y utilícelo más tarde en el transporte de mensajes tal como está. |
Transporte de mensajes
Para llevar el mensaje a su destino, necesitamos definir un MessageTransport.
Esto define una relación entre un tipo de mensaje simbólico y los medios para enrutarlo a su destino, llamado un Transporter.
En este ejemplo estamos configurando un Akka-basado HTTP transportista.
@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)
}
private Iterable<HttpHeader> headersFor(Message<T> message) {
return message.getMetadata().getHeaders().entrySet().stream() (2)
.map(entry -> HttpHeader.parse(entry.getKey(), entry.getValue()))
.collect(Collectors.toList());
}
// ...
}
| 1 | Recupere la URL objetivo de la definición de mensaje relevante. |
| 2 | Incluya los encabezados de mensaje generados como HTTP encabezados de solicitud.
Sin este paso, proporcionado additionalTransportHeaders or Generación de Encabezado de Transporte will simply be ignored. |
Generación de Encabezado de Transporte
El simulador incluye un generador de encabezados de transporte impulsado por configuración que puede agregar automáticamente encabezados a las solicitudes salientes basándose en distribuciones ponderadas.
Configuración
La generación del encabezado de transporte se configura a través de Spring Boot propiedades con el prefijo ipf.simulator.generate-transport-headers.
| Clave | Predeterminado | Descripción |
|---|---|---|
|
|
Si la generación de encabezados está habilitada. |
|
Un mapa de nombres de encabezados a sus distribuciones de valores ponderados. |
Ejemplo de configuración en application.conf:
ipf.simulator.generate-transport-headers {
enabled = true
headers {
ipf_version_selection {
LATEST = 80
OLDEST = 20
}
ipf_schema_version {
1 = 50
2 = 50
}
}
}
En este ejemplo, el 80% de las solicitudes tendrán ipf_version_selection: LATEST y el 20% tendrá ipf_version_selection: OLDEST mientras ipf_schema_version tendrá una división equitativa del 50–50 entre 1 y 2.
Fusión y Sobrescritura de Encabezados
Los encabezados generados por la configuración global pueden ser combinados o reemplazados por los encabezados proporcionados en cada uno. API solicitudes.
-
Solicitudes Únicas: Al utilizar puntos finales como
/sendRequest, usted puede proporcionaradditionalTransportHeadersen el cuerpo de la solicitud (consulte RequestDetails). Estos encabezados se fusionarán con los encabezados generados. Si existe un nombre de encabezado en ambos, el valor deadditionalTransportHeaderstiene prioridad. -
Solicitudes de Carga: Al iniciar una carga a través de
/requestLoad, usted puede especificartransportHeaderDistributions(vea RequestLoadConfiguration). Para cada encabezado especificado en este campo, se utilizará la distribución proporcionada para generar valores, anulando cualquier distribución global para el mismo nombre de encabezado. Los encabezados resultantes se fusionan luego con los encabezados generados a partir de otras distribuciones globales que no fueron anuladas.
Encabezados pasados a través del HTTP la solicitud (ya sea como valores individuales o como distribuciones) siempre anula los proporcionados por el global TransportHeaderGenerator configuración.
|
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. Request Handler interfaz. 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 WireMock "/endpoint" ruta. En lugar de WireMock un controlador 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 la configuración de application.conf archivos en las siguientes ubicaciones y los añade al Spring Entorno:
-
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('.yml') o archivos de propiedades ('.properties') como una alternativa a HOCON('.conf'). |
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.puerto |
55555 |
Puerto TCP para exponer el Simulador API on |
| Clave | Predeterminado | Descripción |
|---|---|---|
ipf.simulator.generate-transport-headers.enabled |
falso |
Si la generación de encabezados está habilitada |
ipf.simulator.generate-transport-headers.headers |
Mapa de nombres de encabezados a sus distribuciones de valores ponderados |
Magic values
El _Manejador de Solicitudes_API expone un método para recuperar una lista de Valores Mágicos 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 valores mágicos del simulador es únicamente 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 valores mágicos:
@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 el agente de Java de canela 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>
Configurar 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 formato compatible con Prometheus 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 fallidas) |
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 no se pudieron enviar 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. |
Métricas Personalizadas
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 todos los eventos de solicitud (solicitudes y respuestas en este caso)
@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_, así 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