Kong API response merger

Build Status

Synopsis

This plugin transforms the response body from upstream by adding additional fields or changing the existing one.

Configuration for Kubernetes

Full config for kubernetes kong plugin

apiVersion: configuration.konghq.com/v1
config:
  upstream:
    uri: http://upstream
    host_header: upstream
  paths:
    - path: /v1/data/[^/]+
      resources_to_extend:
        - data_paths:
            - path: "$.service-a"
              id_path: "$.service-a.id"
          api:
            url: "http://service-a/api/v1/data/"
            data_path: "$"
            id_key: email
        - data_paths:
            - id_path: "$.service-b.id"
              path: "$.service-b"
            - id_path: "$.service-b_2.id"
              path: "$.service-b_2"
          api:
            id_key: id
            url: "http://service-b/v1/data/"
            data_path: "$"
    - path: /v1/data
      upstream_data_path: "$.content"
      resources_to_extend:
        - data_paths:
            - path: "$.service-a"
              id_path: "$.service-a.id"
          api:
            url: "http://service-a/api/v1/data"
            data_path: "$.content"
            query_param_name: ids
            id_key: id
        - data_paths:
            - id_path: "$.service-b.id"
              path: "$.service-b"
          api:
            url: "http://service-b/api/v1/data"
            data_path: "$.content"
            query_param_name: ids
            id_key: id
    - path: /v1/data/(?<id>.*)/print
      methods:
        - GET
      upstream:
        uri: http://printing
        host_header: printing
        path: /data
        method: POST
      request:
        extend_with_auth_token: true
        overwrite_body: |-
            {
              "object": {
                "id": "${id}",
                "user": "${auth_token.ext.user.id}"
              }
            }
        resources_to_extend:
          - data_paths:
              - path: "$.object"
                id_path: "$.object.id"
            api:
                id_key: id
                url: "http://service-b/v1/data/"
                data_path: "$"
          - data_paths:
              - path: "$.configuration"
            add_missing: true,
            api:
              url: "http://service-a/v1/data/"
        
kind: KongPlugin
metadata:
  name: api-response-merger-upstream
  namespace: default
plugin: api-response-merger

Use case

This paragraph will show how usage of this plugins looks like. We assume that there are 3 REST APIs which can be querd using below example

We have upstream service that returns response with only ids of resources from service-a and service-b

$ curl -X GET http://upstream/v1/data/upstream-1
{
    "id": "uptream-1",
    "service-a": {
        "id": "a"
    },
    "service-b": {
        "id": "b"
    },
    "upstream-data": {
        "foo": "bar"
    }
}

Response from service-a

$ curl -X GET http://service-a/api/v1/data/data/a
{
    "id": "a",
    "data": {
        "text": "data from service a"
    }
}

Response from service-b

$ curl -X GET http://service-b/api/v1/data/b
{
    "id": "b",
    "data": {
        "text": "data from service b",
        "foo-b": "bar"
    }
}

This plugin will allow to change upstream response by extending it respone of data from service-a and service-b. See example below

$ curl -X GET http://kong/v1/data/upstream-1
{
    "id": "upstream-1",
    "service-a": {
        "id": "a",
        "data": {
            "text": "data from service a"
        }
    },
    "service-b": {
        "id": "b",
        "data": {
            "text": "data from service b",
            "foo-b": "bar"
        }
    },
    "upstream-data": {
        "foo": "bar"
    }
}

Configuration

Enabling the plugin on a Service

Configure this plugin on a Service by making the following request:

$ curl -X POST http://kong:8001/services/{service}/plugins \
    --data "name=api-response-merger"

service: the id or name of the Service that this plugin configuration will target.

Enabling the plugin on a Route

Configure this plugin on a Route with:

$ curl -X POST http://kong:8001/routes/{route_id}/plugins \
    --data "name=api-response-merger"

route_id: the id of the Route that this plugin configuration will target.

Enabling the plugin on a Consumer

You can use the http://localhost:8001/plugins endpoint to enable this plugin on specific Consumers:

$ curl -X POST http://kong:8001/plugins \
    --data "name=api-response-merger" \
    --data "consumer_id={consumer_id}"

Where consumer_id is the id of the Consumer we want to associate with this plugin.

You can combine consumer_id and service_id in the same request, to furthermore narrow the scope of the plugin.

form parameter default description
name The name of the plugin to use, in this case request-transformer
service_id The id of the Service which this plugin will target.
route_id The id of the Route which this plugin will target.
enabled true Whether this plugin will be applied.
consumer_id The id of the Consumer which this plugin will target.
config.upstream.uri Base upstream uri
config.upstream.host_header Host header which should be passed to upstream
config.upstream.path Overwrites path to upstream
config.upstream.path_prefix `` Prefix for path to upstream
config.upstream.method Method used for upstream call (by default method from the request)
config.paths List of paths on which plugin should merge response
config.paths[0].path Regular expression for path
config.paths[0].upstream Upstream configuration which overrides base upstream config (see config.upstream)
config.paths[0].upstream_data_path $ JSON path for data to transform
config.paths[0].resources_to_extend List of resources to change/expand
config.paths[0].resources_to_extend[0].data_paths List of JSON paths to change - at least one required
config.paths[0].resources_to_extend[0].data_paths[0].path JSON path for key where to put response of resource upstream - path is required
config.paths[0].resources_to_extend[0].data_paths[0].id_path JSON path for id of resource in upstream response
config.paths[0].resources_to_extend[0].api Object with config for resource upstream
config.paths[0].resources_to_extend[0].api.url Adress for api from given resource can be retrieved, url can be interpolated with values from the body (response or request - currently transformed, e.g. for { "object": {"id": "id_value" } } the following expression can be used ${object.id} e.g. url = http://resource-service/resources/${object.id}/other-resources)
config.paths[0].resources_to_extend[0].api.data_path $ JSON path for resource data
config.paths[0].resources_to_extend[0].api.query_param_name nil Query string parameter name when accessing multiple resources
config.paths[0].resources_to_extend[0].allow_missing false Flag indicating if merger should returns error when resource is missing. Default false
config.paths[0].resources_to_extend[0].add_missing false Flag indicating if merger should try adding the whole upstream body under the path if the id is missing
config.paths[0].request.overwrite_body nil New request body used for the upstream. It is interpolated with the captures for the path mapping (named variables are supported, see https://github.com/openresty/lua-nginx-module#ngxrematch)
config.paths[0].request.resources_to_extend nil List of resources to be changed/expanded (see details above)
config.paths[0].request.extend_with_auth_token false Whether the auth token should be used for interpolation of request body - if true, it can be accessed by "auth_token" param, e.g. { "object": {"user_id": "${auth_token.ent.user.id}" } }