Cloudfront Function always returns 503 - amazon-cloudfront

How do you set a default root object for subdirectories for a statically hosted website on Cloudfront?
This is a known issue but what I want to know is, how to set up the lambda in CDK. I have used the solution below but when I access the site I get a 503 response
The CloudFront function returned an invalid value: response.statusCode is missing
Testing this in the AWS console is successful so why wouldn't it work on the hosted site?
redirect handler
function handler(event) {
var request = event.request;
var uri = request.uri;
// Check whether the URI is missing a file name.
if (uri.endsWith('/')) {
request.uri += 'index.html';
}
// Check whether the URI is missing a file extension.
else if (!uri.includes('.')) {
request.uri += '/index.html';
}
return request;
}
cloudfront setup
myFunction = new Function(this, 'ViewerResponseFunction', {
functionName: 'RedirectURIFunction',
code: FunctionCode.fromFile({filePath: myFilePath}).render(),
comment: "Comment about the function"
});
originConfigs: [
{
s3OriginSource: {
s3BucketSource: myBucket,
originAccessIdentity: myOAI,
},
behaviors: [{
functionAssociations: [{
function: myCfnFunction,
eventType: FunctionEventType.VIEWER_RESPONSE
}],
isDefaultBehavior: true
}]
]}

From the restrictions page of Lambda#Edge
The Lambda function must be in the US East (N. Virginia) Region.
Your code will be still executed in the closest in the closest Edge location to the user, but the function itself must be located in us-east-1.
Based on your usecase (which seems to be a simple url redirect) you might want to consider using the newer CloudFront Functions feature, which is faster and more lightweight. This documentation page has a good comparison table.
Edit:
I haven't used CloudFront functions before, but looking at the CDK documentation and your link, I can suggest a few changes.
myFunction = new Function(this, 'ViewerResponseFunction', {
functionName: 'RedirectURIFunction',
code: FunctionCode.fromFile({filePath: myFilePath}).render(),
comment: "Comment about the function"
});
originConfigs: [
{
s3OriginSource: {
s3BucketSource: myBucket,
originAccessIdentity: myOAI,
},
behaviors: [{
functionAssociations: [{
function: myFunction,
eventType: FunctionEventType.VIEWER_REQUEST
}],
isDefaultBehavior: true
}]
]}
The Function Association interface only supports the L2 construct Function and not CfnFunction.
The GitHub sample you linked mentions Viewer Request.

Related

Writing Node business logic behind openapi-generator-cli nodejs-express-server

Currently we are doing the following:
Using an openapitools.json that looks like the following:
{
"$schema": "node_modules/#openapitools/openapi-generator-cli/config.schema.json",
"spaces": 2,
"generator-cli": {
"version": "5.1.1",
"generators": {
"node_v1.0": {
"generatorName": "nodejs-express-server",
"output": "#{cwd}/api/gen/v1.0/#{name}",
"glob": "openapi/*.yaml"
}
}
}
}
And we run a command:
npx openapi-generator-cli generate
Which creates this gen dir which structure like:
- api
- controllers
- services
- utils
etc.
Currently, our solution for business and database logic is to go to the controllers/DefaultController.js and change the require link for Service to point to our actual src/business_logic/Service.js code. In our Service.js code we call other logic in the endpoints like for example:
static getMaterials() {
const payload = MaterialLogic.materialFetchAll();
const code = 200;
return { payload, code };
}
For reference, the generated code look like this, but you can see there is no logic:
/**
* Returns list of materials
*
* returns List
* */
const getMaterials = () => new Promise(
async (resolve, reject) => {
try {
resolve(Service.successResponse({
}));
} catch (e) {
reject(Service.rejectResponse(
e.message || 'Invalid input',
e.status || 405,
));
}
},
);
The OpenAPI spec for this looks like:
/materials:
get:
operationId: getMaterials
description: Returns list of materials
responses:
200:
description: list of Materials
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/Material'
401:
description: not authorized to use this endpoint
It's obviously the wrong approach for us to have to change any code in gen to make our application work as it needs to be overridden and generated at any time (hence the whole point of using openapi gen tools). What's the correct way to do this? In our openapi spec is there a way to specify a different Service.js to use? Or is there some kind of dependency injection we are missing to get our business/database logic in there? Hopefully our problem is clear enough.

Getting coordinates from Yandex Map API Geocoder via multi-geocoder in NodeJs

I need to get coordidinates of set of addresses to display them on Yandex Map widget. There a lot of addresses, so I am going to get coordinates on the nodejs serverside. I have found package multi-geocoder, that looks exactly the solution for me. So I've written the example:
import MultiGeocoder from "multi-geocoder"
const geocoder = new MultiGeocoder({
provider: 'yandex',
coordorder: 'latlong',
lang: 'ru-RU',
apikey: 'My API key from https://developer.tech.yandex.ru/'
});
geocoder.geocode(['Москва'], {
apikey: 'My API key from https://developer.tech.yandex.ru/'
})
.then(function (res) {
console.log(res);
});
I got the response:
{
result: { type: 'FeatureCollection', features: [] },
errors: [ { request: 'Москва', index: 0, reason: 'Forbidden' } ]
}
I assume that something went wrong with apiKey, but cant figure out what exactly. How to get coordinates properly from nodejs script? Is it possible\legal at all?
Thank you.
If you have any problems with the API key, you should contact Yandex. Maps support. The problem may be with the key itself, or with your IP/domain. Only Yandex can determine the exact cause.
If you need to add points from the geocoder to the map, then it is easier to use geocoding in the JS API. It is enough to simply sequentially process the elements of the address array:
var searchArr = ['Москва, Фадеева, 5','Москва, Трубная, 31','Москва, Маросейка, 11'];
searchArr.forEach(function(item) {
ymaps.geocode(item, {
results: 1
}).then(function (res) {
var firstGeoObject = res.geoObjects.get(0),
coords = firstGeoObject.geometry.getCoordinates();
myMap.geoObjects.add(firstGeoObject);
});
});
If you display the data received from the data on a Yandex map and follow the other terms of use, the Yandex.Map API is available for free.
If at least one of the conditions must be violated, you should switch to a commercial license. Check out the fees here: https://tech.yandex.com/maps/tariffs/doc/jsapi/prices/index-docpage/

Google Cloud HTTP function by webhook: Request body is missing data

Im integrating the Zoom API with my Firebase app, and to do so I'm relying on Zooms pre-made webhooks to run an HTTP function on my Firebase for various events like "meeting started" and "meeting ended". Zoom API reference: https://marketplace.zoom.us/docs/guides/webhooks
This is the Google Cloud function that the Zoom API is calling:
exports.zoomTestA = functions.https.onCall((req, res) => {
console.log(req);
let data = req.body;
var xmlData = data.toString();
console.log(xmlData);
});
Here is the payload sent by Zoom:
{
"event": "meeting.ended",
"payload": {
"account_id": "LTf-KjgUTR2df-knT8BVEw",
"object": {
"duration": 0,
"start_time": "2019-05-07T14:02:51Z",
"timezone": "",
"topic": "Alexander Zoom Meeting",
"id": "864370042",
"type": 1,
"uuid": "2h/SWVrrQMu7fcbpLUly3g==",
"host_id": "Ty6ykNolSU2k1N4oc0NRcQ"
}
}
This causes this error to appear in my Google Cloud console:
Request body is missing data. { event: 'meeting.ended',
payload:
{ account_id: 'LTf-KjgUTR2df-knT8BVEw',
object:
{ duration: 0,
start_time: '2019-04-30T14:23:44Z',
timezone: '',
topic: 'Alexander\'s Zoom Meeting',
id: '837578313',
type: 1,
uuid: 'WotbHO3RRpSviETStKEGYA==',
host_id: 'Ty6ykNolSU2k1N4oc0NRcQ' } } }
The request body that Zoom sends is not wrapped in in a "data: {}" tag as required by Google Cloud functions. I've found solutions to this problem if you can control the payload here: Dart json.encode is not encoding as needed by Firebase Function .
My problem is that I am unable to alter the request that the Zoom API sends. Is there any way I can still accept the request in my Google Cloud function? Or is there any way to alter the format of the request sent by Zoom? I'm unable to find references for either.
One potential solution would be to set up another server that receives the request by Zoom, format it to Google Cloud functions specifications, and then pass it on to my Google Cloud function. I would however like to avoid stepping out of the Google Cloud eco-system.
Is this problem solvable on the Google Cloud platform?
So I figured it out. In Firebase / Google Cloud functions you can either make HTTP-functions with
functions.https.onCall((req, res) => {
var data = req.body;
and
functions.https.onRequest((req, res) => {
var data = req.body;
The difference seems to be that onCall is made for being used within the Firebase/ Google Cloud functions environment. However if you wan external functions you need to use onRequest as this does not require specific formatting of the payload.
Using onRequest instead solved all my problems.

loopback remote method return variable other than request data

I have a generic SendMail route which I want to create multiple remote methods to handle multiple request templates. Any ideas on how to return a Email_Type from the remote method back to the base route. I know I could add a default with a code in it, but would like a more elegant solution.
Mail.genericSendMail = function genericEmail(response, callback) {
console.log(response);
let templateId=0;
//PROBLEM: HOW TO KNOW WHICH REMOTE WAS USED
switch (response.emailType) {
case "Template-1":
templateId= 1234;
break;
case "Template-2":
tempalteId = 456;
break;
default:
templateId = 789l
} //switch
console.log(templateId);
};
//Want multiple routes like this to support various templates
Mail.remoteMethod("genericEmail", {
http: {
path: "/emailTemplate1",
verb: "POST"
},
accepts [
{arg: "request", type:"object",http: {source:"body"},
default: {firstName:"", lastName:"",emailAddress:""}
}],
returns: RESTResponseStatic.loopbackAdapterCommonRestResponseDefinition()
});
//Want multiple routes like this to support various templates
Mail.remoteMethod("genericEmail", {
http: {
path: "/emailTemplate2",
verb: "POST"
},
accepts [
{arg: "request", type:"object",http: {source:"body"},
default: {emailAddress:"", promoCode:""}
}],
returns: RESTResponseStatic.loopbackAdapterCommonRestResponseDefinition()
});
There are a couple of different ways to do this. Since it happens to be a POST request, I usually go with attaching data to the body using a before remote hook.
Let's say you have a model method for logging in users.
Say we have a multi realm platform, so we need to know what platform we are logging in. If you don't use realms or don't know what they are, don't worry. This just shows you how to populate the data to the model method.
User.login = function(data, cb) {
if (data.realm == 'platform1) {
return logUserIntoPlatform1(data, cb);
}
return logUserIntoDefaultPlatform(data, cb);
}
Now let's say you don't want the client/frontend to send the realm and you don't want to do the lookup for realm in the model. We can add a beforeRemote hook like so:
User.beforeRemote('login', function (context, user, next) {
context.args.data.realm = lookUpRealmSync(context); // 1
next();
});
This will be called before the login method. Note the next() call: this is how you could do error detection before actually hitting the model method. Something like next({ status: 422, message: 'Parameter missing: password }); would return an error and not execute the User.login method.
You may have to look carefully at your context object (i.e. the line marked with 1 may not work exactly as I've shown for you).
If you want to read more about this stuff, I LoopBack's docs are pretty good. It seems they've been updated since I've last used them so I can't link you to the more useful pages. I found the remote method documentation here though.
Edit: I took a closer look at your question. You should be able to retrieve the path from the context object and pass data accordingly. I'm not going to try to code that since I don't know where it would actually be within the object.

Google+ insert moment with nodejs client

Has anyone been able to get the google-api-nodejs-client to successfully insert a moment?
Whatever I try, I get a generic 400 "Invalid value" error but am unable to narrow down the invalid value because the API Explorer doesn't work either.
Would it be because of the missing data-requestvisibleactions parameter? I'm using passport.js's require('passport-google-oauth').OAuth2Strategy for handling oauth access, and that part is working fine, but I have no idea how to incorporate requestvisibleactions into the oauth request flow since this is definitely not originating from a clientside form.
Here's a snippet of what I'm trying to do (using the latest version of googleapis, v1.0.2):
var google = require('googleapis')
var auth = new google.auth.OAuth2()
auth.setCredentials({
'access_token': user.token
})
google.plus('v1').moments.insert({
collection: 'vault',
userId: 'me',
debug: true,
resource: {
type: "http://schemas.google.com/AddActivity",
target: {
type: "http://schema.org/CreativeWork",
url: "...omitted...",
image: "...omitted...",
description: "test",
name: "test"
}
},
auth: auth
}, function (err, response) {
if (err) {
console.error(err)
res.send(err.code, err)
} else {
console.log(response)
res.send(200)
}
})
ref 1 (out-of-date w.r.t. an older version of googleapis)
ref 2 (client-side, where the use of data-requestvisibleactions is more obvious)
As you speculated, you need the request_visible_actions parameter as part of the URL calling the oauth endpoint.
It looks like the current version of passport-google-oauth doesn't support this parameter. Judging by several of the open issues and pull requests, it isn't clear that the author will respond to requests to add it either. You have two possible options:
Switch to using the OAuth support that is included in google-api-nodejs-client
Patch the passport-google-oauth code. (And possibly submit a pull request in the hopes it will be useful to someone else.)
I don't use passport.js or the passport module in question, so I can't test this, but based on the github repository, I think you can insert the following in lib/passport-google-oauth/oauth2.js after line 136 and before the return statement:
if (options.requestVisibleActions) {
// Space separated list of allowed app actions
// as documented at:
// https://developers.google.com/+/web/app-activities/#writing_an_app_activity_using_the_google_apis_client_libraries
// https://developers.google.com/+/api/moment-types/
params['request_visible_actions'] = options.requestVisibleActions;
}

Resources