How can i use multiple path parameters from serverless framework - aws-lambda

I'm trying to deploy my serverless app.
But have a problem like below.
An error occurred: ApiGatewayResourceServicesServiceidVar - A sibling ({id}) of this resource already has a variable path part -- only one is allowed
And below is my code.
updateApplication:
handler: handler.updateApplication
memorySize: 3008
description: Update application
timeout: 30
events:
- http:
path: services/{serviceId}/applications/{applicationId}
method: post
cors: true
authorizer: authorize
request:
parameters:
paths:
serviceId: true
applicationId: true
Any advice or suggestion would be appreciated. Thank you in advance.

The serverless framework seems to be complaining that you have defined the path parameters twice. Since you have declared it there directly below -http: you can remove the request: parameters: paths: block.
In other words, try this:
updateApplication:
handler: handler.updateApplication
memorySize: 3008
description: Update application
timeout: 30
events:
- http:
path: services/{serviceId}/applications/{applicationId}
method: post
cors: true
authorizer: authorize
Happy coding! 👍

Related

Serverless Error TypeError: Cannot read property 'toLowerCase' of undefined

I tried to deploy something like this example from serverless. Building my serverless.yml, I run into this error, of which I don't find a handle to deal with:
service: products-api
package:
artifact: target\products-api-dev.jar
#artifact: target\${self:service}-${self:provider.stage}.jar #cool alternative :)
provider:
name: aws
runtime: java8
#Copy-pasted
resources:
Resources:
productsTable:
Type: AWS::DynamoDB::Table
Properties:
TableName: products_table
AttributeDefinitions:
- AttributeName: id
AttributeType: S
- AttributeName: name
AttributeType: S
KeySchema:
- AttributeName: id
KeyType: HASH
- AttributeName: name
KeyType: RANGE
ProvisionedThroughput:
ReadCapacityUnits: 1
WriteCapacityUnits: 1
functions:
listProducts:
handler: com.serverless.ListProductsHandler
events:
- http:
path: /products
method: get
getProduct:
handler: com.serverless.GetProductHandler
events:
- http:
path: /products/{id}
method: get
createProduct:
handler: com.serverless.CreateProductHandler
events:
- http:
path: /products
method: post
deleteProduct:
handler: com.serverless.DeleteProductHandler
events:
- http:
path: /products/{id}
The error I get is this - not having a reference to a mistake in my own code makes difficult to spot where I went wrong.
I looked into many Q/A including this and this, but it seems to be more of a javascript and typescript problem there, not serverless as here.
Type Error ----------------------------------------------
TypeError: Cannot read property 'toLowerCase' of undefined
at AwsCompileApigEvents.getHttpMethod (C:\snapshot\serverless\lib\plugins\aws\package\compile\events\apiGateway\lib\validate.js:195:24)
at C:\snapshot\serverless\lib\plugins\aws\package\compile\events\apiGateway\lib\validate.js:50:30
at Array.forEach (<anonymous>)
at C:\snapshot\serverless\lib\plugins\aws\package\compile\events\apiGateway\lib\validate.js:45:37
at Array.forEach (<anonymous>)
at AwsCompileApigEvents.validate (C:\snapshot\serverless\lib\plugins\aws\package\compile\events\apiGateway\lib\validate.js:44:55)
at Object.package:compileEvents [as hook] (C:\snapshot\serverless\lib\plugins\aws\package\compile\events\apiGateway\index.js:318:31)
at PluginManager.invoke (C:\snapshot\serverless\lib\classes\PluginManager.js:579:20)
at async PluginManager.spawn (C:\snapshot\serverless\lib\classes\PluginManager.js:601:5)
at async Object.before:deploy:deploy [as hook] (C:\snapshot\serverless\lib\plugins\deploy.js:60:11)
at async PluginManager.invoke (C:\snapshot\serverless\lib\classes\PluginManager.js:579:9)
at async PluginManager.run (C:\snapshot\serverless\lib\classes\PluginManager.js:639:7)
at async Serverless.run (C:\snapshot\serverless\lib\Serverless.js:452:5)
at async C:\snapshot\serverless\scripts\serverless.js:751:9
For debugging logs, run again after setting the "SLS_DEBUG=*" environment variable.
andymac4182 has the Idea for the solution for your problem and it is actually pretty easy. You have to define a method for each http element, and for the last one, this is missing: method: post
Updated:
deleteProduct:
handler: com.serverless.DeleteProductHandler
events:
- http:
path: /products/{id}
method: post
Serverless is a javascript project, which is why you're seeing a javascript error.
It appears one of your functions is an HTTP endpoint, but lacks any methods. That seems to be the cause of the error.
Try adding method: DELETE to your endpoint:
deleteProduct:
handler: com.serverless.DeleteProductHandler
events:
- http:
path: /products/{id}
method: DELETE

How dynamically add arn cognito to lambda?

I would like to add the cognito authorizer to my lambda function, but for this I need arn cognito, which is created in the stack coud formation(CognitoUserPool in my resources section). I'm using serverless framework.
part of the serverless.yml file
graphql:
handler: src/lambda-functions/graphql/index.handler
timeout: 30
memorySize: 2048
events:
- http:
path: graphql
method: any
private: true
cors: true
authorizer:
arn:
Fn::Join:
- ''
- - 'arn:aws:cognito-idp:'
- ${self:provider.region}
- ':'
- Ref: AWS::AccountId
- ':userpool/'
- Ref: CognitoUserPool
I am getting an error while deploying the application:
TypeError: functionArn.split is not a function
While debugging, I discovered that the function to which the output from Fn::join should be passed is object:
{"Fn::Join":["",["arn:aws:cognito-idp:","eu-west-1",":",{"Ref":"AWS::AccountId"},":userpool/",{"Ref":"CognitoUserPool"}]]}
And there should be passed to the function already resolve arn for example:
arn:aws:cognito-idp:eu-west-1:XXXXXXXXXX:userpool/eu-west-XXXXXXX
How to force that the output from Fn::join to be computed and pass that value to the arn property?
According to this, giving the authorizer a name lets you use intrinsic functions to refer to the ARN:
graphql:
handler: src/lambda-functions/graphql/index.handler
timeout: 30
memorySize: 2048
events:
- http:
path: graphql
method: any
private: true
cors: true
authorizer:
name: CognitoAuthorizer #supply a unique name here
arn:
!GetAtt CognitoUserPool.Arn

Call service from existing api gateway using base path mappings

Our API has the following endpoints:
POST /users - create a user
GET /users/{userId} - get a particular user
GET /posts/{postId} - get a particular post
GET /posts/{postId}/users - get the users who contributed to this post
I have defined two services: users-service and posts-service. In these two services I define the lambdas like so. I'm using the serverless-domain-manager plugin to create base path mappings:
/users-service/serverless.yaml:
service: users-service
provider:
name: aws
runtime: nodejs10.x
stage: dev
plugins:
- serverless-domain-manager
custom:
customDomain:
domainName: 'serverlesstesting.example.com'
basePath: 'users'
stage: ${self:provider.stage}
createRoute53Record: true
functions:
create:
name: userCreate
handler: src/create.handler
events:
- http:
path: /
method: post
get:
name: userGet
handler: src/get.handler
events:
- http:
path: /{userId}
method: get
/rooms-service/serverless.yaml:
service: posts-service
provider:
name: aws
runtime: nodejs10.x
stage: dev
plugins:
- serverless-domain-manager
custom:
customDomain:
domainName: 'serverlesstesting.example.com'
basePath: 'posts'
stage: ${self:provider.stage}
createRoute53Record: true
functions:
get:
name: postsGet
handler: src/get.handler
events:
- http:
path: /{postId}
method: get
getUsersForPost:
handler: userGet ?
events: ??
The problem is that the GET /posts/{postId}/users actually calls the same userGet lambda from the users-service. But the source for that lambda lives in the users-service, not the posts-service.
So my question becomes:
How do I reference a service from another service using base path mappings? In other words, is it possible for the posts service to actually make a call to the parent custom domain and into the users base path mapping and its service?
Consider or refer below approach
https://serverless-stack.com/chapters/share-an-api-endpoint-between-services.html

How to use an external layer with the Serverless Framework?

I would like to use an external layer arn:aws:lambda:eu-central-1:347034527139:layer:tf_keras_pillow:1 in my Serverless project.
I do so by having the following in my serverless.yml:
functions:
api:
handler: functions/api/handler.run
layers: arn:aws:lambda:eu-central-1:347034527139:layer:tf_keras_pillow:1
events:
- http:
path: /image/{id}/{mode}
method: get
request:
parameters:
paths:
id: true
mode: true
However, when checking the AWS Lambda function in the console, there is no layer added after deployment. Any ideas?
The only way to add the layer is by manually doing so in the GUI.
The layers value is an array, per the documentation: https://serverless.com/framework/docs/providers/aws/guide/layers#using-your-layers.
functions:
api:
handler: functions/api/handler.run
layers:
- arn:aws:lambda:eu-central-1:347034527139:layer:tf_keras_pillow:1
events:
- http:
path: /image/{id}/{mode}
method: get
request:
parameters:
paths:
id: true
mode: true
Should work.

Exclude Lambda function from deploy to a particular stage

I am trying to exclude a Lambda function from being deployed via serverless to my prod stage within AWS.
A snippet from my serverless yaml looks something like -
functions:
some-prod-function:
handler: prodFunction.handler
events:
- http:
path: /prod-function
method: post
some-dev-function:
handler: devFunction.handler
events:
- http:
path: /dev-function
method: post
Is there a way to exclude some-dev-function from being deployed to prod?
You can put those definitions on a different property and use variables in order to choose which definitions to use.
environment-functions:
prod:
some-prod-function:
handler: prodFunction.handler
events:
- http:
path: /prod-function
method: post
dev:
some-dev-function:
handler: devFunction.handler
events:
- http:
path: /dev-function
method: post
functions: ${self:environment-functions.${opt:stage}}
You may need to change this depending on how you specify your stage on deployment (${opt:stage} or ${env:stage}).
I'm using SLS 1.32.0
I wasn't able to get functions: ${self:environment-functions.${opt:stage}} to work. (Not sure why)
It returns the following:
A valid service attribute to satisfy the declaration 'self:environment-functions.dev' could not be found.
However, using the same logic in dashmug's answer, file worked for me:
serverless.yml:
functions: ${file(serverless-${opt:stage}.yml)}
serverless-dev.yml:
some-dev-function:
handler: devFunction.handler
events:
- http:
path: /dev-function
method: post
serverless-prod.yml:
some-prod-function:
handler: prodFunction.handler
events:
- http:
path: /prod-function
method: post
If you are using Serverless framework you could use serverless plugin
serverless-plugin-ifelse
Then
plugins:
- serverless-plugin-ifelse
If you want to exclude say func1
functions:
func1:
name: Function 1
handler: lambda.func1
events:
- http:
path: "path1"
method: "post"
authorizer:
arn: arn:aws:cognito-idp:us-east-1:123456789012:userpool/us-east-1_0acCDefgh
func2:
name: Function 2
handler: lambda.func2
events:
- http:
path: "path2"
method: "post"
func3:
name: Function 3
handler: lambda.func2
events:
- http:
path: "path3"
method: "post"
for us-east-1 . Then use below code snippet
- If: '"${self:provider.region}" == "us-east-1"'
Exclude:
- functions.func1
Create a file for example
env-functions.yml
and add content as below
prod:
some-prod-function:
handler: prodFunction.handler
events:
- http:
path: /prod-function
method: post
dev:
some-dev-function:
handler: devFunction.handler
events:
- http:
path: /dev-function
method: post
After this in serverless.yml set
functions: ${file(env-functions.yml):${opt:stage, self:provider.stage}}

Resources