Api gateway 304 responses with Last-Modified header - aws-lambda

I want to response a 304 response with Last-Modified header.
At first I use Error response to implement.
Handler.js
module.exports.handler = function(event, context, cb) {
const UpdateDate = new Date();
return cb("304 Not Modified", {
"Last-Modified": UpdateDate,
"body":{
"message": {}
}
});
};
s-function.json in endpoints
"responses": {
"304 Not Modified.*": {
"statusCode": "304",
"responseParameters": {
"method.response.header.Last-Modified": "integration.response.body.Last-Modified"
},
"responseModels": {
"application/json;charset=UTF-8": "Empty"
},
"responseTemplates": {
"application/json;charset=UTF-8": "$input.json('$.body')"
}
},
"default": {
"statusCode": "200",
"responseParameters": {
"method.response.header.Cache-Control": "'public, max-age=86400'",
"method.response.header.Last-Modified": "integration.response.body.Last-Modified"
},
"responseModels": {
"application/json;charset=UTF-8": "Empty"
},
"responseTemplates": {
"application/json;charset=UTF-8": "$input.json('$.body')"
}
}
}
However, I find it on Lambda doc.
If an error is provided, callback parameter is ignored.
So, this doesn't work.
Is there any solution to response a 304 response with header?
Updated:
Is it possible to return a Error object and map responses 304 in s-function? Below code can't map to 304.
s-funtion.json
"responses": {
".*304 Not Modified.*": {
"statusCode": "304",
"responseParameters": {
"method.response.header.Cache-Control": "'public, max-age=86400'",
"method.response.header.Last-Modified": "integration.response.body.errorMessage.Last-Modified"
}
}
Handler.js
return cb({
"status" : "304 Not Modified",
"Last-Modified": UpdateDate
), null);
I also try this. It can mapping to 304 but header can't get "integration.response.body.errorMessage.Last-Modified"
return cb(JSON.stringify({
"status" : "304 Not Modified",
"Last-Modified": UpdateDate
}), null);
I try $util.parseJson but not working on responseParameter.
Invalid mapping expression specified:$util.parseJson($input.path('$.errorMessage')).Last-Modified
"responseParameters": {
"method.response.header.Cache-Control": "'public, max-age=86400'",
"method.response.header.Last-Modified": "$util.parseJson($input.path('$.errorMessage')).Last-Modified"
},

To return status 304 in your API, you would need to throw an error from your Lambda function. It is possible to return the "Last-Modified" value in the error message from your Lambda function and route that to the "Last-Modified" header in the API response.
For details have a look at Option 2 here
Thanks,
Ryan

Related

Is there any cost advantage of Parse.Object.saveAll vs. saving individually?

The Parse JS SDK provides a Parse.Object.saveAll() method to save many objects with one command.
From looking at ParseServerRESTController.js it seems that each object is saved individually:
if (path === '/batch') {
let initialPromise = Promise.resolve();
if (data.transaction === true) {
initialPromise = config.database.createTransactionalSession();
}
return initialPromise.then(() => {
const promises = data.requests.map(request => {
return handleRequest(
request.method,
request.path,
request.body,
options,
config
).then(
response => {
return {
success: response
};
},
error => {
return {
error: {
code: error.code,
error: error.message
},
};
}
);
});
return Promise.all(promises).then(result => {
if (data.transaction === true) {
if (
result.find(resultItem => typeof resultItem.error === 'object')
) {
return config.database.abortTransactionalSession().then(() => {
return Promise.reject(result);
});
} else {
return config.database.commitTransactionalSession().then(() => {
return result;
});
}
} else {
return result;
}
});
});
}
It seems that saveAll is merely a convenience wrapper around saving each object individually, so it still does seem to make n database requests for n objects.
It it correct that saveAll has no cost advantage (performance, network traffic, etc) vs. saving each object individually in Cloud Code?
I can tell you that the answer is that Parse.Object.saveAll and Parse.Object.destroyAll batch requests by default in batches of 20 objects. But why take my word for it? Let's test it out!
Turn verbose logging on and then run the following:
const run = async function run() {
const objects = [...Array(10).keys()].map(i => new Parse.Object('Test').set({i}));
await Parse.Object.saveAll(objects);
const promises = objects.map(o => o.increment('i').save());
return Promise.all(promises);
};
run()
.then(console.log)
.catch(console.error);
And here's the output from the parse-server logs (I've truncated it, but it should be enough to be apparent what is going on):
verbose: REQUEST for [POST] /parse/batch: { // <--- note the path
"requests": [ // <--- an array of requests!!!
{
"method": "POST",
"body": {
"i": 0
},
"path": "/parse/classes/Test"
},
... skip the next 7, you get the idea
{
"method": "POST",
"body": {
"i": 9
},
"path": "/parse/classes/Test"
}
]
}
.... // <-- remove some irrelevent output for brevity.
verbose: RESPONSE from [POST] /parse/batch: {
"response": [
{
"success": {
"objectId": "szVkuqURVq",
"createdAt": "2020-03-05T21:25:44.487Z"
}
},
...
{
"success": {
"objectId": "D18WB4Nsra",
"createdAt": "2020-03-05T21:25:44.491Z"
}
}
]
}
...
// now we iterate through and there's a request per object.
verbose: REQUEST for [PUT] /parse/classes/Test/szVkuqURVq: {
"i": {
"__op": "Increment",
"amount": 1
}
}
...
verbose: REQUEST for [PUT] /parse/classes/Test/HtIqDIsrX3: {
"i": {
"__op": "Increment",
"amount": 1
}
}
// and the responses...
verbose: RESPONSE from [PUT] /parse/classes/Test/szVkuqURVq: {
"response": {
"i": 1,
"updatedAt": "2020-03-05T21:25:44.714Z"
}
}
...
In the core manager code, you do correctly identify that we are making a request for each object to the data store (i.e. MongoDB), This is necessary because an object may have relations or pointers that have to be handled and that may require additional calls to the data store.
BUT! calls between the parse server and the data store are usually over very fast networks using a binary format, whereas calls between the client and the parse server are JSON and go over longer distances with ordinarily much slower connections.
There is one other potential advantage that you can see in the core manager code which is that the batch is done in a transaction.

Loopback : Validate model from another model is not returning proper error message

I am validating model from another model like below
Model.addFavorite = function (data, callbackFn) {
if (data) {
var faviroteModel = this.app.models.Favorite;
var objFavorite = new faviroteModel(data);
objFavorite.isValid(function (isValid) {
if (isValid) {
callbackFn(null, objFavorite);
}
else {
callbackFn(objFavorite.errors);
}
});
}
else callbackFn("Post data required", {});
}
If i do this then I am getting error like below
{
"error": {
"statusCode": 500,
"t": [
"is not a valid date"
]
}
}
It should be with error message like below
{
"error": {
"statusCode": 422,
"name": "ValidationError",
"message": "The `Favorite` instance is not valid. Details: `t` is not a valid date (value: Invalid Date).",
"details": {
"context": "Favorite",
"codes": {
"t": [
"date"
]
},
"messages": {
"t": [
"is not a valid date"
]
}
}
}
}
Can anyone tell me what am i missing here.
How can i achieve this.
https://github.com/strongloop/loopback-datasource-juggler/blob/master/lib/validations.js#L843
You might run into situations where you need to raise a validation
error yourself, for example in a "before" hook or a custom model
method.
if (model.isValid()) {
return callback(null, { success: true });
}
// This line shows how to create a ValidationError
var err = new MyModel.ValidationError(model);
callback(err);
}

CppRestSDK https request not working

This is returning error code 401 or 500. Can somebody help me where I am going wrong?
http_client client(L"https://oxford-speech.cloudapp.net/token/issueToken/");
uri_builder query;
query.append_query(L"grant_type", L"client_credentials");
query.append_query(L"client_id", L"test-app");
query.append_query(L"client_secret", L"<client secret goes here>");
query.append_query(L"scope", L"https://speech.platform.bing.com");
query.append_query(L"content_type", L"application/x-www-form-urlencoded");
http_request msg(methods::POST);
msg.headers().set_content_type(L"application/x-www-form-urlencoded");
msg.set_request_uri(query.to_string());
std::wstring str = msg.to_string();
return client.request(msg);
Thanks all. I changed the code to following and i got the token!
pplx::task<void> getAccessToken()
{
istream bodyStream;
http_client client(L"https://api.cognitive.microsoft.com/sts/v1.0/issueToken");
http_request req(methods::POST);
req.headers().add(L"Ocp-Apim-Subscription-Key", L"YOUR_KEY");
return client.request(req)
.then([](http_response response)
{
if (response.status_code() != status_codes::OK)
{
return pplx::task_from_result();
}
istream bodyStream = response.body();
container_buffer<std::string> inStringBuffer;
return bodyStream.read_line(inStringBuffer)
.then([inStringBuffer](size_t bytesRead)
{
const std::string &text = inStringBuffer.collection();
std::cout << text;
});
});
};
Here's a generic JSON representation of a request that worked last time I tried (Sept 2016). Your request looks pretty different. Extracted from Woundify settings file
{
"name": "BingSpeechToTextService",
"classInterface": "BingServices.ISpeechToTextService",
"request": {
"method": "post", // { "get" | "post" | <custom> }
"preferChunkedEncodedRequests": false,
"uri": {
"scheme": "https",
"host": "speech.platform.bing.com",
"path": "recognize",
"query": "scenarios=smd&appid=D4D52672-91D7-4C74-8AD8-42B1D98141A5&locale={locale}&device.os=wp7&version=3.0&format=json&instanceid=565D69FF-E928-4B7E-87DA-9A750B96D9E3&requestid={guid}"
},
"headers": [
{
"Name": "Accept",
"Accept": "application/json"
},
{
"Name": "BearerAuthentication",
"BearerAuthentication": {
"type": "bearer", // { basic | bearer | <custom> }
"clientID": "",
"clientSecret": "",
"scope": "https://speech.platform.bing.com",
"uri": "https://oxford-speech.cloudapp.net/token/issueToken",
"grant": "grant_type=client_credentials&client_id={clientID}&client_secret={clientSecret}&scope={scope}"
}
},
{
"Name": "Content-Type",
"ContentType": "audio/wav; codec=\"audio/pcm\"; samplerate={sampleRate}"
}
],
"data": {
"type": "binary" // { ascii | base64 | binary | json | raw | string | urlencode }
}
},
"response": {
"missingResponse": "whatever",
"jsonPath": "results[0].name"
}
},
Please note that there's a simpler token-issuing URL nowadays. Your C++ code will look something like this:
pplx::task<string_t> getToken()
{
http_client client(L"https://api.cognitive.microsoft.com/sts/v1.0/issueToken");
http_request req(methods::POST);
req.headers().add(L"Ocp-Apim-Subscription-Key", YOUR_API_KEY);
return client.request(req).then([=](http_response response) -> pplx::task<string_t>
{
return response.extract_string(true);
});
}
The entire response body is the token unlike the old scheme, which had a JSON response that included the token.

I need Apollo GraphQL to send back http responses

I'd like for the ability to have my express graphQL server to send back the HTTP status code along with the response to the client. At the moment, all I see is data, however, additional functionality based on the status code would be helpful.
I don't see anything here, unless I'm looking in the wrong place:
http://dev.apollodata.com/tools/graphql-server/setup.html
For Apollo Server 2.2.x and later, we can use plugins to custom the HTTP status code. Custom the HTTP status code in willsendresponse event handler. You can check the errors array and set the corresponding HTTP status code for different type of error.
E.g.
server.ts:
import { ApolloServer, gql } from 'apollo-server';
import { ApolloServerPlugin } from 'apollo-server-plugin-base';
function customHTTPStatusPlugin(): ApolloServerPlugin {
return {
requestDidStart(requestContext) {
return {
willSendResponse({ errors, response }) {
if (response && response.http) {
if (errors) {
response.data = undefined;
response.http.status = 500;
}
}
},
};
},
};
}
const typeDefs = gql`
type Query {
hello: String
}
`;
const resolvers = {
Query: {
hello() {
throw new Error('something happened');
},
},
};
const server = new ApolloServer({ typeDefs, resolvers, plugins: [customHTTPStatusPlugin()] });
const port = 3000;
server.listen(port).then(({ url }) => console.log(`Server is ready at ${url}`));
The response:
{
"error": {
"errors": [
{
"message": "something happened",
"locations": [
{
"line": 2,
"column": 3
}
],
"path": [
"hello"
],
"extensions": {
"code": "INTERNAL_SERVER_ERROR",
"exception": {
"stacktrace": [
"Error: something happened",
" at hello (/Users/ldu020/workspace/github.com/mrdulin/apollo-graphql-tutorial/src/stackoverflow/40387508/server.ts:30:13)",
" at field.resolve (/Users/ldu020/workspace/github.com/mrdulin/apollo-graphql-tutorial/node_modules/apollo-server-express/node_modules/graphql-extensions/src/index.ts:274:18)",
" at field.resolve (/Users/ldu020/workspace/github.com/mrdulin/apollo-graphql-tutorial/node_modules/apollo-server-express/node_modules/apollo-server-core/src/utils/schemaInstrumentation.ts:103:18)",
" at resolveFieldValueOrError (/Users/ldu020/workspace/github.com/mrdulin/apollo-graphql-tutorial/node_modules/graphql/execution/execute.js:467:18)",
" at resolveField (/Users/ldu020/workspace/github.com/mrdulin/apollo-graphql-tutorial/node_modules/graphql/execution/execute.js:434:16)",
" at executeFields (/Users/ldu020/workspace/github.com/mrdulin/apollo-graphql-tutorial/node_modules/graphql/execution/execute.js:275:18)",
" at executeOperation (/Users/ldu020/workspace/github.com/mrdulin/apollo-graphql-tutorial/node_modules/graphql/execution/execute.js:219:122)",
" at executeImpl (/Users/ldu020/workspace/github.com/mrdulin/apollo-graphql-tutorial/node_modules/graphql/execution/execute.js:104:14)",
" at Object.execute (/Users/ldu020/workspace/github.com/mrdulin/apollo-graphql-tutorial/node_modules/graphql/execution/execute.js:64:35)",
" at /Users/ldu020/workspace/github.com/mrdulin/apollo-graphql-tutorial/node_modules/apollo-server-express/node_modules/apollo-server-core/src/requestPipeline.ts:548:22"
]
}
}
}
]
}
}
You will also get the HTTP status code - 500 Internal Server Error at the client-side.

Lambda and API gateway mapping

I want to return a value from handler to API gateway response header.
Handler.js
module.exports.handler = function(event, context, cb) {
const UpdateDate = new Date();
return cb(null, {
body: {
message: 'test'
},
header: {
Last-Modified: UpdateDate
}
});
};
s-function.json in "endpoints"
"responses": {
"400": {
"statusCode": "400"
},
"default": {
"statusCode": "200",
"responseParameters": {
"method.response.header.Cache-Control": "'public, max-age=86400'",
"method.response.header.Last-Modified": "integration.response.body.header.Last-Modified"
},
"responseModels": {
"application/json;charset=UTF-8": "Empty"
},
"responseTemplates": {
"application/json;charset=UTF-8": "$input.json('$.body')"
}
}
}
This can work. But I want to know how to use "integration.response.header.Last-Modified". Is my handler callback formate wrong?
Edit:
s-function.json in "endpoints"
"integration.response.header.Last-Modified" This doesn't work.
I want to know specific handler return formate to pass data to "integration.response.header.Last-Modified".
"responses": {
"400": {
"statusCode": "400"
},
"default": {
"statusCode": "200",
"responseParameters": {
"method.response.header.Cache-Control": "'public, max-age=86400'",
"method.response.header.Last-Modified": "integration.response.header.Last-Modified"
},
"responseModels": {
"application/json;charset=UTF-8": "Empty"
},
"responseTemplates": {
"application/json;charset=UTF-8": "$input.json('$.body')"
}
}
}
All of the output from your lambda function is returned in the response body, so you will need to map part of the response body to your API response header.
module.exports.handler = function(event, context, cb) {
const UpdateDate = new Date();
return cb(null, {
message: 'test',
Last-Modified: UpdateDate
});
};
will produce payload "{"message" : "test", "Last-Modified" : "..."}"
In this case you would use "integration.response.body.Last-Modified" as the mapping expression. As a side note, naming things "body" and "header" in your response body may make the mapping expressions confusing to read.
Thanks,
Ryan

Resources