Transformations

One of the core features of any API Gateway is the ability to transform the traffic that it manages. To really enable the decoupling of your services, the API Gateway should be able to mutate requests before forwarding them to your upstream services and do the same with the resulting responses before they reach the downstream clients. Gloo Edge delivers on this promise by providing you with a powerful transformation API.

Defining a transformation

Transformations are defined by adding the transformations attribute to your Virtual Services. You can define this attribute on three different Virtual Service sub-resources:

The configuration format is the same in all three cases and must be specified under the relevant options attribute. For example, to configure transformations for all traffic matching a Virtual Host, you need to add the following attribute to your Virtual Host definition:

# This snippet has been abridged for brevity
virtualHost:
  options:
    stagedTransformations:
      regular:
        requestTransforms:
        - requestTransformation:
            transformationTemplate:
              headers:
                foo:
                  text: 'bar'

Inheritance rules

By default, a transformation defined on a Virtual Service attribute is inherited by all the child attributes:

If a child attribute defines its own transformation, it will override the configuration on its parent. However, if inheritTransformation is set to true on the stagedTransformations for a Route, it can inherit transformations from its parent as illustrated below.

Let’s define the virtualHost and it’s child route is defined as follows:

# This snippet has been abridged for brevity, and only includes transformation-relevant config
virtualHost:
  options:
    stagedTransformations:
      regular:
        requestTransforms:
        - matchers:
            - prefix: '/parent'
          requestTransformation:
            transformationTemplate:
              headers:
                foo:
                  text: 'bar'
  routes:
    - options:
        stagedTransformations:
          inheritTransformation: true
          regular:
            requestTransforms:
            - matchers:
              - prefix: '/child'
              requestTransformation:
                transformationTemplate:
                  headers:
                    foo:
                      text: 'baz'

Because inheritTransformation is set to true on the child route, the parent virtualHost transformation config will be merged into the child. The child route’s transformations will look like:

# This snippet has been abridged for brevity, and only includes transformation-relevant config
routes:
- options:
  stagedTransformations:
    inheritTransformation: true
    regular:
      requestTransforms:
      - matchers:
        - prefix: '/child'
        requestTransformation:
          transformationTemplate:
            headers:
              foo:
                text: 'baz'
      - matchers:
        - prefix: '/parent'
        requestTransformation:
          transformationTemplate:
            headers:
              foo:
                text: 'bar'

As stated above, the route’s configuration will override its parent’s, but now it also inherits the parent’s transformations. So in this case, routes matching /parent will also be transformed. If inheritTransformation was set to false, this would not be the case. Note that only the first matched transformation will run, so if both the child and the parent had the same matchers, the child’s transformation would run.

Configuration format

In this section we will detail all the properties of the stagedTransformations object , which has the following structure:

stagedTransformations:
  early:
    # early transformations
  regular: 
    requestTransforms:
      - matcher:
          prefix : '/'
        clearRouteCache: bool
        requestTransformation: {}
        responseTransformation: {}
    responseTransforms: {}
  inheritTransformations: bool

The early and regular attributes are used to specify when in the envoy filter chain the transformations run. The following diagram illustrates the stages at which each of the transformation filters run in relation to other envoy filters.

Transformation Filter Stages

The inheritTransformation attribute allows child routes to inherit transformations from their parent RouteTables and/or VirtualHosts. This is detailed in the Inheritance rules section.

The requestTransforms attribute specifies a list of transformations which will be evaluated based on the request attributes. The first transformation which has a matcher that matches the request attributes will run.

The responseTransforms attribute specifies a list of transformations which will be evaluated based on the response attributes. A transformation will only be chosen from this list if no transformation in requestTransform matched the request.

The clearRouteCache attribute is a boolean value that determines whether the route cache should be cleared if the request transformation was applied. If the transformation modifies the headers in a way that affects routing, this attribute must be set to true. The default value is false.

The requestTransformation and responseTransformation attributes have the same format and specify transformations that will be applied to requests and responses respectively. The format can take one of two forms:

Transformation templates

Templates are the core of Gloo Edge’s transformation API. They allow you to mutate the headers and bodies of requests and responses based on the properties of the headers and bodies themselves. The following snippet illustrates the structure of the transformationTemplate object:

transformationTemplate:
  parseBodyBehavior: {}
  ignoreErrorOnParse: bool
  extractors:  {}
  headers: {}
  # Only one of body, passthrough, and mergeExtractorsToBody can be specified
  body: {} 
  passthrough: {}
  mergeExtractorsToBody: {}
  dynamicMetadataValues: []
  advancedTemplates: bool

The body, passthrough, and mergeExtractorsToBody attributes define three different ways of handling the body of the request/response. Please note that only one of them may be set, otherwise Gloo Edge will reject the transformationTemplate.

Let’s go ahead and describe each one of these attributes in detail.

parseBodyBehavior

This attribute determines how the request/response body will be parsed and can have one of two values:

The important part to know about the DontParse setting is that the body will be buffered and available, but will not be parsed. If you’re looking to skip any body buffering completely, see the section on passthrough: {}

As we will see later, some of the templating features won’t be available when treating the body as plain text.

ignoreErrorOnParse

By default, Gloo Edge will attempt to parse the body as JSON, unless you have DontParse set as the parseBodyBehavior. If ignoreErrorOnParse is set to true, Envoy will not throw an exception in case the body parsing fails. Defaults to false.

Implicit in this setting is that the body will be buffered and available. If you’re looking to skip any body buffering completely, see the section on passthrough: {}

extractors

Use this attribute to extract information from a request or response. It consists of a set of mappings from a string to an extraction:

An extraction must have one of two sources:

The body extraction source has been introduced with Gloo Edge, release 0.20.12, and Gloo Edge Enterprise, release 0.20.7. If you are using an earlier version, it will not work.

Extracting the body is generally not useful when Gloo Edge has already parsed it as JSON, the default behavior. The parsed body data can be directly referenced using standard JSON syntax. The body extractor treats the body as plaintext, and is interpreted using a regular expression as noted below. This can be useful for body data that cannot be parsed as JSON.

An extraction must also define which information is to be extracted from the source. This can be done by providing a regular expression via the regex attribute. The regular expression will be applied to the body or to the value of relevant header. If your regular expression uses capturing groups, you can select the group match you want to use via the subgroup attribute.

As an example, to define an extraction named foo which will contain the value of the foo query parameter you can apply the following configuration:

extractors:
  # This is the name of the extraction
  foo:
    # The :path pseudo-header contains the URI
    header: ':path'
    # Use a nested capturing group to extract the query param
    regex: '(.*foo=([^&]*).*)'
    # Select the second group match
    subgroup: 2

Extracted values can be used in two ways:

headers

Use this attribute to apply templates to request/response headers. It consists of a map where each key determines the name of the resulting header, while the corresponding value is a template which will determine the value.

For example, to set the header foo to the value of the header bar, you could use the following configuration:

transformationTemplate:
  headers:
    foo:
      text: '{{ header("bar") }}'

You could also use the parsed data from the body and add information to the header. For instance, given a request body:

{
  "Name": "Gloo",
  "Favorites": {
    "Color": "Blue"
  }
}

You could reference the value stored in Color and place it into the headers like so:

transformationTemplate:
  headers:
    color:
      text: '{{ Favorites.Color }}'

See the template language section for more details about template strings.

body

Use this attribute to apply templates to the request/response body. It consists of a template string that will determine the content of the resulting body.

As an example, the following configuration snippet could be used to transform a response body only if the HTTP response code is 404 and preserve the original response body in other cases.

transformationTemplate:
  # [...]
  body: 
    text: '{% if header(":status") == "404" %}{ "error": "Not found!" }{% else %}{{ body() }}{% endif %}'
  # [...]

See the template language section for more details about template strings.

passthrough

In some cases your do not need to transform the request/response body nor extract information from it. In these cases, particularly if the payload is large, you should use the passthrough attribute to instruct Gloo Edge to ignore the body (i.e. to not buffer it). The attribute always takes just the empty value:

transformationTemplate:
  # [...]
  passthrough: {}
  # [...]

If you’re looking to parse the body, and either ignore errors on parsing, or just disable JSON parsing, see those sections in this document, respectively.

mergeExtractorsToBody

Use this type of body transformation to merge all the extractions defined in the transformationTemplate to the body. The values of the extractions will be merged to a location in the body JSON determined by their names. You can use separators in the extractor names to nest elements inside the body.

For an example, see the following configuration:

transformationTemplate:
  mergeExtractorsToBody: {}
  extractors:
  path:
    header: ':path'
    regex: '.*'
  # The name of this attribute determines where the value will be nested in the body
  host.name:
    header: 'host'
    regex: '.*'

This will cause the resulting body to include the following extra attributes (in additional to the original ones):

{
  "path": "/the/request/path",
  "host": {
    "name": "value of the 'host' header"
  }
}
dynamicMetadataValues

This attribute can be used to define an Envoy Dynamic Metadata entry. This metadata can be used by other filters in the filter chain to implement custom behavior.

As an example, the following configuration creates a dynamic metadata entry in the com.example namespace with key foo and value equal to that of the foo header .

dynamicMetadataValues:
- metadataNamespace: "com.example"
  key: 'foo'
  value:
    text: '{{ header("foo") }}'

The metadataNamespace is optional. It defaults to the namespace of the Gloo Edge transformation filter name, i.e. io.solo.transformation.

A common use case for this attribute is to define custom data to be included in your access logs. See the dedicated tutorial for an example of how this can be achieved.

advancedTemplates

This attribute determines which notation to use when accessing elements in JSON structures. If set to true, Gloo Edge will expect JSON pointer notation (e.g. “time/start”) instead of dot notation (e.g. “time.start”). Defaults to false.

Please note that, if set to true, you will need to use the extraction function to access extractors in template strings (e.g. {{ extraction("myExtractor") }}); if the default value of false is used, extractors will simply be available by their name (e.g. {{ myExtractor }}).

Templating language

Templates can be used only if the request/response payload is a JSON string.

Gloo Edge templates are powered by the Inja template engine, which is inspired by the popular Jinja templating language in Python. When writing your templates, you can take advantage of all the core Inja features, i.a. loops, conditional logic, and functions.

In addition to the standard functions available in the core Inja library, you can use additional custom functions that we have added:

You can use templates to mutate headers, the body, and dynamic metadata.

XSLT Transformation

The XSLT transformation has been introduced with Gloo Edge Enterprise, release v1.8.0-beta3. If you are using an earlier version, it will not work.

XSLT transformations allow transformations on HTTP requests using the XSLT transformation language. The following snippet illustrates the structure of the xsltTransformation object.

xsltTransformation:
  xslt: string
  setContentType: string
  nonXmlTransform: bool
xslt

The XSLT transformation is specified in this field as a string. Like other transformations, an invalid XSLT transformation will not be accepted and envoy validation will reject the transformation configuration.

setContentType

XSLT transformations can be used to transform HTTP body between content type. For example, from XML to JSON, or JSON to XML. In the case of these transformations, the content-type HTTP header is set to the value of setContentType. If left empty, the content-type header is unchanged.

nonXmlTransform

XSLT transformations typically accept only XML as input. If the input to the transformation is not XML, this should be set to true. For example, if the XSLT transformation is transforming a JSON input to XML, this would be set to true. By default, this is false and the XSLT transformation will only accept XML input.

Common use cases

On this page we have seen all the properties of the Gloo Edge Transformation API as well as some simple example snippets. If are looking for complete examples, please check out the following tutorials, which will guide you through some of the most common transformation use cases.