DSL 15 - Texto de Error Dinámico
El paso del tutorial utiliza la solución "shared_models_two" del proyecto como su punto de partida.
Si en algún momento desea ver la solución a este paso, puede encontrarla en el add_dynamic_text solución.
== Propósito
En DSL 4 - Uso de un dominio externo, introdujimos el concepto de un código de razón para poder enriquecer nuestro events con conocimiento sobre la razón subyacente para el event se está planteando. En ese momento, solo utilizamos los campos de nombre y descripción para describir el código de razón. En esta sección, daremos un paso más y mostraremos cómo podemos configurar dinámicamente nuestro texto del código de razón en función de la información en el flujo. Por lo tanto, en lugar de tener solo un mensaje de error genérico que se utiliza para todos los pagos, podemos hacer que nuestro mensaje de error sea único para nuestro pago particular y, por lo tanto, proporcionar información enriquecida.
En nuestro caso, vamos a crear algunos reason codes para nuestra respuesta a fraudes y utilizarlos para comenzar a proporcionar mensajes de error dinámicos. Procedamos a ver cómo se hace esto.
== Uso del DSL
=== Marcadores de posición
Lo primero que necesitamos hacer es introducir un nuevo concepto, "marcadores de posición". Los marcadores de posición son una forma de extraer información de cualquier pieza dada de business data(o combinación de business data!) en un formato de cadena simple que podemos utilizar en la mensajería. El marcador tiene dos propiedades:
-
El nombre del marcador de posición
-
Una lista de business data.
Puede pensar en un marcador de posición como una función que recibe una lista de business data elementos y devuelve una cadena. En el caso más simple, el business data puede ser simplemente una cadena en sí misma, pero en casos más complejos, puede desear, por ejemplo, extraer un campo específico en el pacs. 008.
Creemos un marcador de posición para pretender extraer algunos datos de nuestro pacs. 008. Así que comenzamos añadiendo una nueva biblioteca de marcadores de posición haciendo clic derecho en nuestro modelo y seleccionando
Esto se verá y sentirá como todas las otras bibliotecas, así que comencemos haciendo clic en "Agregar marcador de posición" y luego ingresando:
-
un nombre de "Extracto de Pacs008"
-
una descripción de "Extraer una cadena del pacs008"
-
a business data elemento de " Customer Credit Transfer "
Cuando esté completo, debería verse así:
Ahora utilicemos nuestro marcador de posición para crear un texto dinámico para nuestro código de razón.
=== Texto Dinámico
Agreguemos un nuevo "conjunto de códigos de razón" a nuestra "biblioteca de códigos de razón" existente para nuestro sistema de fraude. Usted añadirá dos reason codes, el primero utilizará una descripción estándar mientras que el segundo también incluirá la definición de texto dinámico.
Así que comencemos añadiendo dos reason codes:
-
Uno con el nombre "Razón Simple" y la descripción "Una razón simple sin texto dinámico".
-
Uno con el nombre "Razón Dinámica" y la descripción "Una razón con texto dinámico".
Por la razón dinámica, añadamos un mensaje de texto dinámico. Comenzamos presionando Return (o CTRL+SPACE to bring up the available options and selecting DynamicText) in the text block. Now, we can type our message just like any normal text input. Let’s start by typing "This is a dynamic message. The special value is " into the box. We want to finish our text by grabbing the value of the "Extract from Pacs008" placeholder we set up. To do this, we simply press CTRL+[ESPACIO y luego selecciónelo del menú desplegable de opciones disponibles.
Cuando esté completo, nuestro conjunto de códigos de razón y reason codes debería verse así:
Finalmente, necesitamos añadir nuestro nuevo Fraude Reason codes en nuestra definición de respuesta para nuestra verificación de fraude para que podamos enviar de vuelta el código de razón.
Y eso es todo desde el punto de vista de DSL, ahora pasemos a nuestra implementación.
== Java Implementación
Ahora volvamos a Intellij y veamos cómo integrar esto en nuestro código de implementación. Como es habitual, comenzaremos ejecutando una compilación desde una ventana de terminal en el directorio raíz de nuestro proyecto:
mvn clean install
En nuestro trabajo con DSL creamos un marcador de posición. Lo describimos como una función que toma algunos business data y devuelve una cadena. ¡Eso es exactamente lo que es! Cada marcador de posición resultará en un método en el "Puerto de Marcadores de Posición" del dominio.
Veamos el código generado en /domain-root/domain/target, y ahora debemos encontrar el puerto para definir nuestros marcadores de posición de la siguiente manera:
package com.iconsolutions.ipf.tutorial;
import com.iconsolutions.iso20022.message.definitions.payments_clearing_and_settlement.pacs008. FIToFICustomerCreditTransferV08;
public interface IpftutorialmodelPlaceholderPort {
String executeExtractFromPacs008(FIToFICustomerCreditTransferV08 customerCreditTransfer);
}
Aquí podemos ver que hemos generado una función que toma nuestro FITo FICustomer Credit Transfer V08 y devuelve un String. Esto es lo que necesitaremos implementar y definir en nuestra configuración como de costumbre. Por ahora, haremos esto implementando una constante muy simple.mapping que devuelve la cadena "Test Mapping ". Debe agregar esto a la configuración del tutorial como se indica a continuación:
public IpftutorialmodelDomain init(ActorSystem actorSystem, IsoMappingService mappingService, SchedulerPort schedulerAdapter) {
// All adapters should be added to the domain model
return new IpftutorialmodelDomain. Builder(actorSystem)
.withTutorialDomainFunctionLibraryAdapter(input -> CompletableFuture.completedStage(new DuplicateCheckResponseInput. Builder(input.getId(), AcceptOrRejectCodes. Accepted).build()))
.withAccountingSystemActionAdapter(new SampleAccountingSystemActionAdapter())
.withFraudSystemActionAdapter(new FraudSystemActionAdapter())
.withDecisionLibraryAdapter(input ->
input.getCustomerCreditTransfer().getCdtTrfTxInf().get(0).getIntrBkSttlmAmt().getValue().compareTo(BigDecimal. TEN)>0?
RunFraudCheckDecisionOutcomes. FRAUDREQUIRED: RunFraudCheckDecisionOutcomes. SKIPFRAUD)
.withIpftutorialflowV1AggregateFunctionAdapter(input -> new ExtractCreditorAccountForFlowIpftutorialflowV1AggregateFunctionOutput(input.getCustomerCreditTransfer().getCdtTrfTxInf().get(0).getCdtrAcct()))
.withIpftutorialflowV2AggregateFunctionAdapter(input -> new ExtractCreditorAccountForFlowIpftutorialflowV2AggregateFunctionOutput(input.getCustomerCreditTransfer().getCdtTrfTxInf().get(0).getCdtrAcct()))
.withInitiationFlowAggregateFunctionAdapter(parameters -> new MapPain001ToPacs008ForFlowInitiationFlowAggregateFunctionOutput(mappingService.mapPain001toPacs008(parameters.getPaymentInitiation())))
.withSchedulerAdapter(schedulerAdapter)
.withCSMServiceActionAdapter(new SampleCSMServiceActionAdapter())
.withSanctionsSystemActionAdapter(new SampleSanctionsSystemActionAdapter())
.withPlaceholderAdapter(customerCreditTransfer -> "Test Mapping")
.build();
}
Lo último que necesitamos hacer es actualizar nuestro FraudCheckAdapter para proporcionar el reason codes. Para habilitar nuestras pruebas, lo configuraremos de la siguiente manera:
-
si el valor del pago > 40, rechazaremos con nuestro código de razón dinámico.
-
Si el valor del pago > 30 (y por debajo de 40), lo rechazaremos con nuestra descripción habitual.
Veamos el código para eso:
@Slf4j
public class FraudSystemActionAdapter implements FraudSystemActionPort {
@Override
public CompletionStage<Void> execute(final CheckFraudAction action) {
var id = action.getId();
log.debug("Received an action of type {} for id {}", action.getActionName(), id);
var intrBkSttlmAmt = action.getCustomerCreditTransfer().getCdtTrfTxInf().get(0).getIntrBkSttlmAmt().getValue();
if (intrBkSttlmAmt.compareTo(new BigDecimal("50")) >= 0) {
return CompletableFuture.runAsync(() -> log.debug("Pretending to timeout the fraud call for aggregate {}", action.getProcessingContext().getAssociationId()));
} else if (intrBkSttlmAmt.compareTo(new BigDecimal("40")) >= 0) {
return IpftutorialmodelDomain.fraudSystem().handle(
new FraudCheckResponseInput. Builder(id, AcceptOrRejectCodes. Rejected)
.withReasonCode(FraudReasonCodes. DynamicReason)
.build())
.thenAccept(done -> logResult(action, done));
} else if (intrBkSttlmAmt.compareTo(new BigDecimal("30")) >= 0) {
return IpftutorialmodelDomain.fraudSystem().handle(
new FraudCheckResponseInput. Builder(id, AcceptOrRejectCodes. Rejected)
.withReasonCode(FraudReasonCodes. SimpleReason)
.build())
.thenAccept(done -> logResult(action, done));
} else {
return fraudRequestReplySendConnector.send(action.getProcessingContext(), action.getCustomerCreditTransfer())
.thenCompose(response -> IpftutorialmodelDomain.fraudSystem()
.handle(new FraudCheckResponseInput. Builder(id, AcceptOrRejectCodes. Accepted).build())
.thenAccept(done -> logResult(action, done)));
}
}
private static void logResult(CheckFraudAction action, Done done) {
log.debug("Sent input of type {} for id {} with result {}", done.getCommandName(), action.getId(), done.getResult().name());
}
}
== Verificando nuestra solución
Como es habitual, ahora verifiquemos que la solución funcione. Inicie la aplicación como se indicó anteriormente (instructions están disponibles en Revisando la solicitud inicial si necesita un repaso!)
Ahora probemos nuestra aplicación. Comenzará enviando un pago de 45 USD.- esto debería darnos nuestro texto de error dinámico.
curl -X POST localhost:8080/submit -H 'Content-Type: application/json' -d '{"value": "45"}' | jq
Luego, traigamos el pago en el Developer GUI y observe el domain events(buscar por unit of work id, haga clic en ver, haga clic domain events) entonces vemos:
Ahora, si hacemos clic para ver el cuerpo de nuestro Control de Fraude. Failed event, veremos:
Y podemos ver que nuestro código de razón ha sido generado, extrayendo el valor de nuestra función de marcador de posición.
Sin embargo, si repetimos el proceso para un pago de 35USD, en lugar de utilizar el texto dinámico, revertiremos a utilizar únicamente la descripción proporcionada:
Eso es todo funcionando.
Esta es, evidentemente, una implementación muy simple con algo de texto codificado, pero usted puede proporcionar una implementación del puerto (es decir, la interfaz Ipftutorialmodel Placeholder Port) que acceda a cualquiera de los datos de pago;
public class PlaceHolderAdapter implements IpftutorialmodelPlaceholderPort {
@Override
public String executeExtractFromPacs008(FIToFICustomerCreditTransferV08 customerCreditTransfer) {
return "failed for amount " + customerCreditTransfer.getCdtTrfTxInf().get(0).getIntrBkSttlmAmt().getValue().toString();
}
}
== Conclusiones
En esta sección aprendimos cómo podemos utilizar marcadores de posición para proporcionar información de enriquecimiento dentro del texto de error.