Why is AWS SAM giving me random DynamoDB table names - python-3.x

When i use SAM to create my Lambda,API gateway and DynamoDB table it all works until i get to the actual created table. It is supposed to be called "List" however after the word "list" it gives me a bunch of random numbers and letters.
What i want to do is when all 3 of the services are created they should talk to eachother however since i am getting this problem, I have to manually add the name to my function for it to work.
AWSTemplateFormatVersion: '2010-09-09'
Transform: 'AWS::Serverless-2016-10-31'
Resources:
ClickSam:
Type: 'AWS::Serverless::Function'
Properties:
Handler: app.lambda_handler
Runtime: python3.8
CodeUri: Lambda/
MemorySize: 128
Timeout: 3
Environment:
Variables:
TABLE_NAME: !Ref List
REGION_NAME: !Ref AWS::Region
Events:
ClickAPI:
Type: Api
Properties:
Path: /visits
Method: GET
Policies:
- DynamoDBCrudPolicy:
TableName: !Ref List
List:
Type: AWS::Serverless::SimpleTable
Properties:
PrimaryKey:
Name: url
Type: String
---------- This is my Lambda Function code to create items for the table.
import boto3
import json
def lambda_handler(event, context):
dynamodb = boto3.client('dynamodb')
response = dynamodb.update_item(
TableName='List',
Key={
'url':{'S': "etc.com"}
},
UpdateExpression='ADD visits :inc',
ExpressionAttributeValues={
':inc': {'N': '1'}
},
ReturnValues="UPDATED_NEW"
)
response = {
'statusCode': 200,
'headers': {
"Content-Type" : "application/json",
"Access-Control-Allow-Origin" : "*",
"Allow" : "GET, OPTIONS, POST",
"Access-Control-Allow-Methods" : "GET, OPTIONS, POST",
"Access-Control-Allow-Headers" : "*"
},
'body': json.dumps(int(response["Attributes"]["visits"]["N"]))
}
return response

Use TableName parameter to fix the table name (cf. AWS::Serverless::SimpleTable).
Type: AWS::Serverless::SimpleTable
Properties:
TableName: MyTable
Documentation of the AWS::DynamoDB::Table resource :
If you don't specify a name, AWS CloudFormation generates a unique physical ID and uses that ID for the table name.

Related

`apiRevision` flag isn't working in Bicep template

I am using Bicep to deploy open api json into Azure API Management. The snippet looks like this.
resource fuseintegrationsapi 'Microsoft.ApiManagement/service/apis#2021-08-01' = {
name: '${apim.name}/integrations-api-${environment_name}'
properties: {
description: 'Contains integrations apis used to control the platform.'
type: 'http'
apiRevision: '1234'
isCurrent: true
subscriptionRequired: false
displayName: 'Integrations Api'
serviceUrl: '${api_backend_url}/api/test/v1/integrations'
path: '${environment_name}/api/test/v1/integrations'
protocols: [
protocol
]
value: api_link
format: 'openapi+json-link'
apiType: 'http'
}
dependsOn: [
api2
]
resource symbolicname 'policies' = {
name: 'policy'
properties: {
value: anonymous_operation_policy
format: 'rawxml'
}
}
}
Even though revision is hardcoded to 1234 it's always using default 1 and the API is not updating with latest open api specification.
I had the same problem and figured out that you have to put the revision in the name also.
name: '${apim.name}/integrations-api-${environment_name};rev=1234'

Is there any way to exclude null properties when using Groovy's YAMLBuilder?

I'm using Groovy to generate an openapi spec document in YAML. I'm using YamlBuilder to convert the object model to a YAML string.
It's been working well so far, but one issue I've noticed is that null properties are present in the YAML output. This is causing validation errors in the openapi validators I'm using, so I'd like to remove any null properties from the YAML output.
Is there any way to achieve this? I can't see it in the docs. The equivalent JSONBuilder allows config options to be set, is there such a thing for YamlBuilder?
The part of the script which generates the YAML looks like this:
def generateSpec() {
println "============================\nGenerating Customer SPI spec\n============================"
def components = generateComponents()
def paths = generatePaths()
def info = Info
.builder()
.title('Customer SPI')
.description('A customer API')
.version('0.1')
.build()
def customerSpec = [openapi: "3.0.3", components : components, info : info, paths : paths]
def yaml = new YamlBuilder()
yaml(customerSpec)
println(yaml.toString())
return yaml.toString()
}
Here's my current output. Note the null value of the format property on firstname, among others.
---
openapi: "3.0.3"
components:
schemas:
Customer:
type: "object"
properties:
firstname:
type: "string"
format: null
ArrayOfCustomers:
items:
$ref: "#/components/schemas/Customer"
info:
title: "Customer SPI"
version: "0.1"
description: "An API."
paths:
/customers:
parameters: null
get:
responses:
"200":
content:
application/json:
schema:
$ref: "#/components/schemas/ArrayOfCustomers"
description: "An array of customers matching the search criteria"
summary: "Search customers"
/customers/{customerRef}:
parameters:
- required: true
schema:
type: "string"
format: null
description: "Customer reference"
in: "path"
name: "customerRef"
get:
responses:
"200":
content:
application/json:
schema:
$ref: "#/components/schemas/Customer"
description: "A customer with the given reference"
summary: "Load a customer"

Unable to parse object to the event through API Gateway to get response from Lambda?

Iam new to the serverless development. Iam trying to create a simple rest api using aws Lambda and API Gateway.
I have following json in my s3 and i want to return the object based on request when the API is invoked
{
"customerA":
{"Age": "29", "Product": "Laptop"},
"customerB": {
"Age": "30",
"Product": "Mobile"
}
}
Below is the lambda function for which i have added the same API trigger
import json
import boto3
def customer(event, context):
# TODO implement
resource='s3'
s3=boto3.resource(resource)
s3Bucket='mys3'
bucketKey='customerDetails.json'
obj=s3.Object(s3Bucket,bucketKey)
body=obj.get()['Body'].read()
customer={}
customer=json.loads(body)
value={}
# customerCode is the parameter defined in your serverless.yml
customerCode = event['custCode']
return {
'statusCode': 200,
'body': json.dumps(customer[customerCode]),
'headers': {
"content-type": "application/json"
}
}
service: customer-function
provider:
name: aws
runtime: python3.8
region: eu-west-1
iamRoleStatements:
- Effect: "Allow"
Action:
- "s3:ListBucket"
Resource: "arn:aws:s3:::mys3"
- Effect: "Allow"
Action:
- "s3:PutObject"
- "s3:GetObject"
- "s3:DeleteObject"
Resource: "arn:aws:s3:::mys3/*"
functions:
customer_serverless:
handler: handler.customer
# The following are a few example events you can configure
# NOTE: Please make sure to change your handler code to work with those events
# Check the event documentation for details
events:
- http:
path: customer_resource/cust_content/
method: get
Expected: What iam looking for is, when i invoke the api with the request 'customerA', the api should return the object for the 'customerA' : '{"Age": "29", "Product": "Laptop"}'
So how to invoke the API with the request to get the above response as below
when i invoke api
it return all values if iam only calling
"body': json.dumps(customer)"
with below api
test-api.eu-west-1.amazonaws.com/customer_1/cust-details/
but when i invoke with below api
test-api.eu-west-1.amazonaws.com/customer_1/cust-details/customerA
it throw the key error
My expected result is
{"Age": "29", "Product": "Laptop"}
Can anyone help on this?
this should work
import json
import boto3
def hello(event, context):
# TODO implement
resource='s3'
s3=boto3.resource(resource)
s3Bucket='bucketname'
bucketKey='customer.json'
obj=s3.Object(s3Bucket,bucketKey)
body=obj.get()['Body'].read()
customer={}
customer=json.loads(body)
value={}
# customerCode is the parameter defined in your serverless.yml
customerCode = event['pathParameters']['customerCode']
return {
'statusCode': 200,
'body': json.dumps(customer[customerCode]),
'headers': {
"content-type": "application/json"
}
}
serverless.yml
service: customer-function
provider:
name: aws
runtime: python3.8
region: eu-west-1
iamRoleStatements:
- Effect: "Allow"
Action:
- "s3:ListBucket"
Resource: "arn:aws:s3:::bucketname"
- Effect: "Allow"
Action:
- "s3:PutObject"
- "s3:GetObject"
- "s3:DeleteObject"
Resource: "arn:aws:s3:::bucketname/*"
functions:
hello:
handler: handler.hello
# The following are a few example events you can configure
# NOTE: Please make sure to change your handler code to work with those events
# Check the event documentation for details
events:
- http:
path: customer_1/cust-details/{customerCode}
method: get
hope this helps
You have configured API Gateway as Lambda Proxy. When you make this configuration, a convention is in place that force your Lambda function to return a JSON object like this:
{
statusCode: 200,
body: "{\"Age\": \"29\", \"Product\": \"Laptop\"}",
headers: {
"content-type": "application/json"
}
}

how to pass integer in yaml through csv file in artillery load testing

I am using post request With JSON Object:
{
"name": "Emmy",
"age": 11,
"state": "Goa",
"country": "india"
}
my CSV file is :
name,age,state,country
Emmy,11,Goa,india
and my artilery code :
config:
target: 'http://localhost:5000'
phases:
- duration: 60
arrivalRate: 10
defaults:
headers:
token: 'TOKEN'
payload:
path: "./hello.csv"
fields:
- "name"
- "age"
- "state"
- "country"
scenarios:
- flow:
- post:
url: "url"
json:
name: "{{name}}"
age: "{{age}}"
state: "{{state}}"
country: "{{country}}"
I have validation for each field in which age will take only integer values but artillery is taking string values so i an getting validation error. how to pass age as integer in yaml file.
If you don't find any other solution, you can run custom code :
function setJSONBody(requestParams, context, ee, next) {
return next(); // MUST be called for the scenario to continue
}
doc : https://artillery.io/docs/http-reference/#advanced-writing-custom-logic-in-javascript
To explicitly load a string enclosed in quotes as an integer in YAML, you need to add the !!int tag:
---
integer: !!int "{{ variable }}"
It is needed in this case, because a plain scalar cannot start with { as this is starting a flow style mapping.
However, this won't work if the substitution of the {{ var }} happens after loading and resolving the tag, because then it would try to resolve the verbatim string {{ var }} as an integer which will fail.
Here's an article about Tags/Schemas/Types in YAML 1.1 and 1.2 I wrote in December which might help understanding:
Introduction to YAML Schemas and Tags

AWS Serverless - Auto Create DynamoDB

In my serverless.yml file, I have specified a DynamoDB resource, something to this effect (see below). I'd like to know two things:
Why is it that I'm not seeing these tables get created when they don't exist, forcing me to manually enter AWS console and do so myself?
In my source code (nodejs), i'm not sure how I'd reference a table specified in the yml file instead of hardcoding it.
The two questions above roll up into a singular problem, which is that I'd like to be able to specify the tables in the yml and then when doing a "deploy", have a different table set created per environment.
i.e. If I deploy to "--stage Prod", then table would be "MyTable_Prod". If I deploy to "--stage Dev", then table would be "MyTable_Dev", etc...
Figuring this out would go a long way to making deployments much smoother :).
The serverless.yml section of interest is as follows:
resources:
Resources:
DynamoDbTable:
Type: AWS::DynamoDB::Table
Properties:
TableName: MyHappyFunTable
AttributeDefinitions:
- AttributeName: id
AttributeType: S
KeySchema:
- AttributeName: id
KeyType: HASH
ProvisionedThroughput:
ReadCapacityUnits: 5
WriteCapacityUnits: 5
DynamoDBIamPolicy:
Type: AWS::IAM::Policy
DependsOn: DynamoDbTable
Properties:
PolicyName: lambda-dynamodb
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- dynamodb:Query
- dynamodb:Scan
- dynamodb:GetItem
- dynamodb:PutItem
- dynamodb:UpdateItem
- dynamodb:DeleteItem
Resource: "arn:aws:dynamodb:${opt:region, self:provider.region}:*:table/${self:provider.environment.DYNAMODB_TABLE}"
Roles:
- Ref: IamRoleLambdaExecution
A sample of my horrid 'hardcoded' table names is as follows:
dbParms = {
TableName : "MyTable_Dev",
FilterExpression: "#tid = :tid and #owner = :owner",
ProjectionExpression: "#id, #name",
ExpressionAttributeNames: {
"#tid" : "tenantid",
"#id" : "id",
"#name" : "name",
"#owner" : "owner"
},
ExpressionAttributeValues: {
":tid": tenantId,
":owner": owner
}
};
Note the "MyTable_Dev" ... ideally i'd like that to be something like "MyTable_"
+ {$opt.stage} ... or something to that effect.
In my source code (nodejs), i'm not sure how I'd reference a table specified in the yml file instead of hardcoding it.
I would put your stage in an environment variable that your Lambda function has access to.
In your serverless.yml,
provider:
...
environment:
STAGE: {$opt:stage}
Then, in your code you can access it through process.env.STAGE.
const tableName = 'MyTable_' + process.env.STAGE

Resources