5
votes

I am following Google's tutorial on setting up Google Cloud endpoint (not AWS API Gateway) in front of my Cloud Function. I am triggering my Cloud Function to trigger an AWS lambda function, AND I am trying to pass a path parameter from my Endpoint as defined by OpenAPI spec.

Path parameters are variable parts of a URL path. They are typically used to point to a specific resource within a collection, such as a user identified by ID. A URL can have several path parameters, each denoted with curly braces { }.

paths:   /users/{id}:
    get:
      parameters:
        - in: path
          name: id   # Note the name is the same as in the path
          required: true
          schema: 
            type: integer

GET /users/{id}

My openapi.yaml

swagger: '2.0'
info:
  title: Cloud Endpoints + GCF
  description: Sample API on Cloud Endpoints with a Google Cloud Functions backend
  version: 1.0.0
host: HOST
x-google-endpoints:
- name: "HOST"
  allowCors: "true
schemes:
  - https
produces:
  - application/json
paths:
  /function1/{pathParameters}:
    get:
      operationId: function1
      parameters:
      - in: path
        name: pathParameters
        required: true
        type: string
      x-google-backend:
        address: https://REGION-FUNCTIONS_PROJECT_ID.cloudfunctions.net/function1
      responses:
        '200':
          description: A successful response
          schema:
            type: string

The error I get when I use Endpoint URL https://REGION-FUNCTIONS_PROJECT_ID.cloudfunctions.net/function1/conversations is a TypeError from my AWS lambda function

StatusCode:200, FunctionError: "Unhandled", ExecutedVersion: "$LATEST". Payload: "errorType":"TypeError", errorMessage:"Cannot read property 'startsWith' of undefined..."

It is saying that on line

var path = event.pathParameters;
...
...
if (path.startsWith('conversations/'){...};

my path var is undefined.

I initially thought my Google Function was not correctly passing pathParameters but when I tested my Google function using triggering event {"pathParameters":"conversations"}, my Lambda returns the payload successfully.

My Google Cloud Function:

let AWS = require('aws-sdk');

AWS.config.update({
  accessKeyId: 'key',
  secretAccessKey: 'secret',
  region: 'region'
})

let lambda = new AWS.Lambda();

exports.helloWorld = async(req,res) => {
  let params = {
     FunctionName:'lambdafunction',
     InvocationType: "RequestRespone",
     Payload: JSON.stringify(req.body)
  };

  res.status(200).send(await lambda.invoke(params, function(err,data){
      if(err){throw err}
      else{
         return data.Payload
      }
   }).promise());
}

EDIT 1:

Seeing this Google Group post, I tried adding to my openapi.yaml file path_translation: APPEND_PATH_TO_ADDRESS, yet still I'm having no luck.

...
paths:
  /{pathParameters}:
    get:
     ...
     x-google-backend:
       address: https://tomy.cloudfunctions.net/function-Name
       path_translation: APPEND_PATH_TO_ADDRESS

@Arunmainthan Kamalanathan mentioned in the comments that testing in AWS and Google Cloud directly with trigger event {"pathParameters":"conversations"} is not equivalent to passing req.body from my Google function to AWS lambda. I think this is where my error is occurring -- I'm not correctly passing my path parameter in the payload.

EDIT 2:

There is this Stackoverflow post concerning passing route parameters to Cloud Functions using req.path. When I console.log(req.path) I get / and console.log(req.params) I get {'0': '' }, so for some reason my path parameter is not getting passed correctly from Cloud Endpoint URL to my Google function.

2
I think event.pathParameters is the way of identifying the parameters in the path. can you try using another parameter name in your openapi specification. - Arun K
The reason why your lambda work when you trigger it directly is , you are passing the values directly to the lambda's event. when you have a parameter defined in the path, it can be accessible from the lambda as event.pathParameters. But in your case pathParameters is the parameter name. try changing the parameter name and see if it's working. - Arun K
For lambda, its not either of those. you can pass a json body , query string or headers and test the lambda. I haven't use google functions. - Arun K

2 Answers

4
votes

I was running into the same issue when specifying multiple paths/routes within my openapi.yaml. It all depends on where you place the x-google-backend (top-level versus operation-level). This has implications on the behaviour of the path_translation. You could also overwrite path_translation yourself, as the documentation clearly describes:

path_translation: [ APPEND_PATH_TO_ADDRESS | CONSTANT_ADDRESS ]

Optional. Sets the path translation strategy used by ESP when making target backend requests.

Note: When x-google-backend is used at the top level of the OpenAPI specification, path_translation defaults to APPEND_PATH_TO_ADDRESS, and when x-google-backend is used at the operation level of the OpenAPI specification, path_translation defaults to CONSTANT_ADDRESS. For more details on path translation, please see the Understanding path translation section.

Example 1: Do you have one cloud function with multiple paths? Put x-google-backend at the top-level.

Example 2: Do you have multiple cloud functions corresponding to different paths? Put x-google-backend at the operation-level, so you can configure different targets.

0
votes

When your invocation type is RequestRespone, you can access the payload directly from the event parameter of lambda.

Look at the `Payload' parameter of the Google function:

let params = {
   FunctionName:'lambdafunction',
   InvocationType: "RequestRespone",
   Payload: JSON.stringify({ name: 'Arun'})
};

res.status(200).send(await lambda.invoke(params)...)

Also the Lambda part:

exports.handler = function(event, context) {
  context.succeed('Hello ' + event.name);
};

I hope this helps.