Saltar al contenido

Rutar mensajes XML por tipo de nodo en Jitterbit Studio

Introducción

Cuando un único endpoint entrega más de un tipo de mensaje XML, la integración debe inspeccionar la carga útil antes de poder procesarla. Una carpeta compartida de entrada, cola o endpoint de API puede contener tanto una actualización de registro como una cancelación de registro, cada una utilizando un elemento diferente bajo la misma raíz. La operación que maneja las actualizaciones no puede procesar un mensaje de cancelación, por lo que la carga útil debe ser examinada y dirigida a la operación que le corresponde.

Para probar si un nodo específico está presente, ejecuta una consulta XPath contra la carga útil con SelectNodes y cuenta los resultados con Length. Un resultado vacío significa que el nodo está ausente. Luego, se ramifica en ese resultado y se llama a la operación correspondiente con RunOperation.

Esta guía utiliza un mensaje de pedido como ejemplo. Ambos tipos de mensajes comparten la misma raíz y elementos de cuerpo, y difieren solo en el elemento directamente debajo del cuerpo:

Create or update message        Cancellation message

Envelope                        Envelope
└── Body                        └── Body
    └── OrderUpdate                 └── OrderCancellation
        └── Order                       └── Order

Esta estructura es común en sobres de estilo de intercambio, donde un elemento raíz compartido lleva ya sea un registro o un evento sobre ese registro. El objetivo es detectar cuál de OrderUpdate o OrderCancellation está presente, y luego invocar ya sea la operación Insert or Update Order o la operación Cancel Order.

Nota

GetNodeName está diseñado para leer el nombre de un nodo que ya ha sido devuelto por una función como SelectSingleNode, no para probar la presencia de un nodo directamente. Para usarlo para el enrutamiento, emparejalo con SelectSingleNode como se muestra en Opción B a continuación.

Patrón de diseño

La lógica de enrutamiento vive en un paso de script en su propia operación de enrutador, aguas arriba de las operaciones que realizan el trabajo:

flowchart LR A[Source activity
reads XML] --> B[Script step
router] B --> C[Insert or
Update Order] B --> D[Cancel Order]

La operación del enrutador lee la carga útil, determina el tipo de mensaje y llama exactamente a una operación descendente. Cada operación descendente se mantiene simple, porque solo recibe el tipo de mensaje para el que fue construida.

Paso 1: Leer la carga útil en una variable

El script del enrutador necesita el XML sin procesar como una cadena. Cómo lo obtienes depende de cómo llega el mensaje.

  • Desde un archivo o actividad de almacenamiento: Usa ReadFile con una ruta de referencia a la actividad de lectura:

    $gv_payload = ReadFile("<TAG>activity:tempstorage/Inbound XML/tempstorage_read/Read Message</TAG>");
    
  • Desde una API del API Manager: Usa la variable jitterbit.api.request.body, que contiene la carga útil enviada:

    $gv_payload = $jitterbit.api.request.body;
    

Asignar la carga útil a una variable global tiene un segundo propósito. Las operaciones llamadas con RunOperation heredan todas las variables globales, por lo que la operación descendente puede leer el mensaje desde $gv_payload sin leer la fuente una segunda vez.

Paso 2: Identificar el espacio de nombres

La mayoría de los XML de aplicaciones empresariales tienen espacio de nombres. Si el elemento raíz de la carga útil lleva un atributo xmlns, cada elemento en el documento pertenece a ese espacio de nombres, y una consulta XPath que lo omite no coincide con nada.

Abre una carga útil de muestra y observa el elemento raíz:

<Envelope xmlns="http://example.com/schemas/orders">

Copia la URI del espacio de nombres. En el siguiente paso, declaras un prefijo para él y usas ese prefijo en cada elemento de la consulta XPath. SelectNodes acepta estas declaraciones como argumentos de cadena adicionales en forma de prefix=uri.

Si el elemento raíz no tiene un atributo xmlns, el documento no tiene espacio de nombres. Omite las declaraciones de prefijo y los prefijos en los nombres de los elementos.

Paso 3: Prueba si el nodo existe

SelectNodes devuelve un arreglo de cada nodo que coincide con la consulta. Cuando no hay coincidencias, el arreglo está vacío, por lo que Length devuelve 0. Comparar ese conteo con cero es la prueba de existencia:

$gv_updateNodes = SelectNodes($gv_payload,
    "/o:Envelope/o:Body/o:OrderUpdate",
    "o=http://example.com/schemas/orders");

// True when the payload contains a create or update message
Length($gv_updateNodes) > 0;

Reemplaza el URI del espacio de nombres con el de el Paso 2, y reemplaza los nombres de los elementos con los de tu propia carga útil.

Consejo

Consulta la ruta específica en lugar del nombre del elemento solo. Una ruta anclada en la raíz confirma tanto que el elemento está presente como que aparece donde el esquema lo espera, lo que evita una coincidencia falsa en un elemento con el mismo nombre en otra parte del documento.

Paso 4: Rutea a la operación coincidente

Elige uno de los siguientes dos enfoques. La Opción A es la elección más directa para dos tipos de mensajes. La Opción B es la mejor opción para tres o más, porque ejecuta una consulta XPath independientemente de cuántos tipos de mensajes maneje el endpoint.

Opción A: Prueba cada tipo de mensaje por turno

Agrega un paso de script a la operación del enrutador. El script prueba primero el mensaje de actualización y luego recurre al mensaje de cancelación.

Reemplaza la ruta de referencia de actividad, el URI del espacio de nombres, los nombres de los elementos y las dos rutas de referencia de operación con los valores de tu propio proyecto.

// Read the inbound message
$gv_payload = ReadFile("<TAG>activity:tempstorage/Inbound XML/tempstorage_read/Read Message</TAG>");

// Look for the create or update message: Envelope/Body/OrderUpdate
$gv_updateNodes = SelectNodes($gv_payload,
    "/o:Envelope/o:Body/o:OrderUpdate",
    "o=http://example.com/schemas/orders");

// Look for the cancellation message: Envelope/Body/OrderCancellation
$gv_cancelNodes = SelectNodes($gv_payload,
    "/o:Envelope/o:Body/o:OrderCancellation",
    "o=http://example.com/schemas/orders");

If(Length($gv_updateNodes) > 0,
    WriteToOperationLog("Routing to insert or update.");
    If(!RunOperation("<TAG>operation:Insert or Update Order</TAG>"),
        RaiseError(GetLastError())
    );
,
    If(Length($gv_cancelNodes) > 0,
        WriteToOperationLog("Routing to cancellation.");
        If(!RunOperation("<TAG>operation:Cancel Order</TAG>"),
            RaiseError(GetLastError())
        );
    ,
        RaiseError("Unrecognized message type: no OrderUpdate or OrderCancellation node found.")
    );
);

Opción B: Lee el tipo de mensaje del cuerpo

Debido a que ambos tipos de mensajes ocupan la misma posición en el documento, puedes seleccionar cualquier elemento que esté bajo el cuerpo y leer su nombre, luego ramificarte en ese único valor. SelectSingleNode devuelve el primer nodo coincidente, y GetNodeName devuelve su nombre. El * en la consulta XPath coincide con un elemento de cualquier nombre, por lo que la consulta no necesita conocer los tipos de mensajes de antemano.

Este enfoque también registra el tipo de mensaje en el registro de operaciones en cada ejecución, lo cual es útil cuando las cargas útiles del endpoint no están completamente documentadas.

// Read the inbound message
$gv_payload = ReadFile("<TAG>activity:tempstorage/Inbound XML/tempstorage_read/Read Message</TAG>");

// Select whatever element sits directly under Body, then read its name
$gv_bodyChild = SelectSingleNode($gv_payload,
    "/o:Envelope/o:Body/*",
    "o=http://example.com/schemas/orders");
$gv_messageType = GetNodeName($gv_bodyChild);

WriteToOperationLog("Message type: " + $gv_messageType);

// Route on the message type. Add a branch for each additional type,
// and raise an error on anything unrecognized.
If($gv_messageType == "OrderUpdate",
    If(!RunOperation("<TAG>operation:Insert or Update Order</TAG>"),
        RaiseError(GetLastError())
    );
,
    If($gv_messageType == "OrderCancellation",
        If(!RunOperation("<TAG>operation:Cancel Order</TAG>"),
            RaiseError(GetLastError())
        );
    ,
        RaiseError("Unrecognized message type: " + $gv_messageType)
    );
);

Una carga útil cuyo elemento de cuerpo no coincide con ninguna de las ramas cae en el RaiseError final, que también cubre el caso en el que el cuerpo no tiene ningún elemento hijo en absoluto.

Puntos clave sobre ambos scripts:

  • RunOperation devuelve false cuando la operación llamada falla. Probar ese valor de retorno y llamar a RaiseError con GetLastError detiene la operación del enrutador y activa su acción configurada de On Fail. Sin esta prueba, un fallo en la operación descendente deja al enrutador informando éxito.
  • Generar un error en un tipo de mensaje no reconocido revela cambios en el esquema y cargas útiles mal formadas en los registros de operaciones en lugar de permitir que el mensaje pase sin procesar.
  • RunOperation se ejecuta de manera sincrónica por defecto, por lo que el enrutador espera a que la operación descendente termine. Esto es lo que permite al enrutador detectar un fallo descendente. Ejecutar de manera asincrónica devolvería el control de inmediato e informaría solo si la operación fue encolada.
  • Las operaciones llamadas con RunOperation están encadenadas y se ejecutan en el mismo agente que el enrutador.
  • WriteToOperationLog registra qué rama se tomó, lo que hace que un mensaje mal enrutado sea fácil de diagnosticar después del hecho.

Paso 5: Proporcionar a las operaciones descendentes su entrada

Cada operación descendente necesita la carga útil que el enrutador inspeccionó. Utiliza cualquiera de estas que se ajuste al proyecto:

  • Leer de la variable global. La operación descendente hereda $gv_payload del enrutador. Mapea o script desde esa variable directamente, sin una segunda lectura de la fuente.
  • Leer la fuente nuevamente. Si la carga útil se encuentra en almacenamiento temporal u otra ubicación persistente, la operación descendente puede usar su propia actividad de lectura contra la misma ubicación.

Cada operación descendente tiene un esquema de origen para un solo tipo de mensaje, lo que mantiene su transformación sencilla.

Verificar la integración

  1. Prepara una carga útil de muestra de cada tipo de mensaje, además de una tercera carga útil cuyo elemento de cuerpo no coincida con ninguno.

  2. Despliega y ejecuta la operación del enrutador contra el mensaje de creación o actualización.

  3. En los registros de operaciones, confirma que el enrutador registró la ruta de actualización y que la operación Insertar o Actualizar Pedido se ejecutó. Confirma que la operación Cancelar Pedido no se ejecutó.

  4. Ejecuta el enrutador contra el mensaje de cancelación y confirma lo contrario: la operación Cancelar Pedido se ejecutó y la operación Insertar o Actualizar Pedido no se ejecutó.

  5. Ejecuta el enrutador contra la carga útil no reconocida y confirma que la operación falló con el error "Tipo de mensaje no reconocido" y que ninguna operación descendente se ejecutó.

  6. Si cada carga útil cae en la rama no reconocida, el espacio de nombres es lo primero que se debe verificar. Confirma que la URI en el script coincida exactamente con el valor de xmlns en el elemento raíz de la carga útil, incluyendo cualquier barra diagonal al final, y que cada elemento en la consulta XPath lleve el prefijo declarado.

  7. Si una carga útil se enruta a la operación incorrecta, registra los conteos de nodos (Opción A) o el tipo de mensaje (Opción B) y compáralos con la carga útil de muestra. WriteToOperationLog("Nodos de actualización: " + Length($gv_updateNodes)) confirma con qué coincidió realmente la consulta.