Skip to content

PIDX RNIF

Purpose

Build and send (or receive) RNIF envelopes that contain one of the following PIDX payloads:

  • Invoice
  • InvoiceResponse
  • FieldTicket
  • FieldTicketResponse
  • Quote
  • QuoteNotification
  • QuoteRequest
  • OrderChange
  • OrderCreate
  • OrderResponse
  • Receipt Acknowledgment
  • Quote
  • Exception

See:

Methods

Binding name: pidxrnif


Method: Tuple4 postAndWaitResponse( final String xmlConf, final Map< String, String > parameters, final CloseableHttpClient httpClient, final HttpPost postMethod )

Using the supplied XML configuration metadata (see below) and a Map of optional parameters, an RNIF payload is constructed and POSTed to a trading partner using the supplied httpClient and postMethod.

The httpClient and postMethod are from the Apache Http Components API. The httpClient is best built using the securesocket DSL.

The POST response is received and parsed. The Tuple4 object returned contains the following:

  • Http Status Code
  • Http Response Message
  • Http Response Entity
  • The Content-Type of the Response Entity

Method: void sendMessageToStream( final String xmlConf, final OutputStream os, final Map< String, String > parameters )

A component method of the postAndWaitResponse(). Using the supplied XML configuration metadata (see below) and a Map of optional parameters, an RNIF payload is constructed and written to the supplied OutputStream.


Method: Tuple4 readResponseFromStream( int responseCode, String responseMessage, Map< String, String > headers, InputStream is )

A component method of the postAndWaitResponse(). Given the components of an Http Response, build the standard Tuple4 response structure.


Method: Tuple2< String, String > receive( Map< String, String > headers, InputStream is, final Closure partClosure )

Method: Tuple2< String, String > receive( Map< String, String > headers, byte[] ba, final Closure partClosure )

Trading Partners send RNIF messages to a URL configured using the routes service and p6rest component via an HTTPS POST request. Neither HTTP nor GET requests are advisable. The POST headers and POST entity should be supplied as Map and byte[]/InputStream arguments respectively. HTTPS is not supported directly via the Platform 6 instance. An inbound HTTP(S) proxy is required for this such as NGINX or Apached.

Files (the content + the attachments) will be detached to the p6 temporary folder (e.g /opt/b2box5.data/tmp). Each time an RNIF Message is processed the given closure will be called with the following arguments:

  • Message Concept
  • Message Content as XML

The Tuple2 object returned contains:

  • Content-Type of Response Message
  • Response message

RNIF Message Metadata

Typically created via an XSLT using a Transaction document such as a MessageInfo:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0" exclude-result-prefixes="#all"> 
    <xsl:output method="xml" omit-xml-declaration="no"/> 

    <xsl:param name="TIMESTAMP"/>
    <xsl:param name="CONTEXTID"/>

    <xsl:variable name="uri"><xsl:value-of select="/MessageInfo/CurrentDocumentURI"/></xsl:variable>   
    <xsl:variable name="SenderDUNS"><xsl:value-of select="/MessageInfo/KeyValue[Key='SenderDUNS']/Value"/></xsl:variable>
    <xsl:variable name="SenderLocationID"><xsl:value-of select="/MessageInfo/KeyValue[Key='SenderLocationID']/Value"/></xsl:variable> 
    <xsl:variable name="ReceiverDUNS"><xsl:value-of select="/MessageInfo/KeyValue[Key='ReceiverDUNS']/Value"/></xsl:variable>   
    <xsl:variable name="ReceiverLocationID"><xsl:value-of select="/MessageInfo/KeyValue[Key='ReceiverLocationID']/Value"/></xsl:variable>   
    <xsl:variable name="MessageID"><xsl:value-of select="/MessageInfo/Id"/></xsl:variable>   
    <xsl:variable name="BusinessDocName"><xsl:value-of select="/MessageInfo/BusinessDocName"/></xsl:variable>   

    <xsl:template match="/">
        <RnifMessage>
            <MetaData>
                <ReceiverDUNS><xsl:value-of select="$ReceiverDUNS"/></ReceiverDUNS>
                <ReceiverLocationID><xsl:value-of select="$ReceiverLocationID"/></ReceiverLocationID>
                <SenderDUNS><xsl:value-of select="$SenderDUNS"/></SenderDUNS>
                <SenderLocationID><xsl:value-of select="$SenderLocationID"/></SenderLocationID>
                <MessageTrackingID><xsl:value-of select="$MessageID"/></MessageTrackingID>
                <PipInstanceIdentifier><xsl:choose>
                     <xsl:when test="$BusinessDocName = 'Quote'"><xsl:value-of select="concat('JDE_QPR_.',$MessageID)"/></xsl:when>
                     <xsl:otherwise><xsl:value-of select="$MessageID"/></xsl:otherwise>
                </xsl:choose></PipInstanceIdentifier>
                <Concept><xsl:choose>
                    <xsl:when test="$BusinessDocName = 'Invoice'">Invoice</xsl:when>
                    <xsl:when test="$BusinessDocName = 'InvoiceResponse'">InvoiceResponse</xsl:when>
                    <xsl:when test="$BusinessDocName = 'Field Ticket'">FieldTicket</xsl:when>
                    <xsl:when test="$BusinessDocName = 'FT Response'">FieldTicketResponse</xsl:when>
                    <xsl:otherwise><xsl:value-of select="$BusinessDocName"/></xsl:otherwise>
                </xsl:choose></Concept>
                <GlobalUsageCode>Test</GlobalUsageCode>
            </MetaData>

            <Content>
                <ContentID>pidxContent</ContentID>
                <Description>xml</Description>
                <MimeType>text/xml</MimeType>
                <URI><xsl:value-of select="$uri"/></URI>
            </Content>

            <Attachments>
                <!-- <IgnoreMissingAttachment>false</IgnoreMissingAttachment> -->
                <xsl:apply-templates select="//Attachment"/>
            </Attachments>
        </RnifMessage>
    </xsl:template>

    <xsl:template match="Attachment">
        <Content>
            <ContentID>Attachment<xsl:value-of select="position()"/></ContentID>
            <Description>Other</Description>
            <MimeType><xsl:value-of select="ContentType"/></MimeType>
            <URI><xsl:value-of select="URI"/></URI>
        </Content>
    </xsl:template>
</xsl:stylesheet>

Some additional parameters can be passed to the DSL via a Map which will provide default values when they are not available in the Metadata XML:

XPath Description
/RnifMessage/MetaData/ReceiverDUNS The receiver DUNS number
/RnifMessage/MetaData/ReceiverLocationID The receiver Location Id parameter
/RnifMessage/MetaData/SenderDUNS The sender DUNS number
/RnifMessage/MetaData/SenderLocationID The sender Location Id parameter
/RnifMessage/MetaData/MessageTrackingID The RNIF unique Message Tracking ID
/RnifMessage/MetaData/PipInstanceIdentifier The RNIF PIP Instance Identifier
/RnifMessage/MetaData/Concept The document Type Invoice, ReceiptAcknowledgment, Quote, etc…
/RnifMessage/MetaData/GlobalUsageCode The RNIF Global usage code; usually Test or Production
/RnifMessage/Content/URI The URI to the XML file with the content. The file MUST be UTF-8 encoded.
/RnifMessage/Content/TransferEncoding The Content-Transfer-Encoding to set on the Mime Part holding the content, defaults to binary when not specified
/RnifMessage/Attachments/IgnoreMissingAttachment If set to true and an attachment file cannot be found at the URI specified in the Metadata XML, the attachment will be ignored and not sent
/RnifMessage/Attachments/SendEmptyAttachment If set to true and an attachment file is found at the URI specified in the Metadata XML but is empty, the attachment is sent. Otherwise an error is raised.
/RnifMessage/Attachments/Content Optional and can be repeated
/RnifMessage/Attachments/Content/ContentID The content ID header
/RnifMessage/Attachments/Content/Description The description of the attachment, optional
/RnifMessage/Attachments/Content/MimeType The mime type (with charset when appropriate) of the part
/RnifMessage/Attachments/Content/URI The URI of the attachment
/RnifMessage/Attachments/Content/TransferEncoding The Content-Transfer-Encoding to set on the Mime Part holding the content. Defaults to binary when not specified

Example Rnif Metadata XML

<?xml version="1.0" encoding="UTF-8"?>
<RnifMessage>
    <MetaData>
        <ReceiverDUNS>146880422</ReceiverDUNS>
        <ReceiverLocationID>Houston</ReceiverLocationID>
        <SenderDUNS>065485625</SenderDUNS>
        <SenderLocationID>Houston</SenderLocationID>
        <MessageTrackingID>065485625|P21|TP_CVX_PIDX_352438_INVOIC_94973051_200712191107049|Invoice|Invoice Notification</MessageTrackingID>
        <PipInstanceIdentifier>TP_CVX_PIDX_352438_INVOIC_94973051_200712191107049</PipInstanceIdentifier>
        <Concept>Invoice</Concept>
        <GlobalUsageCode>Test</GlobalUsageCode>
    </MetaData>

    <Content>
        <URI>file://outbound/Invoice-ContentsXML.xml</URI>
    </Content>

    <Attachments>
        <IgnoreMissingAttachment>false</IgnoreMissingAttachment>
        <Content>
            <ContentID>Attachment1</ContentID>
            <Description>Field Ticket</Description>
            <MimeType>application/pdf</MimeType>
            <URI>file://outbound/sample.pdf</URI>
        </Content>
        <Content>
            <ContentID>Attachment2</ContentID>
            <Description>scan</Description>
            <MimeType>application/pdf</MimeType>
            <URI>file://outbound/sample2.pdf</URI>
            <TransferEncoding>base64</TransferEncoding>
        </Content>
    </Attachments>
</RnifMessage>

Examples

import org.apache.http.client.methods.HttpPost
import org.apache.http.client.config.RequestConfig

// Sixty second timeout
def timeout = 60

// Use Xslt to build configuration meta data from input transaction
def xmlRnifConfig = xslt.process( "RnifMessage", resource.get('RnifMessage'), transaction )

// A few parameter overrides for this script
def parameters = [
    'globalUsageCode': 'Test',
    'ignoreMissingAttachment': 'false',
    'deleteAttachmentLocations': 'true'
]

def config = RequestConfig.custom()
    .setConnectTimeout( timeout * 1000 )
    .setConnectionRequestTimeout( timeout * 1000 )
    .setSocketTimeout( timeout * 1000 )
    .build()

// TWO_WAY TLS is a common requirement of RNIF POSTs
def ctx = securesocket.contextBuilder()
    .setType( SecureContext.BundleType.TWO_WAY_TRUST_ANY )
    .setIdentityPrivateKeyPath( "file://${B2BOX_DATA}/resources/certificates/privatekey.pem" )
    .setIdentityCertsPaths( "file://${B2BOX_DATA}/resources/certificates/publickey.pem" )
    .build()

// Further customise our httpClient builder
def clientBuilder = securesocket.clientBuilder( ctx )
    .disableAuthCaching()
    .disableAutomaticRetries()
    .disableCookieManagement()
    .setDefaultRequestConfig( config )

// Auto-close 'with' block
clientBuilder.build().withCloseable { client ->
    def tpl = pidxrnif.postAndWaitResponse( xmlRnifConfig, parameters, client, new HttpPost( "https://httpbin.org/post" ) )

    // Extract the response from teh Tuple4
    def responseStatus = tpl.first
    def responseReason = tpl.second
    def responseMessage = tpl.third
    def responseMessageContentType = tpl.fourth

    assert responseStatus == 200   
}

Inbound POST Route

rest('/public/rnif')
    .post('/receive')
    .consumes('multipart/related;boundary="RN-Http-Body-Boundary";type="multipart/related"')
    .produces('text/plain')
    .to('p6cmb://scripts?platform6.request.action=execute&id=RNIFInbound')
    .id('RNIFInbound')

Once the route is deployed, a POST test can be made to: https://localhost:8443/b2box/rest/resource/public/rnif/receive or alternatively to an NGINX mapped destination (recommended)

Script: RNIFInbound

def tpl = pidxrnif.receive( pipeline.toStringMap(), pipeline.getBytes('body'), { concept, xml ->
    println '++++++ Got RNIF Part!  Concept: ' + concept + '\nXml: ' + xml
})

// Build the Http POST response into the pipeline
pipeline.put("Content-Type", tpl.first)
pipeline.put("body", tpl.second, tpl.first)
pipeline.put("CamelHttpResponseCode", (tpl.second=='OK') ? '200' : '500')