DSL 15 - Texto de Error Dinámico

Iniciando

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, esta se puede encontrar 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 nuestros eventos con conocimiento sobre la razón subyacente por la cual se genera el evento. En ese momento, solo utilizábamos 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 el texto de nuestro código de razón basado en 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 ende, proporcionar información enriquecida.

En nuestro caso, vamos a crear algunos códigos de razón para nuestra respuesta ante el fraude y utilizarlos para comenzar a proporcionar mensajes de error dinámicos. Procedamos a ver cómo se realiza 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 conjunto de datos empresariales (¡o combinación de datos empresariales!) en un formato de cadena simple que podemos utilizar en la mensajería. El marcador de posición tiene dos propiedades:

  • El nombre del marcador de posición

  • Una lista de datos empresariales.

Puede pensar en un marcador de posición como una función que recibe una lista de elementos de datos empresariales y devuelve una cadena. En el caso más simple, los datos empresariales pueden ser solo una cadena en sí misma, pero en casos más complejos, puede, por ejemplo, desear extraer un campo específico en el pacs.008.

Creemos un marcador de posición para simular la extracción de 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 Nuevo  v2Flo  Biblioteca de Marcadores de Posición

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 extracto de "Pacs008"

  • una descripción de "Extraer una cadena del pacs008"

  • un elemento de datos empresariales de " Customer Credit Transfer "

Cuando esté completo, debería verse así:

error 1

Ahora utilicemos nuestro marcador de posición para crear un texto dinámico para nuestro código de razón.

Texto Dinámico

Añadamos un nuevo "conjunto de códigos de razón" a nuestra existente "biblioteca de códigos de razón" para nuestro sistema de fraude. Usted añadirá dos códigos de razón, el primero utilizará solo 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 códigos de razón:

  • 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 los códigos de razón deberían verse así:

error 2

Finalmente, necesitamos agregar nuestros nuevos códigos de Motivo de Fraude en nuestra definición de respuesta para nuestra verificación de fraude, de modo que podamos enviar de vuelta el código de motivo.

error 3

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 datos comerciales 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". Usted añadirá 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 los códigos de razón. 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ámica.

  • Si el valor del pago > 30 (y por debajo de 40), lo rechazaremos con nuestra descripción normal.

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 cómo funciona la solución. Inicie la aplicación como se indicó anteriormente (las instrucciones 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 id de unidad de trabajo, haga clic en ver, haga clic domain events) entonces vemos:

error 4

Ahora, si hacemos clic para ver el cuerpo de nuestro evento de Verificación de Fraude Fallida, veremos:

error 5

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, digamos, 35USD, en lugar de utilizar el texto dinámico, revertiremos a utilizar únicamente la descripción proporcionada:

error 6

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.