Leap Data Transformations

What are Transformations?

Leap Data Transformations are often referred to simply as “transformations”. The reason we call them Leap Data Transformations is due to the fact that the Leap Framework includes an API for performing standard transformations as well as transforming data that is already in LDC.

Transformations are not serializers and do not implement reflection.

Transformations are implemented using XSL-T. As most of the data flowing through Leap will be either: String, XML, or JSON; it’s easy to convert the data to XML and apply an XSL stylesheet and then convert to your data format of choice. This approach is used instead of manual serialization because it allows for the transformation files to be stored outside of the project classpath for ease of access and updates. Additionally, XML and XSL-T are generally easier to understand for less technical users.

Where are Transformations used?

Most commonly, transformations will occur directly in the implementation route or as part of a Leap Entity definition. However, transformations can be done manually inside of a bean that is called from any route. Keep in mind that the primary benefit of Leap Transformations is that they are highly configurable; if you are going to do a transformation manually you should keep the XSL file external or consider another approach. It may be advisable to apply manual transformations if you wish to have more control over performance.

In-Route Transformation

In-Route transformation is very powerful as it gives you full control over when, where, and how a transformation is applied. In the case of Entities, the data pipeline occurs in a specific order.

While data is traversing a route, you can apply a transformation by invoking one of the many transformation methods, such as transformData of the LeapConfigurationUtil bean. Depending on the overload, transformations can occur directly on the body of the Exchange or be placed into LDC.

When you apply a transformation such as, transformDataFromExchange(xslFileName, exchange) it takes the Exchange as a parameter and applies the transformation directly on the message body; which results in the newly transformed data being applied to the body.

Yet, other methods such as transformDataFromLdcGetTagAndPushToLDC(xsmlFileName, '#data', '#transformed_data', exchange) will maintain the original message body. This is because the 2nd parameter of this method will source the data to be transformed from the specific tag in LDC and then place the data back into LDC using the tag name mentioned as the 3rd parameter. If you want to apply a transformation to data in LDC and then set that data as the Exchange body, you must first fetch the data from LDC to Exchange body and then apply a transformation.

LeapConfigurationUtil Transformation Methods

  • void transformDataFromLdcGetTagAndPushToLDC(String xslFileName, String ldcTagToGet, String ldcTagToPut, Exchange exchange)
  • String transformDataFromLdcGetTag(String xslFileName, String ldcTagToGet, Exchange exchange)
  • void transformDataFromExchange(String xslFileName, Exchange exchange)
<route>
    <from uri="direct:processing"></from>
        <to uri="bean:leapConfigUtil?method=jsonToXML" />
        <to uri="bean:leapConfigUtil?method=transformdataFromLdcGetTagAndPushToLDC(${in.header.transFileName},'#leap_initial','#enriched_leap_initial')" />
        <to uri="bean:leapConfigUtil?method=xmlToJson('#enriched_leap_initial')"></to>
</route>
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
	xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
	<xsl:output method="xml" />
	<xsl:template match="/">
		<data>
			<messageType>WcsPickWave</messageType>
			<Pick>
				<pickid>
					<xsl:value-of select="pickId" />
				</pickid>
				<quantity>
					<xsl:value-of select="quantity" />
				</quantity>
				<sourcelocation>
					<xsl:value-of select="sourceLocation" />
				</sourcelocation>
				<sourcezone>
					<xsl:value-of select="sourceZone" />
				</sourcezone>
				<type>
					<xsl:value-of select="type" />
				</type>
				<partnumber>
					<xsl:value-of select="partNumber" />
				</partnumber>
			</Pick>

		</data>
	</xsl:template>
</xsl:stylesheet>
{
    "pickId": "123",
    "quantity": 1,
    "sourceLocation": "CCF20843Do1",
    "sourceZone": "208",
    "partNumber": "abc123",
    "type": "soft"
}
{
    "messageType": "WcsPickWave",
    "Pick": {
        "pickid": "123",
        "quantity": 1,
        "sourcelocation": "CCF20843Do1",
        "sourcezone": "208",
        "partnumber": "abc123",
        "type": "soft"
    }
}

Leap Entity Transformation

Transformation is an optional step when defining a Leap Entity. There are two times in an entity definition where a transformation can occur:

  1. Prior to calling an entity service
  2. After calling an entity service

The reason for these two cases is that a transformation may be needed in order to call the entity service that is expecting a specific formation. Likewise, the response from the entity service call may not be in a desirable format for a return response from Leap. In that case, the second transformation can be applied to coerce data into a proper return structure.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE project>
<LeapDataServices version="1.0">
	<Entity name="robot" isEnable="true">
		<EntityDef>
			<EntityFields DefType="JSONSchema">
			</EntityFields>
		</EntityDef>

		<EntityAccessConfig configType="WS-Rest">
			<FeatureServiceConfig>
				<FeatureServiceRouteURI
					featureServiceName="picking" serviceName="processPicks">
				</FeatureServiceRouteURI>
			</FeatureServiceConfig>

			<EntityRestAccess accessType="read"
				authorizedResource="robotRequest" accessMethod="POST"
				serviceName="" serviceMethod="">

				<EntityRestRequest contentType="json">
					<EntityRestRequestHeaders>
						<EntityRestRequestHeader name="accountId"
							source="static" value="ALL" />
						<EntityRestRequestHeader name="siteId"
							source="static" value="all" />
					</EntityRestRequestHeaders>

					<EntityRestRequestBody source="LDC">
					<TransformationConfig required="true" fileName="6RS_Pick.xsl"></TransformationConfig>
						<LDCRequestConfigs>
							<ApplyLDCConfig>
								<LDCSchema required="false"
									schemaFileName="schemaFileName" />
								<LDCProjection required="false"
									projectionFileName="projectionFileName"
									projectionSource="swagger" />
								<LDCTaxonomy required="true"
									taxonomyFileName="riverSystems" />
							</ApplyLDCConfig>
						</LDCRequestConfigs>
						
					</EntityRestRequestBody>
				</EntityRestRequest>

				<EntityRestResponse>
				    <TransformationConfig required="true" fileName="leap_format.xml"></TransformationConfig>
					<LDCResponseConfigs>
						<ApplyLDCConfig applyAt="impl" sequence="1">
							<LDCSchema required="false"
								schemaFileName="schemaFileName" />
							<LDCProjection required="false"
								projectionFileName="projectionFileName"
								projectionSource="swagger" />
							<LDCTaxonomy required="false"
								taxonomyFileName="attunedlabs" />
						</ApplyLDCConfig>
					</LDCResponseConfigs>
				</EntityRestResponse>
			</EntityRestAccess>
		</EntityAccessConfig>
	</Entity>
</LeapDataServices>
<route>
    <from uri="direct:processing"></from>
    <choice>
    <when>
        <!-- When a transformation is required, convert to xml, transform, push newly transformed data into LDC as #enriched_leap_initial -->
        <simple>${in.header.required} == true</simple>
        <to uri="bean:leapConfigUtil?method=jsonToXMLFromExchange" />
        <to uri="bean:leapConfigUtil?method=transformDataFromLdcGetTagAndPushToLDC(${in.header.transFileName},'#enriched_leap_initial','#enriched_leap_initial')" />
        <to uri="bean:bean?method=xmlToJson('#enriched_leap_initial')"></to>
    </when>
    </choice>
</route>

When using an Entity, you must still call the transformation directly from your route, however, when the Entity configuration is loaded into memory, the filename for the required transformation will be set in the headers as transFileName. Additionally, the entity will also set the required header to indicate if a transformation is necessary.

Conventions and Best Practices

Field Names

It is recommended that you use a taxonomy to change the names of the fields prior to transformation or after transformation. If you do not use a taxonomy, the transformation file needs to get updated in the case of syntax changes from the source or destination of data. By reducing edits to the transformation file, we’re reducing the risk of error.

Additionally, a taxonomy can be applied based on vendor, service, or a specific inbound header value. If you use a taxonomy, it’s more likely that the transformation can be re-used.

By default, place taxonomy files in the feature-specific configuration directory. LDC will only check /resources in the classpath after first looking in /config/<featurePath>.

Updated on March 30, 2022

Was this article helpful?

Related Articles