Skip to content

codemonger-io/mapping-template-compose

Repository files navigation

English / 日本語

Mapping Template Compose

Makes Amazon API Gateway's mapping template composable.

This library is especially powerful if you combine it with AWS Cloud Development Kit.

Installing this library

npm install https://github.com/codemonger-io/mapping-template-compose.git#v0.1.1

Motivation

Have you ever felt that describing mapping templates for Amazon API Gateway is cumbersome? I got tired of writing mapping templates because:

  1. I had to write a lot of boilderplate code; e.g., extracting a query parameter as a property:

    "username": "$util.escapeJavaScript($util.urlDecode($input.params("username"))).replaceAll("\\'", "'")"
  2. It was difficult to reuse components of mapping templates

Regarding the second issue, the major difficulty is in making sure a JSON representation resulting from a mapping template valid.

Suppose you have the following property definitions as components:

const username = `"username": "$util.escapeJavaScript($util.urlDecode($input.params("username"))).replaceAll("\\'", "'")"`;
const signature = `"signature": $input.json("$.signature")`;
const date = `"date": "$util.escapeJavaScript($util.urlDecode($input.params("date"))).replaceAll("\\'", "'")"`;

If we want to construct a simple object that has all the above properties:

{
  "username": "$util.escapeJavaScript($util.urlDecode($input.params("username"))).replaceAll("\\'", "'")",
  "signature": $input.json("$.signature"),
  "date": "$util.escapeJavaScript($util.urlDecode($input.params("date"))).replaceAll("\\'", "'")"
}

You have to omit a trailing comma after the last property date, though, this is rather straightforward:

`{
  ${[username, signature, date].join(',\n  ')}
}`

What if all the properties may be omitted? You might come up with the following code:

`{
#if ($input.params('username') != "")
  ${username},
#end
#if ($input.json('$.signature') != "")
  ${signature},
#end
#if ($input.params('date') != "")
  ${date}
#end
}`

Unfortunately, the above template does not always produce a valid JSON object. It will end up with a dangling comma if date is omitted.

{
  "username": "<username>",
  "signature": "<signature>",
}

We have do something like below:

`{
#if ($input.params('username') != "")
  ${username}
#end
#if ($input.json('$.signature') != "")
#if ($input.params('username') != "")
  ,
#end
  ${signature}
#end
#if ($input.params('date') != "")
#if (($input.params('username') != "") || ($input.json('$.signature') != ""))
  ,
#end
  ${date}
#end
}`

It is daunting, isn't it? This library is intended to relieve the pain of writing mapping templates like the above.

Example

You can rewrite the example in the previous section with this library into:

import { composeMappingTemplate, ifThen } from 'mapping-template-compose';

composeMappingTemplate([
  ifThen(
    '$input.params("username") != ""',
    [['username', `"$util.escapeJavaScript($util.urlDecode($input.params("username"))).replaceAll("\\'", "'")"`]],
  ),
  ifThen(
    '$input.json("signature") != ""',
    [['signature', '$input.json("$.signature")']],
  ),
  ifThen(
    '$input.params("date") != ""',
    [['date', `"$util.escapeJavaScript($util.urlDecode($input.params("date"))).replaceAll("\\'", "'")"`]],
  ),
]);

You can make it further modular:

import { type KeyValue, composeMappingTemplate, ifThen } from 'mapping-template-compose';

const username: KeyValue = ['username', `"$util.escapeJavaScript($util.urlDecode($input.params("username"))).replaceAll("\\'", "'")"`];
const signature: KeyValue = ['signature', '$input.json("$.signature")'];
const date: KeyValue = ['date', `"$util.escapeJavaScript($util.urlDecode($input.params("date"))).replaceAll("\\'", "'")"`];
const optionalUsername = ifThen('$input.params("username") != ""', [username]);
const optionalSignature = ifThen('$input.json("signature") != ""', [signature]);
const optionalDate = ifThen('$input.params("date") != ""', [date]);

composeMappingTemplate([
  optionalUsername,
  optionalSignature,
  optionalDate,
]);

What mapping template can we compose?

What this library specifically does is only one thing: to determine where to place a comma (","). However, this seemingly trivial task turns out complicated as you can see in the example in Section "Motivation". Thus, not all of the possible patterns in mapping templates are supported. For instance, looping is not supported yet. Please refer to supported-patterns.md for what mapping template you can compose with this library.

API documentation

You can find the API documentation in ./api-docs/markdown folder.

Development

Resolving dependencies

npm ci

Building

npm run build

Testing

npm test

Generating the API documentation

npm run build:doc