API Gateway - Return XML or JSON - amazon-web-services

I have a node API Gateway stack and a Node Lambda. I've been trying to get API gateway to return content-type: application/xml OR application/json depending on the request (returnType=xml or returnType=json).
I have tried adding response models and that didn't work. BinaryTypes didn't work either. I have gotten it to do either application/json OR application/xml but I can't get it to do both. Is what I'm trying to do even possible? or should I create two separate endpoints?
This example always returns application/json.
Here is my lambda:
exports.handler = async function (event, context, callback) {
var format = event.format;
if (!format) {
callback(Error("[BadRequest] missing parameters"));
}
const promise = new Promise(function (resolve, reject) {
https
.get("exampleendpoint.com", (res) => {
let body = "";
res.on("data", (chunk) => {
body += chunk;
});
res.on("end", () => {
var results = JSON.parse(body);
if (format && format.toUpperCase() === "XML") {
var response = {
statusCode: 200,
headers: { "content-type": "application/xml" },
body:
'<?xml version="1.0" encoding="UTF-8"?><result>' +
OBJtoXML(results) +
"</result>",
};
resolve(response);
} else {
var response = {
statusCode: 200,
headers: { "content-type": "application/json" },
body: JSON.stringify(results),
};
resolve(response);
}
});
})
.on("error", (e) => {
var response = {
statusCode: 500,
body: "",
errorMessage: "Error from example.com",
};
resolve(response);
});
});
return promise;
};
Here is my api gateway code:
const epqApi = new gateway.RestApi(this, "restApi", {
restApiName: "resultsApi",
cloudWatchRole: true,
description: "Calls the service for the app",
endpointTypes: [gateway.EndpointType.REGIONAL],
deployOptions: {
stageName: "prod",
loggingLevel: gateway.MethodLoggingLevel.OFF,
dataTraceEnabled: false,
},
});
const epqResource = epqApi.root.addResource("v1");
const epqIntegration: gateway.LambdaIntegration =
new gateway.LambdaIntegration(generatePqsResultFunction, {
proxy: false,
allowTestInvoke: true,
passthroughBehavior: gateway.PassthroughBehavior.NEVER,
contentHandling: gateway.ContentHandling.CONVERT_TO_TEXT,
requestTemplates: {
"application/json": `{
"format":"$input.params(\'format\')"
}`,
},
integrationResponses: [
{
statusCode: "200",
responseParameters: {
"method.response.header.Access-Control-Allow-Origin": "'*'",
},
responseTemplates: {
"application/json": "$input.path('$.body')",
"application/xml": "$input.path('$.body')",
},
},
{
statusCode: "400",
selectionPattern: "^\\[BadRequest\\].*",
responseParameters: {
"method.response.header.Access-Control-Allow-Origin": "'*'",
},
responseTemplates: {
"application/javascript":
"#set($inputRoot = $input.path('$')) {\"errorMessage\" : \"$input.path('$.errorMessage')\"}",
},
},
],
});
epqResource.addMethod("GET", epqIntegration, {
requestParameters: {
//all params need to be in here, even if they are not required
"method.request.querystring.x": false,
"method.request.querystring.y": false,
"method.request.querystring.units": false,
"method.request.querystring.format": false,
"method.request.querystring.wkid": false,
"method.request.querystring.includeDate": false,
},
methodResponses: [
// Successful response from the integration
{
statusCode: "200",
responseParameters: {
"method.response.header.Access-Control-Allow-Origin": true,
},
},
{
statusCode: "400",
responseParameters: {
"method.response.header.Access-Control-Allow-Origin": true,
},
},
],
});
}

Related

Nuxt Auth redirects after refresh when cookie is set to false

I am using Nuxt-auth local strategy, I set the cookie in nuxt.config.js to false because my token is too large for cookie, but when I do that when refreshing the page it redirects me to login page.
Here is my auth configuration.
auth: {
cookie: false,
localStorage: {
prefix: "auth.",
},
redirect: {
login: "/auth/signin",
logout: "/auth/signin",
callback: "/auth/signin",
home: false,
},
strategies: {
local: {
token: {
property: "token",
global: true,
required: true,
type: "Bearer",
},
user: {
property: "data",
autoFetch: true,
},
endpoints: {
login: { url: "/login", method: "post" },
logout: { url: "/logout", method: "post" },
user: { url: "/auth/user", method: "get" },
},
},
},
},
any solutions?

Postman pre-request script login with Query Params

I want to create a pre-script in postman to login so that i can get tokens.
pm.sendRequest({
url: 'https://localhost/api/login',
method: 'POST',
header: {
'Content-Type': 'multipart/form-data',
},
body: {
mode: 'formdata',
formdata: [
{key: "email", value: "myemail#mydoman.com", disabled: false, description: {content:"", type:"text/plain"}},
{key: "password", value: "mypass", disabled: false, description: {content:"", type:"text/plain"}},
{key: "action", value: "login", disabled: false, description: {content:"", type:"text/plain"}}
]
}
}, function(err, response) {
pm.environment.set("access_token");
});
I'm still no get token with those pre-script, help needed
const loginRequest = {
url: pm.environment.get('my_url') +"/api/login",
method: 'POST',
header: 'Content-Type: application/json',
body: {
mode: 'application/json',
raw: JSON.stringify({
"email": "myemail#mydoman.com",
"password":"mypass"
})
}
};
pm.sendRequest(loginRequest, function (err, response) {
pm.environment.set("access_token", response.json().token);
});

aws api gateway and lamda - how to get event.body

I'm new to aws and I have a strange problem of getting the body of event inside my lamda handler function.
exports.handler = async (event) => {
const response = {
statusCode: 200,
body: event.body
};
return response;
};
When I run test I get
Response:
{
"statusCode": 200
}
However when I only return event
exports.handler = async (event) => {
const response = {
statusCode: 200,
body: event <=====
};
return response;
};
I get
Response:
{
"statusCode": 200,
"body": {
"key1": "value1",
"key2": "value2",
"key3": "value3"
}
}
I'm using node 8.10. Does anybody knows what I'm doing wrong here?
The test event in to Lambda console is exactly what you get as the event parameter in your Lambda handler. When you put {"a":1}, you get {"a":1}.
You can simulate a different event types of AWS service (SNS, S3, API Gateway) selecting a template from the combobox.
As you are returning a HTTP response, you want probably to simulate an API Gateway event, it could look like this:
{
"body": "{\"a\":1}",
"pathParameters": {
"id": "XXX"
},
"resource": "/myres",
"path": "/myres",
"httpMethod": "GET",
"isBase64Encoded": true,
"requestContext": {
"authorizer": {
"tenantId": "TEST"
},
"accountId": "123456789012",
"resourceId": "123456",
"stage": "test",
"requestId": "test-request-id",
"requestTime": "09/Apr/2015:12:34:56 +0000",
"requestTimeEpoch": 1428582896000,
"path": "/myres",
"resourcePath": "/myres,
"httpMethod": "GET",
"apiId": "1234567890",
"protocol": "HTTP/1.1"
}
}
Then you will get the body in event.body as JSON string - you can convert it into an object by JSON.parse(event.body).
When returning, you have to serialize the response body with JSON.stringify:
return {
statusCode: 200,
body: JSON.stingify({your:'object'})
};
Change
exports.handler = async (event) => {
const response = {
statusCode: 200,
body: event.body
};
return response;
};
to
exports.handler = async (event) => {
const response = {
statusCode: 200,
body: JSON.stringify(event.body)
};
return response;
};
The body you return in API Gateway must be stringified, otherwise it doesn't know how to deal with the response.

Automating CORS using BOTO3 for AWS Api Gateway

I have been attempting for the last many hours to configure CORS for the AWS Api Gateway. I have attempted to copy verbatim what the "Enable CORS" button does within the aws console. But even though every method looks identical in the console, POSTing to the rest API works with the "Enable CORS" button but returns a 500 permission error when CORS is set up using my code.
This is the code that is relevant to CORS setup:
# Set the put method response of the POST method
self.apigateway.put_method_response(
restApiId=self.rest_api['id'],
resourceId=root_resource['id'],
httpMethod='POST',
statusCode='200',
responseParameters={
'method.response.header.Access-Control-Allow-Origin': False
},
responseModels={
'application/json': 'Empty'
}
)
# Set the put integration response of the POST method
self.apigateway.put_integration_response(
restApiId=self.rest_api['id'],
resourceId=root_resource['id'],
httpMethod='POST',
statusCode='200',
responseParameters={
'method.response.header.Access-Control-Allow-Origin': '\'*\''
},
responseTemplates={
'application/json': ''
}
)
# Add an options method to the rest api
api_method = self.apigateway.put_method(
restApiId=self.rest_api['id'],
resourceId=root_resource['id'],
httpMethod='OPTIONS',
authorizationType='NONE'
)
# Set the put integration of the OPTIONS method
self.apigateway.put_integration(
restApiId=self.rest_api['id'],
resourceId=root_resource['id'],
httpMethod='OPTIONS',
type='MOCK',
requestTemplates={
'application/json': ''
}
)
# Set the put method response of the OPTIONS method
self.apigateway.put_method_response(
restApiId=self.rest_api['id'],
resourceId=root_resource['id'],
httpMethod='OPTIONS',
statusCode='200',
responseParameters={
'method.response.header.Access-Control-Allow-Headers': False,
'method.response.header.Access-Control-Allow-Origin': False,
'method.response.header.Access-Control-Allow-Methods': False
},
responseModels={
'application/json': 'Empty'
}
)
# Set the put integration response of the OPTIONS method
self.apigateway.put_integration_response(
restApiId=self.rest_api['id'],
resourceId=root_resource['id'],
httpMethod='OPTIONS',
statusCode='200',
responseParameters={
'method.response.header.Access-Control-Allow-Headers': '\'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token\'',
'method.response.header.Access-Control-Allow-Methods': '\'POST,OPTIONS\'',
'method.response.header.Access-Control-Allow-Origin': '\'*\''
},
responseTemplates={
'application/json': ''
}
)
This is the response from get-method for POST and OPTIONS when CORS is enabled through the AWS console:
{
"httpMethod": "POST",
"apiKeyRequired": false,
"methodIntegration": {
"httpMethod": "POST",
"cacheKeyParameters": [],
"integrationResponses": {
"200": {
"responseParameters": {
"method.response.header.Access-Control-Allow-Origin": "'*'"
},
"statusCode": "200",
"responseTemplates": {
"application/json": null
}
}
},
"uri": "arn:aws:apigateway:us-east-1:lambda:path/2015-03-31/functions/arn:aws:lambda:us-east-1:477869670267:function:controller/invocations",
"requestTemplates": {
"application/json": null
},
"cacheNamespace": "o9h9b8tzo2",
"type": "AWS"
},
"methodResponses": {
"200": {
"responseParameters": {
"method.response.header.Access-Control-Allow-Origin": false
},
"statusCode": "200",
"responseModels": {
"application/json": "Empty"
}
}
},
"authorizationType": "NONE"
}
{
"requestParameters": {},
"httpMethod": "OPTIONS",
"methodResponses": {
"200": {
"statusCode": "200",
"responseParameters": {
"method.response.header.Access-Control-Allow-Headers": false,
"method.response.header.Access-Control-Allow-Methods": false,
"method.response.header.Access-Control-Allow-Origin": false
},
"responseModels": {
"application/json": "Empty"
}
}
},
"apiKeyRequired": false,
"methodIntegration": {
"cacheNamespace": "o9h9b8tzo2",
"type": "MOCK",
"requestTemplates": {
"application/json": "{\"statusCode\": 200}"
},
"integrationResponses": {
"200": {
"responseTemplates": {
"application/json": null
},
"statusCode": "200",
"responseParameters": {
"method.response.header.Access-Control-Allow-Headers": "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token'",
"method.response.header.Access-Control-Allow-Methods": "'POST,OPTIONS'",
"method.response.header.Access-Control-Allow-Origin": "'*'"
}
}
},
"cacheKeyParameters": []
},
"authorizationType": "NONE"
}
And this is the response of get-method from CORS being enabled using my code:
{
"authorizationType": "NONE",
"httpMethod": "POST",
"methodIntegration": {
"requestTemplates": {
"application/json": null
},
"cacheNamespace": "308o168qal",
"uri": "arn:aws:apigateway:us-east-1:lambda:path/2015-03-31/functions/arn:aws:lambda:us-east-1:477869670267:function:controller/invocations",
"httpMethod": "POST",
"cacheKeyParameters": [],
"integrationResponses": {
"200": {
"responseParameters": {
"method.response.header.Access-Control-Allow-Origin": "'*'"
},
"statusCode": "200",
"responseTemplates": {
"application/json": null
}
}
},
"type": "AWS"
},
"apiKeyRequired": false,
"methodResponses": {
"200": {
"responseParameters": {
"method.response.header.Access-Control-Allow-Origin": false
},
"responseModels": {
"application/json": "Empty"
},
"statusCode": "200"
}
}
}
{
"authorizationType": "NONE",
"apiKeyRequired": false,
"methodIntegration": {
"integrationResponses": {
"200": {
"statusCode": "200",
"responseTemplates": {
"application/json": null
},
"responseParameters": {
"method.response.header.Access-Control-Allow-Methods": "'POST,OPTIONS'",
"method.response.header.Access-Control-Allow-Origin": "'*'",
"method.response.header.Access-Control-Allow-Headers": "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token'"
}
}
},
"cacheNamespace": "bm4zmvzkdk",
"type": "MOCK",
"cacheKeyParameters": [],
"requestTemplates": {
"application/json": "{\"statusCode\": 200}"
}
},
"requestParameters": {},
"methodResponses": {
"200": {
"statusCode": "200",
"responseModels": {
"application/json": "Empty"
},
"responseParameters": {
"method.response.header.Access-Control-Allow-Methods": false,
"method.response.header.Access-Control-Allow-Origin": false,
"method.response.header.Access-Control-Allow-Headers": false
}
}
},
"httpMethod": "OPTIONS"
}
I can not see a single difference, what am I doing wrong?
As per the request of MikeD at AWS the POST request is made from javascript within a file sitting within s3:
function post_request() {
var xhr = new XMLHttpRequest();
var params = JSON.stringify({
request: "registerUser",
user:{
username: document.getElementById("usernameInput").value,
email: document.getElementById("emailInput").value,
password: document.getElementById("passwordInput").value
}
});
xhr.open("POST", "$(endpoint_url)", true);
xhr.setRequestHeader("Content-type", "application/json");
xhr.setRequestHeader("x-api-key", "$(api_key)");
xhr.onreadystatechange = function(){
if(xhr.readyState === 4){
if(xhr.status === 200){
alert("You are registered!");
}
else{
alert("Could not register. Please try again later.");
}
}
};
xhr.send(params);
return false;
}
Where $(endpoint_url) and $(api_key) are replaced with appropriate values by my setup script (I have confirmed the values are accurate).
This is the verbatim response from the chrome console when the POST request is made:
register.html?X-Amz-Date=20160628T070211Z&X-Amz-Expires=300&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-…:39 OPTIONS https://dn9sjxz0i9.execute-api.us-east-1.amazonaws.com/prod post_request # register.html?X-Amz-Date=20160628T070211Z&X-Amz-Expires=300&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-…:39document.getElementById.onsubmit # register.html?X-Amz-Date=20160628T070211Z&X-Amz-Expires=300&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-…:44
register.html?X-Amz-Date=20160628T070211Z&X-Amz-Expires=300&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-…:1 XMLHttpRequest cannot load https://dn9sjxz0i9.execute-api.us-east-1.amazonaws.com/prod. Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'https://s3.amazonaws.com' is therefore not allowed access. The response had HTTP status code 500.
The put integration of the OPTIONS method needs to have a mapping template which includes a statusCode with a 200 value. It looks like your code was setting the mapping template to an empty string (''). When you create the integration via the API Gateway, it adds a default mapping template of: {"statusCode": 200}
Add the same mapping template to your put integration like so:
# Set the put integration of the OPTIONS method
self.apigateway.put_integration(
restApiId=self.rest_api['id'],
resourceId=root_resource['id'],
httpMethod='OPTIONS',
type='MOCK',
requestTemplates={
'application/json': '{"statusCode": 200}'
}
)

Redux Promise Middleware + Redux Mock Store Testing

I tried to test an action creator that returns a promise, using also redux mock store.
import promiseMiddleware from 'redux-promise-middleware';
import nock from 'nock';
import configureStore from 'redux-mock-store';
import { domain, port } from '../../config/environment';
import { GET_ITEMS_START,
GET_ITEMS_SUCCESS } from '../../constants/items';
import { getItems } from './items';
const promise = promiseMiddleware({
promiseTypeSuffixes: ['START', 'SUCCESS', 'ERROR']
});
describe('Get Items', () => {
it('should create GET_ITEMS_SUCCESS action after successfully getting items', (done) => {
nock(`${domain}:${port}`)
.get('/api/items')
.reply(200, {
_id: '1',
text: 'Make Eggs',
completed: false
});
const expectedActions = [
{ type: GET_ITEMS_START },
{ type: GET_ITEMS_SUCCESS, payload: {
data: { _id: '1', text: 'Make Eggs', completed: false }
}}
];
const store = mockStore({}, expectedActions, done);
store.dispatch(getItems());
});
});
and here is my action creator code
export function getItems() {
return {
type: GET_ITEMS,
payload: {
promise: axios.get(`${domain}:${port}/api/items`)
}
};
}
but the result is mismatch because the promise resolved a deep nested objects
Error: Expected { payload: { config: { headers: {}, method: 'get', timeout: 0, transformRequest: [ [Function] ], transformResponse: [ [Function] ], url: 'http://localhost:3000/api/items', withCredentials: undefined }, data: { _id: '1', completed: false, text: 'Make Eggs' }, headers: {}, status: 200, statusText: 'OK' }, type: 'GET_ITEMS_SUCCESS' } to equal { payload: { data: { _id: '1', completed: false, text: 'Make Eggs' } }, type: 'GET_ITEMS_SUCCESS' }
+ expected - actual
{
"payload": {
- "config": {
- "headers": {}
- "method": "get"
- "timeout": 0
- "transformRequest": [
- [Function]
- ]
- "transformResponse": [
- [Function]
- ]
- "url": "http://localhost:3000/api/items"
- "withCredentials": [undefined]
- }
"data": {
"_id": "1"
"completed": false
"text": "Make Eggs"
}
- "headers": {}
- "status": 200
- "statusText": "OK"
}
"type": "GET_ITEMS_SUCCESS"
}
I obviously don't want to copy all of those deep nested properties into my test suite.
Is there a better way of doing this?
If you're writing a unit test, you probably don't need to call the actual REST API. Instead you can mock axios and make it return some fake data which won't be so deep.