├── .gitignore ├── .npmignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── example.js ├── index.js ├── misc ├── endpoint.js ├── examples.js ├── gen.js ├── post_examples.js └── result.js ├── package.json └── test ├── _.js ├── _.md ├── input.js ├── renderer.js └── util.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | npm-debug.log 3 | package-lock.json 4 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | /examples 2 | /misc 3 | /web 4 | /node_modules 5 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | 2 | ## [0.0.8](https://github.com/ToQoz/api-gateway-mapping-template/compare/v0.0.7...v0.0.8) (2020-04-29) 3 | 4 | 5 | ### Bug Fixes 6 | 7 | * print null objects as empty string #4 8 | 9 | 10 | 11 | 12 | ## [0.0.7](https://github.com/ToQoz/api-gateway-mapping-template/compare/v0.0.6...v0.0.7) (2015-12-19) 13 | 14 | 15 | ### Bug Fixes 16 | 17 | * **package:** don't escape HTML ([f3c5d99](https://github.com/ToQoz/api-gateway-mapping-template/commit/f3c5d99)) 18 | * **readme:** update ([6fab78e](https://github.com/ToQoz/api-gateway-mapping-template/commit/6fab78e)) 19 | 20 | 21 | 22 | 23 | ## [0.0.6](https://github.com/ToQoz/api-gateway-mapping-template/compare/v0.0.5...v0.0.6) (2015-12-18) 24 | 25 | 26 | ### Bug Fixes 27 | 28 | * **package:** In $input.json, if payload is empty, treat it as empty object ([0bcb8e2](https://github.com/ToQoz/api-gateway-mapping-template/commit/0bcb8e2)) 29 | * **package:** In $input.path, if payload is empty, treat it as empty object ([47cbd78](https://github.com/ToQoz/api-gateway-mapping-template/commit/47cbd78)) 30 | 31 | 32 | 33 | 34 | ## [0.0.5](https://github.com/ToQoz/api-gateway-mapping-template/compare/v0.0.4...v0.0.5) (2015-12-18) 35 | 36 | 37 | ### Bug Fixes 38 | 39 | * **package:** fix a bug in $input.json ([fde5289](https://github.com/ToQoz/api-gateway-mapping-template/commit/fde5289)) 40 | 41 | 42 | 43 | 44 | ## [0.0.4](https://github.com/ToQoz/api-gateway-mapping-template/compare/v0.0.3...v0.0.4) (2015-12-15) 45 | 46 | 47 | ### Bug Fixes 48 | 49 | * **example:** follow new(v.0.0.3) API ([404742b](https://github.com/ToQoz/api-gateway-mapping-template/commit/404742b)) 50 | 51 | 52 | 53 | 54 | ## [0.0.3](https://github.com/ToQoz/api-gateway-mapping-template/compare/v0.0.2...v0.0.3) (2015-12-15) 55 | 56 | 57 | ### Bug Fixes 58 | 59 | * **test:** wait(d).fn -> wait(d).then(fn) ([9652ecb](https://github.com/ToQoz/api-gateway-mapping-template/commit/9652ecb)) 60 | 61 | ### Features 62 | 63 | * **package:** change API signature of module.exports ([06b320f](https://github.com/ToQoz/api-gateway-mapping-template/commit/06b320f)) 64 | 65 | 66 | ### BREAKING CHANGE 67 | 68 | - module.exports takes `parameters` instead of `template`, `payload`, `params`, `context` 69 | 70 | 71 | 72 | 73 | ## [0.0.2](https://github.com/ToQoz/api-gateway-mapping-template/compare/v0.0.1...v0.0.2) (2015-12-14) 74 | 75 | 76 | ### Bug Fixes 77 | 78 | * **package:** improve workaround for obj.toString ([a45557f](https://github.com/ToQoz/api-gateway-mapping-template/commit/a45557f)) 79 | 80 | 81 | 82 | 83 | ## 0.0.1 (2015-12-13) 84 | 85 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Takatoshi Matsumoto 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # api-gateway-mapping-template 2 | 3 | make AWS API Gateway's Mapping Template testable 4 | 5 | ![Image](http://toqoz.net/art/images/api-gateway-mapping-template.png) 6 | 7 | ## Installation 8 | 9 | ``` 10 | npm install api-gateway-mapping-template 11 | ``` 12 | 13 | ## Usage 14 | 15 | simple.js: 16 | 17 | ```node 18 | var mappingTemplate = require('api-gateway-mapping-template') 19 | 20 | var vtl = '$input.json(\'$.data\')'; 21 | var payload = '{"data": {"url": "https://github.com/ToQoz/api-gateway-mapping-template"}}'; 22 | 23 | var result = mappingTemplate({template: vtl, payload: payload}) 24 | console.dir(result); 25 | ``` 26 | 27 | *** 28 | 29 | ``` 30 | $ node ./simple.js 31 | '{"url":"https://github.com/ToQoz/api-gateway-mapping-template"}' 32 | ``` 33 | 34 | ## Examples 35 | 36 | - [ToQoz/api-gateway-localdev](https://github.com/ToQoz/api-gateway-localdev) - simulate API Gateway + Lambda in your local. 37 | - [online checker](https://github.com/ToQoz/mapping-template-checker.toqoz.net) - http://mapping-template-checker.toqoz.net 38 | 39 | ## API 40 | 41 | ```node 42 | var mappingTemplate = require('api-gateway-mapping-template') 43 | ``` 44 | 45 | ### mappingTemplate(parameters) 46 | 47 | This function renders AWS API Gateway's Mapping Template by using given payload, params and context. 48 | 49 | - Arguments 50 | - parameters - **required** - `map` 51 | - template - **required** - `String|Buffer` 52 | - payload - **required** - `String|Buffer` 53 | - params - `map` 54 | - path - `map` 55 | - querystring - `map` 56 | - header - `map` 57 | - context - `map` 58 | - indentity - `map` 59 | - cognitoAuthenticationType - `String` 60 | - cognitoIdentityId - `String` 61 | - cognitoIdentityPoolId - `String` 62 | - sourceIp - `String` 63 | - user - `String` 64 | - userAgent - `String` 65 | - userArn - `String` 66 | - requestId - `String` 67 | - resourceId - `String` 68 | - resourcePath - `String` 69 | - stage - `String` 70 | - Return value 71 | - rendered template - `String` 72 | -------------------------------------------------------------------------------- /example.js: -------------------------------------------------------------------------------- 1 | var mappingTemplate = require('api-gateway-mapping-template'); 2 | 3 | var vtl = '$input.json(\'$.data\')'; 4 | var payload = '{"data": {"url": "https://github.com/ToQoz/api-gateway-mapping-template"}}'; 5 | 6 | var result = mappingTemplate({template: vtl, payload: payload}); 7 | console.dir(result); 8 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var clone = require('clone'); 2 | var Velocity = require('velocityjs'); 3 | var jsonpath = workaroundJsonPath(require('JSONPath')); 4 | 5 | module.exports = function(parameters) { 6 | parameters = clone(parameters || {}); 7 | 8 | var template = parameters.template; 9 | var payload = parameters.payload; 10 | 11 | var params = clone(parameters.params || {}); 12 | params.path = params.path || {}; 13 | params.querystring = params.querystring || {}; 14 | params.header = params.header || {}; 15 | 16 | var context = clone(parameters.context || {}); 17 | context.identity = context.identity || {}; 18 | 19 | // API Gateway Mapping Template Reference 20 | // http://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-mapping-template-reference.html 21 | var data = { 22 | context: context, 23 | input: { 24 | _payload: payload.toString(), 25 | path: function(path) { 26 | var obj; 27 | if (this._payload === '') { 28 | // if payload is empty, treat it as empty object 29 | // https://github.com/ToQoz/api-gateway-mapping-template/blob/master/test/_.md#example-91575d0e 30 | obj = {}; 31 | } else if (/^\s*(?:{|\[|")/.test(this._payload)) { 32 | // if payload starts with `{` or `[` or `"`, treat as JSON 33 | obj = JSON.parse(this._payload); 34 | } else { 35 | // treat as string 36 | obj = this._payload; 37 | } 38 | 39 | return jsonpath(obj, path); 40 | }, 41 | json: function(path) { 42 | var obj; 43 | if (this._payload === '') { 44 | // if payload is empty, treat it as empty object 45 | // https://github.com/ToQoz/api-gateway-mapping-template/blob/master/test/_.md#example-098fd028 46 | obj = {}; 47 | } else if (/^\s*(?:{|\[|")/.test(this._payload)) { 48 | // if payload starts with `{` or `[` or `"`, treat as JSON 49 | obj = JSON.parse(this._payload); 50 | } else { 51 | // treat as string 52 | obj = this._payload; 53 | } 54 | 55 | return JSON.stringify(jsonpath(obj, path)); 56 | }, 57 | params: function(x) { 58 | switch (true) { 59 | case x === undefined: 60 | return params; 61 | case x in params.path: 62 | return params.path[x]; 63 | case x in params.querystring: 64 | return params.querystring[x]; 65 | case x in params.header: 66 | return params.header[x]; 67 | } 68 | }, 69 | }, 70 | util: { 71 | escapeJavaScript: escapeJavaScript, 72 | urlEncode: encodeURIComponent, 73 | urlDecode: decodeURIComponent, 74 | base64Encode: base64Encode, 75 | base64Decode: base64Decode, 76 | } 77 | }; 78 | 79 | data = workaroundAwsObjectSerialization(data); 80 | 81 | var ast = Velocity.parse(template.toString()); 82 | var config = { 83 | escape: false, // don't escape HTML 84 | }; 85 | return (new Velocity.Compile(ast, config)).render(data, null, true); 86 | }; 87 | 88 | // Workaround to followings 89 | // When the tempalte is "$input.params" (is a Function) 90 | // - AWS API Gateway returns "{}". 91 | // - This is not org.apache.velocity's behaviour. It returns "$input.params". 92 | // When the tempalte is "$input" or "$util" 93 | // - AWS API Gateway returns "{}" 94 | // - This is not org.apache.velocity's behaviour. It returns serialized hash. 95 | function workaroundAwsObjectSerialization(data) { 96 | var returnEmptyObject = function() { return "{}"; }; 97 | 98 | // This never be called because keySet/entrySet/size will be processed by velocity.js. 99 | // But I want to handling only toString 100 | var builtinMethod = function() { throw "unexpected error"; }; 101 | builtinMethod.toString = returnEmptyObject; 102 | 103 | data.input.toString = returnEmptyObject; 104 | data.util.toString = returnEmptyObject; 105 | walk(data, function(obj) { 106 | if (typeof(obj) == 'function') { 107 | obj.toString = returnEmptyObject; 108 | } 109 | 110 | obj.keySet = builtinMethod; 111 | obj.entrySet = builtinMethod; 112 | obj.size = builtinMethod; 113 | }); 114 | 115 | return data; 116 | } 117 | 118 | function workaroundJsonPath(jsonpath) { 119 | return function(obj, path) { 120 | if (path === '$') { 121 | return obj; 122 | } 123 | 124 | var result = jsonpath({ 125 | json: obj, 126 | path: path 127 | }); 128 | 129 | if (result.length === 1) { 130 | return result[0]; 131 | } else { 132 | return result; 133 | } 134 | }; 135 | } 136 | 137 | function walk(obj, cb) { 138 | cb(obj); 139 | 140 | if (Array.isArray(obj)) { 141 | obj.forEach(function(c) { 142 | walk(c, cb); 143 | }); 144 | } else if ({}.toString.call(obj) === '[object Object]') { 145 | Object.keys(obj).forEach(function(k) { 146 | walk(obj[k], cb); 147 | }); 148 | } 149 | } 150 | 151 | function base64Encode(x) { 152 | return (new Buffer(x)).toString('base64'); 153 | } 154 | 155 | function base64Decode(x) { 156 | return (new Buffer(x, 'base64')).toString(); 157 | } 158 | 159 | // I recognize $util.escapeJavaScript as almost `escapeJSONString` and implemented so. 160 | // c.f. 24.3.2.2 Runtime Semantics: QuoteJSONString ( value ) 161 | // http://www.ecma-international.org/ecma-262/6.0/index.html#sec-quotejsonstring 162 | // DO: 2.a -> 2.b -> 2.c -> 2.d 163 | var escapeJavaScriptTable = { 164 | '"': '\"', // 2.a 165 | '\\': '\\\\', 166 | '\b': '\\b', // 2.b (skip abbrev) 167 | '\f': '\\f', 168 | '\n': '\\n', 169 | '\r': '\\r', 170 | '\t': '\\t', 171 | }; 172 | // 2.c 173 | for (var code = 0; code < 20; code++) { 174 | escapeJavaScriptTable[String.fromCharCode(code)] = ((code < 16) ? '\\u000' : '\\u00') + code.toString(16); 175 | } 176 | function escapeJavaScript(x) { 177 | return x.split("").map(function(c) { 178 | // 2.a - 2.c 179 | if (c in escapeJavaScriptTable) { 180 | return escapeJavaScriptTable[c]; 181 | } 182 | 183 | // 2.d 184 | return c; 185 | }).join(""); 186 | } 187 | -------------------------------------------------------------------------------- /misc/endpoint.js: -------------------------------------------------------------------------------- 1 | var AWS = require('aws-sdk'); 2 | var lambda = new AWS.Lambda(); 3 | var apiGateway = new AWS.APIGateway(); 4 | 5 | var Promise = require('bluebird'); 6 | Promise.promisifyAll(Object.getPrototypeOf(apiGateway)); 7 | 8 | module.exports = Endpoint; 9 | 10 | function Endpoint(params) { 11 | this.region = params.region; 12 | this.accountId = params.accountId; 13 | 14 | this.apiName = params.apiName; 15 | this.restApiId = null; 16 | this.resourceId = null; 17 | this.lambdaRole = params.lambdaRole; 18 | this.functionName = params.functionName; 19 | this.functionZip = params.functionZip; 20 | this.functionArn = null; 21 | this.httpMethod = "POST"; 22 | 23 | this._defer = []; 24 | 25 | this.promise = Promise.resolve(); 26 | } 27 | 28 | Endpoint.prototype.defer = function(cb) { 29 | this._defer = this._defer || []; 30 | this._defer.push(cb); 31 | }; 32 | 33 | Endpoint.prototype.cleanup = function() { 34 | console.log("cleanup..."); 35 | while(true) { 36 | if (!this._defer || this._defer <= 0) break; 37 | this._defer.pop()(); 38 | } 39 | }; 40 | 41 | Endpoint.prototype.then = function (onFulfilled, onRejected) { 42 | this.promise = this.promise.then(onFulfilled, onRejected); 43 | return this; 44 | }; 45 | 46 | Endpoint.prototype["catch"] = function (onRejected) { 47 | this.promise = this.promise.catch(onRejected); 48 | return this; 49 | }; 50 | 51 | Endpoint.prototype.createFunction = function() { 52 | var that = this; 53 | 54 | return this.then(function() { 55 | console.log("create function"); 56 | 57 | return new Promise(function(resolve, reject) { 58 | lambda.createFunction( 59 | { 60 | FunctionName: that.functionName, 61 | Runtime: "nodejs", 62 | Role: that.lambdaRole, 63 | Handler: 'index.handler', 64 | Timeout: 6, 65 | MemorySize: 128, 66 | Code: { 67 | ZipFile: that.functionZip.toBuffer() 68 | } 69 | }, 70 | function(err, data) { 71 | if (err) { 72 | reject(err); 73 | } else { 74 | resolve(data); 75 | } 76 | } 77 | ); 78 | }) 79 | .then(function(data) { 80 | that.functionArn = data.FunctionArn; 81 | that.defer(function() { 82 | console.log("delete lambda functionName=" + that.functionName); 83 | lambda.deleteFunction({FunctionName: that.functionName}, function(err) { if (err) console.log(err); }); 84 | }); 85 | }); 86 | }); 87 | }; 88 | 89 | Endpoint.prototype.createRestApi = function() { 90 | var that = this; 91 | 92 | return this.then(function() { 93 | console.log("create rest api"); 94 | return apiGateway.createRestApiAsync({name: that.apiName}) 95 | .then(function(data) { 96 | that.restApiId = data.id; 97 | that.endpoint_url = "https://" + that.restApiId + ".execute-api." + that.region + ".amazonaws.com/dev"; 98 | 99 | that.defer(function() { 100 | console.log("delete api restApiId=" + that.restApiId); 101 | apiGateway.deleteRestApiAsync({restApiId: that.restApiId}); 102 | }); 103 | 104 | console.log("get resources"); 105 | return apiGateway.getResourcesAsync({restApiId: that.restApiId}) 106 | .then(function(data) { 107 | var rootResourceId = data.items[0].id; 108 | that.resourceId = rootResourceId; 109 | }); 110 | }); 111 | }); 112 | }; 113 | 114 | Endpoint.prototype.putMethod = function() { 115 | var that = this; 116 | 117 | return this.then(function() { 118 | console.log("add put method"); 119 | return apiGateway.putMethodAsync({ 120 | restApiId: that.restApiId, 121 | resourceId: that.resourceId, 122 | httpMethod: that.httpMethod, 123 | authorizationType: 'None', 124 | apiKeyRequired: false, 125 | requestParameters: {} 126 | }); 127 | }); 128 | }; 129 | 130 | Endpoint.prototype.putIntegration = function(requestTemplates) { 131 | var that = this; 132 | 133 | return this.then(function() { 134 | console.log("add put integration"); 135 | 136 | return apiGateway.putIntegrationAsync({ 137 | restApiId: that.restApiId, 138 | resourceId: that.resourceId, 139 | httpMethod: that.httpMethod, 140 | integrationHttpMethod: "POST", 141 | type: "AWS", 142 | requestTemplates: requestTemplates, 143 | uri: "arn:aws:apigateway:" + that.region + ":lambda:path/2015-03-31/functions/" + that.functionArn + "/invocations" 144 | }); 145 | }); 146 | }; 147 | 148 | Endpoint.prototype.putMethodResponse = function(responseModels) { 149 | var that = this; 150 | 151 | return this.then(function() { 152 | console.log("add put integration response"); 153 | 154 | return Promise.all([200, 400].map(function(code) { 155 | return apiGateway.putMethodResponseAsync({ 156 | restApiId: that.restApiId, 157 | resourceId: that.resourceId, 158 | httpMethod: "POST", 159 | statusCode: code.toString(), 160 | responseModels: responseModels 161 | }); 162 | })); 163 | }); 164 | }; 165 | 166 | Endpoint.prototype.putIntegrationResponse = function(responseTemplates) { 167 | var that = this; 168 | 169 | return this.then(function() { 170 | console.log("add put integration response"); 171 | return apiGateway.putIntegrationResponseAsync({ 172 | restApiId: that.restApiId, 173 | resourceId: that.resourceId, 174 | httpMethod: "POST", 175 | statusCode: "200", 176 | responseTemplates: responseTemplates 177 | }); 178 | }); 179 | }; 180 | 181 | Endpoint.prototype.addPermissionInvokeFunction = function() { 182 | var that = this; 183 | 184 | return this.then(function() { 185 | console.log("add permission"); 186 | return new Promise(function(resolve, reject) { 187 | lambda.addPermission( 188 | { 189 | FunctionName: that.functionName, 190 | StatementId: "sid-agmt-" + (Date.now()), 191 | Action: "lambda:InvokeFunction", 192 | Principal: "apigateway.amazonaws.com", 193 | SourceArn: "arn:aws:execute-api:ap-northeast-1:" + that.accountId + ":" + that.restApiId + "/*/POST/", 194 | }, 195 | function(err, data) { 196 | if (err) { 197 | reject(err); 198 | } else { 199 | resolve(data); 200 | } 201 | } 202 | ); 203 | }); 204 | }); 205 | }; 206 | 207 | Endpoint.prototype.deploy = function() { 208 | var that = this; 209 | 210 | return this.then(function() { 211 | console.log("deploy endpoint"); 212 | return apiGateway.createDeploymentAsync({ 213 | restApiId: that.restApiId, 214 | stageName: "dev" 215 | }); 216 | }); 217 | }; 218 | -------------------------------------------------------------------------------- /misc/examples.js: -------------------------------------------------------------------------------- 1 | module.exports = [ 2 | { 3 | template: '"$input.path(\'$\')"', 4 | payload: "a=b", 5 | headers: {}, 6 | }, 7 | { 8 | template: '"$input.path(\'$\')"', 9 | payload: '"a=b"', 10 | headers: {}, 11 | }, 12 | { 13 | template: '$input.json(\'$\')', 14 | payload: "a=b", 15 | headers: {}, 16 | }, 17 | { 18 | template: '$input.json(\'$\')', 19 | payload: '"a=b"', 20 | headers: {}, 21 | }, 22 | { 23 | template: '"$input.path(\'$\')"', 24 | payload: "{}", 25 | headers: {}, 26 | }, 27 | { 28 | template: '"$input.path(\'$\')"', 29 | payload: '"{}"', 30 | headers: {}, 31 | }, 32 | { 33 | template: '$input.json(\'$\')', 34 | payload: "{}", 35 | headers: {}, 36 | }, 37 | { 38 | template: '$input.json(\'$\')', 39 | payload: "", 40 | headers: {}, 41 | }, 42 | { 43 | template: '$input.path(\'$\')', 44 | payload: "", 45 | headers: {}, 46 | }, 47 | { 48 | template: '{"name": "$input.path(\'$\')"}', 49 | payload: "name=toqoz", 50 | headers: {}, 51 | }, 52 | { 53 | template: '"$input.path(\'$\')"', 54 | payload: "{a", 55 | headers: {}, 56 | }, 57 | { 58 | template: '"$input.path(\'$\')"', 59 | payload: "a{b", 60 | headers: {}, 61 | }, 62 | { 63 | template: '"$input.path(\'$\')"', 64 | payload: "[a", 65 | headers: {}, 66 | }, 67 | { 68 | template: '"$input.path(\'$\')"', 69 | payload: "a[", 70 | headers: {}, 71 | }, 72 | { 73 | template: '"$input.path(\'$\')"', 74 | payload: "null{", 75 | headers: {}, 76 | }, 77 | { 78 | template: '"$input.path(\'$\')"', 79 | payload: "true{", 80 | headers: {}, 81 | }, 82 | { 83 | template: '"$input.path(\'$\')"', 84 | payload: "false{", 85 | headers: {}, 86 | }, 87 | { 88 | template: '"$input.path(\'$\')"', 89 | payload: "undefined{", 90 | headers: {}, 91 | }, 92 | { 93 | template: '$input', 94 | payload: "", 95 | headers: {}, 96 | }, 97 | { 98 | template: '$input.keySet', 99 | payload: "", 100 | headers: {}, 101 | }, 102 | { 103 | template: '$input.params.keySet', 104 | payload: "", 105 | headers: {}, 106 | }, 107 | { 108 | template: '$util', 109 | payload: "", 110 | headers: {}, 111 | }, 112 | { 113 | template: '$input.params', 114 | payload: "", 115 | headers: {}, 116 | }, 117 | { 118 | template: '$input.json', 119 | payload: "", 120 | headers: {}, 121 | }, 122 | { 123 | template: '$util.urlEncode', 124 | payload: "", 125 | headers: {}, 126 | }, 127 | ]; 128 | -------------------------------------------------------------------------------- /misc/gen.js: -------------------------------------------------------------------------------- 1 | var PRODUCT_NAME="api-gateway-mapping-template-integration-test"; 2 | 3 | if (!process.env.AWS_ACCOUNT_ID) { 4 | throw "process.env.AWS_ACCOUNT_ID is required"; 5 | } 6 | 7 | process.env.AWS_REGION = process.env.AWS_REGION || "ap-northeast-1"; 8 | process.env.AWS_PROFILE = process.env.AWS_PROFILE || PRODUCT_NAME; 9 | process.env.LAMBDA_ROLE = process.env.LAMBDA_ROLE || "arn:aws:iam::" + process.env.AWS_ACCOUNT_ID + ":role/" + PRODUCT_NAME; 10 | 11 | var Promise = require('bluebird'); 12 | var Zip = require('adm-zip'); 13 | 14 | var Endpoint = require('./endpoint'); 15 | var postExamples = require('./post_examples'); 16 | var writeResults = require('./result'); 17 | 18 | 19 | var zip = new Zip(); 20 | zip.addFile('index.js', new Buffer("exports.handler = function(event, context) { context.succeed(JSON.stringify(event)); };")); 21 | 22 | var examples = require('./examples'); 23 | var requestTemplates = examples.reduce(function(product, e, i) { product["text/test-" + i] = e.template; return product; }, {}); 24 | var responseModels = examples.reduce(function(product, e, i) { product["text/test-" + i] = "Empty"; return product; }, {}); 25 | var responseTemplates = examples.reduce(function(product, e, i) { product["text/test-" + i] = "$input.path('$')"; return product; }, {}); 26 | 27 | var endpoint = new Endpoint({ 28 | region: process.env.AWS_REGION, 29 | accountId: process.env.AWS_ACCOUNT_ID, 30 | apiName: PRODUCT_NAME, 31 | lambdaRole: process.env.LAMBDA_ROLE, 32 | functionName: PRODUCT_NAME + "-console-log", 33 | functionZip: zip 34 | }); 35 | 36 | endpoint 37 | .createFunction() 38 | .createRestApi() 39 | .putMethod() 40 | .putIntegration(requestTemplates) 41 | .putMethodResponse(responseModels) 42 | .putIntegrationResponse(responseTemplates) 43 | .addPermissionInvokeFunction() 44 | .deploy() 45 | .then(function() { 46 | console.log(endpoint.endpoint_url); 47 | return postExamples(endpoint.endpoint_url, examples); 48 | }) 49 | .then(writeResults) 50 | .then(function() { 51 | endpoint.cleanup(); 52 | }) 53 | .catch(function(err) { 54 | console.error(err); 55 | err.stack.split('\n').forEach(function(t) { console.error(t); }); 56 | endpoint.cleanup(); 57 | }); 58 | -------------------------------------------------------------------------------- /misc/post_examples.js: -------------------------------------------------------------------------------- 1 | var http = require('https'); 2 | var parseUrl = require('url').parse; 3 | var Promise = require('bluebird'); 4 | 5 | module.exports = function (url, examples) { 6 | var q = examples.map(function(ex, i) { 7 | var req = function() { return post(url, extend({}, ex.headers, {'Content-Type': "text/test-" + i}), ex.payload); }; 8 | return req() 9 | .then(function(res) { 10 | if (res.statusCode === 500) { 11 | return wait(1000)().then(req); 12 | } 13 | 14 | res.headers = ex.headers; 15 | res.template = ex.template; 16 | res.payload = ex.payload; 17 | return res; 18 | }); 19 | }); 20 | 21 | return Promise.all(q); 22 | }; 23 | 24 | function post(url, headers, data) { 25 | return new Promise(function(resolve, reject) { 26 | var u = parseUrl(url); 27 | 28 | var options = { 29 | hostname: u.hostname, 30 | port: 443, 31 | path: u.path, 32 | method: 'POST', 33 | headers: headers 34 | }; 35 | 36 | var req = http.request(options, function(res) { 37 | res.setEncoding('utf8'); 38 | 39 | var responseBody = ''; 40 | 41 | res.on('data', function (chunk) { 42 | responseBody += chunk; 43 | }); 44 | 45 | res.on('end', function() { 46 | resolve({ 47 | statusCode: res.statusCode, 48 | headers: res.headers, 49 | body: responseBody 50 | }); 51 | }); 52 | 53 | req.on('error', function(err) { 54 | reject(err); 55 | }); 56 | }); 57 | 58 | req.setTimeout(5 * 1000, function() { 59 | reject("timeout: POST " + url); 60 | }); 61 | 62 | req.write(data); 63 | req.end(); 64 | }); 65 | } 66 | 67 | function wait(delay) { 68 | return function() { 69 | return new Promise(function(resolve) { 70 | setTimeout(function() { resolve(); }, delay); 71 | }); 72 | }; 73 | } 74 | 75 | function extend(target) { 76 | var sources = [].slice.call(arguments, 1); 77 | sources.forEach(function (source) { 78 | for (var prop in source) { 79 | target[prop] = source[prop]; 80 | } 81 | }); 82 | return target; 83 | } 84 | -------------------------------------------------------------------------------- /misc/result.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'); 2 | var crypto = require('crypto'); 3 | 4 | var sprintf = require("sprintf-js").sprintf; 5 | var Promise = require('bluebird'); 6 | 7 | module.exports = function(results) { 8 | return new Promise(function(resolve, reject) { 9 | var testOut = process.env.TEST ? fs.createWriteStream(process.env.TEST) : process.stdout; 10 | var markdownOut = process.env.MD ? fs.createWriteStream(process.env.MD) : process.stdout; 11 | 12 | results = results.map(function(result) { 13 | var r = new Result(); 14 | r.mappingTemplate = result.template; 15 | r.requestBody = result.payload; 16 | r.requestHeaders = result.headers; 17 | r.statusCode = result.statusCode; 18 | r.responseBody = result.body; 19 | return r; 20 | }); 21 | 22 | // write markdown 23 | results.forEach(function(r, i) { 24 | markdownOut.write(sprintf("## example-%s\n", hash(JSON.stringify(r)))); 25 | r.toMarkdown(markdownOut); 26 | }); 27 | 28 | // write test 29 | testOut.write(sprintf("// This file is generated by `TEST=%s node misc/gen.js`\n", process.env.TEST)); 30 | testOut.write("\n"); 31 | testOut.write("var assert = require('assert')\n"); 32 | testOut.write("var mappingTemplate = require('../')\n"); 33 | testOut.write("\n"); 34 | testOut.write("describe('$input.path|$input.json', function() {\n"); 35 | 36 | var mdUrl = "https://github.com/ToQoz/api-gateway-mapping-template/blob/master/test/_.md"; 37 | results.forEach(function(r, i) { 38 | testOut.write(sprintf(" // %s#example-%s\n", mdUrl, hash(JSON.stringify(r)))); 39 | r.toTest(testOut, 1); 40 | }); 41 | 42 | testOut.write("});"); 43 | 44 | resolve(); 45 | }); 46 | }; 47 | 48 | function Result() { 49 | this.mappingTemplate = null; 50 | this.requestBody = null; 51 | this.requestHeaders = null; 52 | this.statusCode = null; 53 | this.responseBody = null; 54 | } 55 | 56 | Result.prototype.toMarkdown = function(out) { 57 | var s = ""; 58 | var header = Object.keys(this.requestHeaders).map(function(k) { return k + " : " + this.requestHeaders[k]; }.bind(this)).join("\n"); 59 | 60 | var h = ["Template", "Header", "Payload", "Status code", "Result"]; 61 | s += h.join("|") + "\n"; 62 | s += h.map(function(v) { return v.replace(/./g, "-"); }).join("|") + "\n"; 63 | 64 | s += [ 65 | "`" + this.mappingTemplate + "`", 66 | "`" + (header || "None") + "`", 67 | "`" + this.requestBody + "`", 68 | "`" + this.statusCode + "`", 69 | "`" + this.responseBody + "`", 70 | ].join("|"); 71 | 72 | out.write(s); 73 | out.write("\n\n"); 74 | }; 75 | 76 | Result.prototype.toTest = function(out, indentLevel) { 77 | var indent = ""; 78 | for (var i = 0; i < indentLevel; i++) { 79 | indent += " "; 80 | } 81 | 82 | if (this.statusCode !== 200 || this.responseBody.indexOf("{errorMessage=Unable") === 0) { 83 | out.write(sprintf(indent + "describe('H=`%s` P=`%s` ===> T=`%s`', function() {\n", JSON.stringify(this.requestHeaders).replace(/'/g, "\\'"), this.requestBody.replace(/'/g, "\\'"), this.mappingTemplate.replace(/'/g, "\\'"))); 84 | out.write(indent + " it('throw error', function() {\n"); 85 | out.write(sprintf(indent + " assert.throws(function() { mappingTemplate({template: %s, payload: %s}); });\n", JSON.stringify(this.mappingTemplate), JSON.stringify(this.requestBody))); 86 | out.write( indent + " });\n"); 87 | out.write( indent + "});\n"); 88 | } else { 89 | out.write(sprintf(indent + "describe('H=`%s` P=`%s` ===> T=`%s`', function() {\n", JSON.stringify(this.requestHeaders).replace(/'/g, "\\'"), this.requestBody.replace(/'/g, "\\'"), this.mappingTemplate.replace(/'/g, "\\'"))); 90 | out.write(sprintf(indent + " it('return %s', function() {\n", this.requestBody.replace(/'/g, "\\'"))); 91 | out.write(sprintf(indent + " var expected = %s;\n", this.responseBody)); 92 | out.write(sprintf(indent + " var actual = JSON.parse(mappingTemplate({template: %s, payload: %s}));\n", JSON.stringify(this.mappingTemplate), JSON.stringify(this.requestBody))); 93 | out.write( indent + " assert.deepEqual(expected, actual);\n"); 94 | out.write( indent + " });\n"); 95 | out.write( indent + "});\n"); 96 | } 97 | }; 98 | 99 | function hash(input) { 100 | var sha1 = crypto.createHash('sha1'); 101 | sha1.update(input); 102 | return sha1.digest('hex').slice(0, 8); 103 | } 104 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "api-gateway-mapping-template", 3 | "version": "0.0.8", 4 | "description": "Make AWS API Gateway's Mapping Template testable.", 5 | "repository": { 6 | "type": "git", 7 | "url": "http://github.com/ToQoz/api-gateway-mapping-template.git" 8 | }, 9 | "main": "index.js", 10 | "dependencies": { 11 | "JSONPath": "^0.11.2", 12 | "clone": "^1.0.2", 13 | "velocityjs": "^0.7.4" 14 | }, 15 | "devDependencies": { 16 | "adm-zip": "^0.4.7", 17 | "api-gateway-mapping-template": "./", 18 | "aws-sdk": "^2.2.22", 19 | "bluebird": "^3.0.6", 20 | "conventional-changelog": "^0.5.1", 21 | "conventional-github-releaser": "^0.5.0", 22 | "express": "^4.13.3", 23 | "mocha": "^2.3.4", 24 | "sprintf-js": "^1.0.3", 25 | "testling": "^1.7.1" 26 | }, 27 | "scripts": { 28 | "test": "mocha", 29 | "gen": "TEST=test/_.js MD=test/_.md node misc/gen.js", 30 | "changelog": "conventional-changelog -p angular -i CHANGELOG.md -w", 31 | "gh-release": "conventional-github-releaser -p angular" 32 | }, 33 | "license": "MIT", 34 | "author": { 35 | "name": "Takatoshi Matsumoto", 36 | "email": "toqoz403@gmail.com", 37 | "url": "http://toqoz.net" 38 | }, 39 | "tonicExampleFilename": "./example.js", 40 | "testling": { 41 | "harness": "mocha-bdd", 42 | "files": "test/*.js", 43 | "browsers": [ 44 | "ie/6..latest", 45 | "chrome/22..latest", 46 | "firefox/16..latest", 47 | "safari/latest", 48 | "opera/11.0..latest", 49 | "iphone/6..latest", 50 | "ipad/6..latest", 51 | "android-browser/latest" 52 | ] 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /test/_.js: -------------------------------------------------------------------------------- 1 | // This file is generated by `TEST=test/_.js node misc/gen.js` 2 | 3 | var assert = require('assert') 4 | var mappingTemplate = require('../') 5 | 6 | describe('$input.path|$input.json', function() { 7 | // https://github.com/ToQoz/api-gateway-mapping-template/blob/master/test/_.md#example-12f77fd5 8 | describe('H=`{}` P=`a=b` ===> T=`"$input.path(\'$\')"`', function() { 9 | it('return a=b', function() { 10 | var expected = "a=b"; 11 | var actual = JSON.parse(mappingTemplate({template: "\"$input.path('$')\"", payload: "a=b"})); 12 | assert.deepEqual(expected, actual); 13 | }); 14 | }); 15 | // https://github.com/ToQoz/api-gateway-mapping-template/blob/master/test/_.md#example-fcaf7ddd 16 | describe('H=`{}` P=`"a=b"` ===> T=`"$input.path(\'$\')"`', function() { 17 | it('return "a=b"', function() { 18 | var expected = "a=b"; 19 | var actual = JSON.parse(mappingTemplate({template: "\"$input.path('$')\"", payload: "\"a=b\""})); 20 | assert.deepEqual(expected, actual); 21 | }); 22 | }); 23 | // https://github.com/ToQoz/api-gateway-mapping-template/blob/master/test/_.md#example-0dce6fa3 24 | describe('H=`{}` P=`a=b` ===> T=`$input.json(\'$\')`', function() { 25 | it('return a=b', function() { 26 | var expected = "a=b"; 27 | var actual = JSON.parse(mappingTemplate({template: "$input.json('$')", payload: "a=b"})); 28 | assert.deepEqual(expected, actual); 29 | }); 30 | }); 31 | // https://github.com/ToQoz/api-gateway-mapping-template/blob/master/test/_.md#example-dbd6cf1c 32 | describe('H=`{}` P=`"a=b"` ===> T=`$input.json(\'$\')`', function() { 33 | it('return "a=b"', function() { 34 | var expected = "a=b"; 35 | var actual = JSON.parse(mappingTemplate({template: "$input.json('$')", payload: "\"a=b\""})); 36 | assert.deepEqual(expected, actual); 37 | }); 38 | }); 39 | // https://github.com/ToQoz/api-gateway-mapping-template/blob/master/test/_.md#example-03be1e25 40 | describe('H=`{}` P=`{}` ===> T=`"$input.path(\'$\')"`', function() { 41 | it('return {}', function() { 42 | var expected = "{}"; 43 | var actual = JSON.parse(mappingTemplate({template: "\"$input.path('$')\"", payload: "{}"})); 44 | assert.deepEqual(expected, actual); 45 | }); 46 | }); 47 | // https://github.com/ToQoz/api-gateway-mapping-template/blob/master/test/_.md#example-1b8d22cd 48 | describe('H=`{}` P=`"{}"` ===> T=`"$input.path(\'$\')"`', function() { 49 | it('return "{}"', function() { 50 | var expected = "{}"; 51 | var actual = JSON.parse(mappingTemplate({template: "\"$input.path('$')\"", payload: "\"{}\""})); 52 | assert.deepEqual(expected, actual); 53 | }); 54 | }); 55 | // https://github.com/ToQoz/api-gateway-mapping-template/blob/master/test/_.md#example-b9f18c27 56 | describe('H=`{}` P=`{}` ===> T=`$input.json(\'$\')`', function() { 57 | it('return {}', function() { 58 | var expected = {}; 59 | var actual = JSON.parse(mappingTemplate({template: "$input.json('$')", payload: "{}"})); 60 | assert.deepEqual(expected, actual); 61 | }); 62 | }); 63 | // https://github.com/ToQoz/api-gateway-mapping-template/blob/master/test/_.md#example-098fd028 64 | describe('H=`{}` P=`` ===> T=`$input.json(\'$\')`', function() { 65 | it('return ', function() { 66 | var expected = {}; 67 | var actual = JSON.parse(mappingTemplate({template: "$input.json('$')", payload: ""})); 68 | assert.deepEqual(expected, actual); 69 | }); 70 | }); 71 | // https://github.com/ToQoz/api-gateway-mapping-template/blob/master/test/_.md#example-91575d0e 72 | describe('H=`{}` P=`` ===> T=`$input.path(\'$\')`', function() { 73 | it('return ', function() { 74 | var expected = {}; 75 | var actual = JSON.parse(mappingTemplate({template: "$input.path('$')", payload: ""})); 76 | assert.deepEqual(expected, actual); 77 | }); 78 | }); 79 | // https://github.com/ToQoz/api-gateway-mapping-template/blob/master/test/_.md#example-25c6993c 80 | describe('H=`{}` P=`name=toqoz` ===> T=`{"name": "$input.path(\'$\')"}`', function() { 81 | it('return name=toqoz', function() { 82 | var expected = {"name":"name=toqoz"}; 83 | var actual = JSON.parse(mappingTemplate({template: "{\"name\": \"$input.path('$')\"}", payload: "name=toqoz"})); 84 | assert.deepEqual(expected, actual); 85 | }); 86 | }); 87 | // https://github.com/ToQoz/api-gateway-mapping-template/blob/master/test/_.md#example-122ff6ce 88 | describe('H=`{}` P=`{a` ===> T=`"$input.path(\'$\')"`', function() { 89 | it('throw error', function() { 90 | assert.throws(function() { mappingTemplate({template: "\"$input.path('$')\"", payload: "{a"}); }); 91 | }); 92 | }); 93 | // https://github.com/ToQoz/api-gateway-mapping-template/blob/master/test/_.md#example-d4719b3d 94 | describe('H=`{}` P=`a{b` ===> T=`"$input.path(\'$\')"`', function() { 95 | it('return a{b', function() { 96 | var expected = "a{b"; 97 | var actual = JSON.parse(mappingTemplate({template: "\"$input.path('$')\"", payload: "a{b"})); 98 | assert.deepEqual(expected, actual); 99 | }); 100 | }); 101 | // https://github.com/ToQoz/api-gateway-mapping-template/blob/master/test/_.md#example-2b1f2df4 102 | describe('H=`{}` P=`[a` ===> T=`"$input.path(\'$\')"`', function() { 103 | it('throw error', function() { 104 | assert.throws(function() { mappingTemplate({template: "\"$input.path('$')\"", payload: "[a"}); }); 105 | }); 106 | }); 107 | // https://github.com/ToQoz/api-gateway-mapping-template/blob/master/test/_.md#example-c0cfe50d 108 | describe('H=`{}` P=`a[` ===> T=`"$input.path(\'$\')"`', function() { 109 | it('return a[', function() { 110 | var expected = "a["; 111 | var actual = JSON.parse(mappingTemplate({template: "\"$input.path('$')\"", payload: "a["})); 112 | assert.deepEqual(expected, actual); 113 | }); 114 | }); 115 | // https://github.com/ToQoz/api-gateway-mapping-template/blob/master/test/_.md#example-a7361ae0 116 | describe('H=`{}` P=`null{` ===> T=`"$input.path(\'$\')"`', function() { 117 | it('return null{', function() { 118 | var expected = "null{"; 119 | var actual = JSON.parse(mappingTemplate({template: "\"$input.path('$')\"", payload: "null{"})); 120 | assert.deepEqual(expected, actual); 121 | }); 122 | }); 123 | // https://github.com/ToQoz/api-gateway-mapping-template/blob/master/test/_.md#example-728fbd1f 124 | describe('H=`{}` P=`true{` ===> T=`"$input.path(\'$\')"`', function() { 125 | it('return true{', function() { 126 | var expected = "true{"; 127 | var actual = JSON.parse(mappingTemplate({template: "\"$input.path('$')\"", payload: "true{"})); 128 | assert.deepEqual(expected, actual); 129 | }); 130 | }); 131 | // https://github.com/ToQoz/api-gateway-mapping-template/blob/master/test/_.md#example-461fd8fa 132 | describe('H=`{}` P=`false{` ===> T=`"$input.path(\'$\')"`', function() { 133 | it('return false{', function() { 134 | var expected = "false{"; 135 | var actual = JSON.parse(mappingTemplate({template: "\"$input.path('$')\"", payload: "false{"})); 136 | assert.deepEqual(expected, actual); 137 | }); 138 | }); 139 | // https://github.com/ToQoz/api-gateway-mapping-template/blob/master/test/_.md#example-48665148 140 | describe('H=`{}` P=`undefined{` ===> T=`"$input.path(\'$\')"`', function() { 141 | it('return undefined{', function() { 142 | var expected = "undefined{"; 143 | var actual = JSON.parse(mappingTemplate({template: "\"$input.path('$')\"", payload: "undefined{"})); 144 | assert.deepEqual(expected, actual); 145 | }); 146 | }); 147 | // https://github.com/ToQoz/api-gateway-mapping-template/blob/master/test/_.md#example-cd963d7c 148 | describe('H=`{}` P=`` ===> T=`$input`', function() { 149 | it('return ', function() { 150 | var expected = {}; 151 | var actual = JSON.parse(mappingTemplate({template: "$input", payload: ""})); 152 | assert.deepEqual(expected, actual); 153 | }); 154 | }); 155 | // https://github.com/ToQoz/api-gateway-mapping-template/blob/master/test/_.md#example-ec2bf3b4 156 | describe('H=`{}` P=`` ===> T=`$input.keySet`', function() { 157 | it('return ', function() { 158 | var expected = {}; 159 | var actual = JSON.parse(mappingTemplate({template: "$input.keySet", payload: ""})); 160 | assert.deepEqual(expected, actual); 161 | }); 162 | }); 163 | // https://github.com/ToQoz/api-gateway-mapping-template/blob/master/test/_.md#example-3e03488f 164 | describe('H=`{}` P=`` ===> T=`$input.params.keySet`', function() { 165 | it('return ', function() { 166 | var expected = {}; 167 | var actual = JSON.parse(mappingTemplate({template: "$input.params.keySet", payload: ""})); 168 | assert.deepEqual(expected, actual); 169 | }); 170 | }); 171 | // https://github.com/ToQoz/api-gateway-mapping-template/blob/master/test/_.md#example-a35f2355 172 | describe('H=`{}` P=`` ===> T=`$util`', function() { 173 | it('return ', function() { 174 | var expected = {}; 175 | var actual = JSON.parse(mappingTemplate({template: "$util", payload: ""})); 176 | assert.deepEqual(expected, actual); 177 | }); 178 | }); 179 | // https://github.com/ToQoz/api-gateway-mapping-template/blob/master/test/_.md#example-29301967 180 | describe('H=`{}` P=`` ===> T=`$input.params`', function() { 181 | it('return ', function() { 182 | var expected = {}; 183 | var actual = JSON.parse(mappingTemplate({template: "$input.params", payload: ""})); 184 | assert.deepEqual(expected, actual); 185 | }); 186 | }); 187 | // https://github.com/ToQoz/api-gateway-mapping-template/blob/master/test/_.md#example-819c3c26 188 | describe('H=`{}` P=`` ===> T=`$input.json`', function() { 189 | it('return ', function() { 190 | var expected = {}; 191 | var actual = JSON.parse(mappingTemplate({template: "$input.json", payload: ""})); 192 | assert.deepEqual(expected, actual); 193 | }); 194 | }); 195 | // https://github.com/ToQoz/api-gateway-mapping-template/blob/master/test/_.md#example-caa3f41d 196 | describe('H=`{}` P=`` ===> T=`$util.urlEncode`', function() { 197 | it('return ', function() { 198 | var expected = {}; 199 | var actual = JSON.parse(mappingTemplate({template: "$util.urlEncode", payload: ""})); 200 | assert.deepEqual(expected, actual); 201 | }); 202 | }); 203 | }); -------------------------------------------------------------------------------- /test/_.md: -------------------------------------------------------------------------------- 1 | ## example-12f77fd5 2 | Template|Header|Payload|Status code|Result 3 | --------|------|-------|-----------|------ 4 | `"$input.path('$')"`|`None`|`a=b`|`200`|`"a=b"` 5 | 6 | ## example-fcaf7ddd 7 | Template|Header|Payload|Status code|Result 8 | --------|------|-------|-----------|------ 9 | `"$input.path('$')"`|`None`|`"a=b"`|`200`|`"a=b"` 10 | 11 | ## example-0dce6fa3 12 | Template|Header|Payload|Status code|Result 13 | --------|------|-------|-----------|------ 14 | `$input.json('$')`|`None`|`a=b`|`200`|`"a=b"` 15 | 16 | ## example-dbd6cf1c 17 | Template|Header|Payload|Status code|Result 18 | --------|------|-------|-----------|------ 19 | `$input.json('$')`|`None`|`"a=b"`|`200`|`"a=b"` 20 | 21 | ## example-03be1e25 22 | Template|Header|Payload|Status code|Result 23 | --------|------|-------|-----------|------ 24 | `"$input.path('$')"`|`None`|`{}`|`200`|`"{}"` 25 | 26 | ## example-1b8d22cd 27 | Template|Header|Payload|Status code|Result 28 | --------|------|-------|-----------|------ 29 | `"$input.path('$')"`|`None`|`"{}"`|`200`|`"{}"` 30 | 31 | ## example-b9f18c27 32 | Template|Header|Payload|Status code|Result 33 | --------|------|-------|-----------|------ 34 | `$input.json('$')`|`None`|`{}`|`200`|`{}` 35 | 36 | ## example-098fd028 37 | Template|Header|Payload|Status code|Result 38 | --------|------|-------|-----------|------ 39 | `$input.json('$')`|`None`|``|`200`|`{}` 40 | 41 | ## example-91575d0e 42 | Template|Header|Payload|Status code|Result 43 | --------|------|-------|-----------|------ 44 | `$input.path('$')`|`None`|``|`200`|`{}` 45 | 46 | ## example-25c6993c 47 | Template|Header|Payload|Status code|Result 48 | --------|------|-------|-----------|------ 49 | `{"name": "$input.path('$')"}`|`None`|`name=toqoz`|`200`|`{"name":"name=toqoz"}` 50 | 51 | ## example-122ff6ce 52 | Template|Header|Payload|Status code|Result 53 | --------|------|-------|-----------|------ 54 | `"$input.path('$')"`|`None`|`{a`|`400`|`{"message": "Could not process payloadnet.minidev.json.parser.ParseException: Unexpected End Of File position 1: null"}` 55 | 56 | ## example-d4719b3d 57 | Template|Header|Payload|Status code|Result 58 | --------|------|-------|-----------|------ 59 | `"$input.path('$')"`|`None`|`a{b`|`200`|`"a{b"` 60 | 61 | ## example-2b1f2df4 62 | Template|Header|Payload|Status code|Result 63 | --------|------|-------|-----------|------ 64 | `"$input.path('$')"`|`None`|`[a`|`400`|`{"message": "Could not process payloadnet.minidev.json.parser.ParseException: Unexpected End Of File position 1: EOF"}` 65 | 66 | ## example-c0cfe50d 67 | Template|Header|Payload|Status code|Result 68 | --------|------|-------|-----------|------ 69 | `"$input.path('$')"`|`None`|`a[`|`200`|`"a["` 70 | 71 | ## example-a7361ae0 72 | Template|Header|Payload|Status code|Result 73 | --------|------|-------|-----------|------ 74 | `"$input.path('$')"`|`None`|`null{`|`200`|`"null{"` 75 | 76 | ## example-728fbd1f 77 | Template|Header|Payload|Status code|Result 78 | --------|------|-------|-----------|------ 79 | `"$input.path('$')"`|`None`|`true{`|`200`|`"true{"` 80 | 81 | ## example-461fd8fa 82 | Template|Header|Payload|Status code|Result 83 | --------|------|-------|-----------|------ 84 | `"$input.path('$')"`|`None`|`false{`|`200`|`"false{"` 85 | 86 | ## example-48665148 87 | Template|Header|Payload|Status code|Result 88 | --------|------|-------|-----------|------ 89 | `"$input.path('$')"`|`None`|`undefined{`|`200`|`"undefined{"` 90 | 91 | ## example-cd963d7c 92 | Template|Header|Payload|Status code|Result 93 | --------|------|-------|-----------|------ 94 | `$input`|`None`|``|`200`|`{}` 95 | 96 | ## example-ec2bf3b4 97 | Template|Header|Payload|Status code|Result 98 | --------|------|-------|-----------|------ 99 | `$input.keySet`|`None`|``|`200`|`{}` 100 | 101 | ## example-3e03488f 102 | Template|Header|Payload|Status code|Result 103 | --------|------|-------|-----------|------ 104 | `$input.params.keySet`|`None`|``|`200`|`{}` 105 | 106 | ## example-a35f2355 107 | Template|Header|Payload|Status code|Result 108 | --------|------|-------|-----------|------ 109 | `$util`|`None`|``|`200`|`{}` 110 | 111 | ## example-29301967 112 | Template|Header|Payload|Status code|Result 113 | --------|------|-------|-----------|------ 114 | `$input.params`|`None`|``|`200`|`{}` 115 | 116 | ## example-819c3c26 117 | Template|Header|Payload|Status code|Result 118 | --------|------|-------|-----------|------ 119 | `$input.json`|`None`|``|`200`|`{}` 120 | 121 | ## example-caa3f41d 122 | Template|Header|Payload|Status code|Result 123 | --------|------|-------|-----------|------ 124 | `$util.urlEncode`|`None`|``|`200`|`{}` 125 | 126 | -------------------------------------------------------------------------------- /test/input.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'); 2 | var mappingTemplate = require('../'); 3 | 4 | describe('$input', function() { 5 | describe('null object references', function () { 6 | it('render as empty string', function() { 7 | assert.equal(mappingTemplate({template: '$input.path("$").item1', payload: '{"item1":"hello world"}'}), 'hello world'); 8 | assert.equal(mappingTemplate({template: '$input.path("$").item2', payload: '{"item1":"hello world"}'}), ''); 9 | }); 10 | }); 11 | 12 | describe('.path(x)', function () { 13 | it('returns object at x(JSON Path) in payload', function() { 14 | assert.equal(mappingTemplate({template: '$input.path("$")', payload: "toqoz"}), 'toqoz'); 15 | assert.equal(mappingTemplate({template: '$input.path("$")', payload: "to{qoz"}), 'to{qoz'); 16 | 17 | assert.throws(function() { mappingTemplate({template: '$input.path("$")', payload: '{'}); }, Error); 18 | assert.throws(function() { mappingTemplate({template: '$input.path("$")', payload: '['}); }, Error); 19 | 20 | assert.equal(mappingTemplate({template: '$input.path("$.name")', payload: '{"name": "toqoz"}'}), 'toqoz'); 21 | assert.equal(mappingTemplate({template: '$input.path("$.names")', payload: '{"names": ["toqoz", "foo", "bar"]}'}), '[toqoz, foo, bar]'); 22 | }); 23 | }); 24 | 25 | describe('.json(x)', function () { 26 | it('returns object at x(JSON Path) in payload', function() { 27 | assert.equal(mappingTemplate({template: '$input.json("$.name")', payload: '{"name": "toqoz"}'}), '"toqoz"'); 28 | assert.equal(mappingTemplate({template: '$input.json("$.names")', payload: '{"names": ["toqoz", "foo", "bar"]}'}), '["toqoz","foo","bar"]'); 29 | }); 30 | }); 31 | 32 | describe('.params(x)', function () { 33 | var template = '$input.params("name")'; 34 | 35 | it('searchs the value from path at first', function() { 36 | assert.equal(mappingTemplate({template: template, payload: "", params: {path: {name: "toqoz"}, querystring: {name: "ftoqoz"}, header: {name: "fftoqoz"}}}), 'toqoz'); 37 | }); 38 | 39 | it('searchs the value from querystring at second', function() { 40 | assert.equal(mappingTemplate({template: template, payload: "", params: {querystring: {name: "toqoz"}, header: {name: "ftoqoz"}}}), 'toqoz'); 41 | }); 42 | 43 | it('searchs the value from header at third', function() { 44 | assert.equal(mappingTemplate({template: template, payload: "", params: {header: {name: "toqoz"}}}), 'toqoz'); 45 | }); 46 | }); 47 | 48 | describe('.params()', function () { 49 | describe('.header', function() { 50 | it('is header', function() { 51 | var template = '{"header": "$input.params().header"}'; 52 | var result = mappingTemplate({template: template, payload: "", params: {header: {"NAME": "TOQOZ", "AGE": 999}}}); 53 | assert.equal(result, '{"header": "{NAME=TOQOZ, AGE=999}"}'); 54 | }); 55 | 56 | describe('.get(x)', function() { 57 | it('returns header value for x', function () { 58 | var template = "{\"name\": \"$input.params().header.get('NAME')\"}"; 59 | var result = mappingTemplate({template: template, payload: "", params: {header: {"NAME": "TOQOZ", "AGE": 999}}}); 60 | assert.equal(result, '{"name": "TOQOZ"}'); 61 | }); 62 | }); 63 | 64 | describe('.entrySet()', function() { 65 | it('returns entry(=key-value) set', function () { 66 | var template = "\"$input.params().header.entrySet()\""; 67 | var result = mappingTemplate({template: template, payload: "", params: {header: {"NAME": "TOQOZ", "AGE": 999}}}); 68 | assert.equal(result, '"[{key=NAME, value=TOQOZ}, {key=AGE, value=999}]"'); 69 | }); 70 | describe('.size()', function() { 71 | it('returns length of receiver', function () { 72 | var template = "$input.params().header.entrySet().size()"; 73 | var result = mappingTemplate({template: template, payload: "", params: {header: {"NAME": "TOQOZ", "AGE": 999}}}); 74 | assert.equal(result, "2"); 75 | }); 76 | }); 77 | }); 78 | 79 | describe('.keySet()', function() { 80 | it('returns key set', function () { 81 | var template = "\"$input.params().header.keySet()\""; 82 | var result = mappingTemplate({template: template, payload: "", params: {path: {id: 12}, querystring: {}, header: {"NAME": "TOQOZ", "AGE": 999}}}); 83 | assert.equal(result, '"[NAME, AGE]"'); 84 | }); 85 | 86 | describe('.size()', function() { 87 | it ('returns length of receiver', function() { 88 | var template = "$input.params().header.keySet().size()"; 89 | var result = mappingTemplate({template: template, payload: "", params: {path: {id: 12}, querystring: {}, header: {"NAME": "TOQOZ", "AGE": 999}}}); 90 | assert.equal(result, "2"); 91 | }); 92 | }); 93 | }); 94 | 95 | describe('.keySet() and .get(x)', function() { 96 | it('returns header names', function () { 97 | var template = 98 | '{' + 99 | '#foreach($key in $input.params().header.keySet())' + 100 | '"$key": "$input.params().header.get($key)"#if($foreach.hasNext), #end' + 101 | '#end' + 102 | '}'; 103 | var header = {"NAME": "TOQOZ", "AGE": 999}; 104 | var result = mappingTemplate({template: template, payload: "", params: {header: header}}); 105 | assert.equal(result, '{"NAME": "TOQOZ", "AGE": "999"}'); 106 | }); 107 | }); 108 | }); 109 | }); 110 | }); 111 | -------------------------------------------------------------------------------- /test/renderer.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'); 2 | var mappingTemplate = require('../'); 3 | 4 | describe("escape HTML", function() { 5 | it("DO NOT", function() { 6 | assert.equal(mappingTemplate({template: '$input.path("$")', payload: "

"}), '

'); 7 | assert.equal(mappingTemplate({template: '$input.path("$")', payload: "

"}), '

'); 8 | }); 9 | }); 10 | -------------------------------------------------------------------------------- /test/util.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'); 2 | var mappingTemplate = require('../'); 3 | 4 | describe('$util', function() { 5 | describe('.escapeJavaScript()', function() { 6 | it ('escapes as javascript string', function() { 7 | var template = '$util.escapeJavaScript($input.path(\'$\'))'; 8 | var result = mappingTemplate({template: template, payload: 'bo"dy'}); 9 | assert.equal(result, 'bo\"dy'); 10 | }); 11 | }); 12 | describe('.urlEncode()', function() { 13 | it ('encodes to url', function() { 14 | var template = '$util.urlEncode($input.path(\'$\'))'; 15 | var actual = mappingTemplate({template: template, payload: 'エーピーアイゲートウェイ テンプレートマッピング'}); 16 | var expected = '%E3%82%A8%E3%83%BC%E3%83%94%E3%83%BC%E3%82%A2%E3%82%A4%E3%82%B2%E3%83%BC%E3%83%88%E3%82%A6%E3%82%A7%E3%82%A4%20%E3%83%86%E3%83%B3%E3%83%97%E3%83%AC%E3%83%BC%E3%83%88%E3%83%9E%E3%83%83%E3%83%94%E3%83%B3%E3%82%B0'; 17 | assert.equal(actual, expected); 18 | }); 19 | }); 20 | describe('.urlDecode()', function() { 21 | it ('decodes from url', function() { 22 | var template = '$util.urlDecode($input.path(\'$\'))'; 23 | var actual = mappingTemplate({template: template, payload: '%E3%82%A8%E3%83%BC%E3%83%94%E3%83%BC%E3%82%A2%E3%82%A4%E3%82%B2%E3%83%BC%E3%83%88%E3%82%A6%E3%82%A7%E3%82%A4%20%E3%83%86%E3%83%B3%E3%83%97%E3%83%AC%E3%83%BC%E3%83%88%E3%83%9E%E3%83%83%E3%83%94%E3%83%B3%E3%82%B0'}); 24 | var expected = 'エーピーアイゲートウェイ テンプレートマッピング'; 25 | assert.equal(actual, expected); 26 | }); 27 | }); 28 | describe('.base64Encode()', function() { 29 | it ('encodes to base64', function() { 30 | var template = '$util.base64Encode($input.path(\'$\'))'; 31 | var actual = mappingTemplate({template: template, payload: 'エーピーアイゲートウェイテンプレートマッピング'}); 32 | var expected = '44Ko44O844OU44O844Ki44Kk44Ky44O844OI44Km44Kn44Kk44OG44Oz44OX44Os44O844OI44Oe44OD44OU44Oz44Kw'; 33 | assert.equal(actual, expected); 34 | }); 35 | }); 36 | describe('.base64Decode()', function() { 37 | it ('decode from base64', function() { 38 | var template = '$util.base64Decode($input.path(\'$\'))'; 39 | var actual = mappingTemplate({template: template, payload: '44Ko44O844OU44O844Ki44Kk44Ky44O844OI44Km44Kn44Kk44OG44Oz44OX44Os44O844OI44Oe44OD44OU44Oz44Kw'}); 40 | var expected = 'エーピーアイゲートウェイテンプレートマッピング'; 41 | assert.equal(actual, expected); 42 | }); 43 | }); 44 | }); 45 | --------------------------------------------------------------------------------