15、Serverless Movie API: Development, Testing, and Security

Serverless Movie API: Development, Testing, and Security

1. Frontend Lambda Function Implementations

1.1 Filter by Movie Category

To implement a Lambda function that takes a movie category as input and returns a list of movies corresponding to that category, use the following code:

func filter(category string)(events.APIGatewayProxyResponse, error) {
    ...
    filter := expression.Name("category").Equal(expression.Value(category))
    projection := expression.NamesList(expression.Name("id"), expression.Name("name"), 
expression.Name("description"))
    expr, err := expression.NewBuilder().WithFilter(filter).WithProjection(projection).Build()
    if err != nil {
        return events.APIGatewayProxyResponse {
            StatusCode: http.StatusInternalServerError,
            Body: "Error while building DynamoDB expression",
        }, nil
    }
    svc := dynamodb.New(cfg)
    req := svc.ScanRequest(&dynamodb.ScanInput {
        TableName: aws.String(os.Getenv("TABLE_NAME")),
        ExpressionAttributeNames: expr.Names(),
        ExpressionAttributeValues: expr.Values(),
        FilterExpression: expr.Filter(),
        ProjectionExpression: expr.Projection(),
    })
    ...
}

1.2 Filter by Movie Title

To implement a Lambda function that takes a movie’s title as input and returns all movies with the keyword in their title, use the following code:

func filter(keyword string) (events.APIGatewayProxyResponse, error) {
  ...
  filter := expression.Name("name").Contains(keyword)
  projection := expression.NamesList(expression.Name("id"), expression.Name("name"), 
expression.Name("description"))
  expr, err := expression.NewBuilder().WithFilter(filter).WithProjection(projection).Build()
  if err != nil {
    return events.APIGatewayProxyResponse{
      StatusCode: http.StatusInternalServerError,
      Body: "Error while building DynamoDB expression",
    }, nil
  }
  svc := dynamodb.New(cfg)
  req := svc.ScanRequest(&dynamodb.ScanInput{
    TableName: aws.String(os.Getenv("TABLE_NAME")),
    ExpressionAttributeNames: expr.Names(),
    ExpressionAttributeValues: expr.Values(),
    FilterExpression: expr.Filter(),
    ProjectionExpression: expr.Projection(),
  })
  ... 
}

1.3 Delete and Edit Movie Buttons

  • Delete Button : Update the MoviesAPI service to include the following function to implement a delete button on the web application to delete a movie by calling the DeleteMovie Lambda function from API Gateway.
delete(id: string){
    return this.http
      .delete(`${environment.api}/${id}`, {headers: this.getHeaders()})
      .map(res => {
        return res
      })
}
  • Edit Button : To implement an edit button on the web application to allow the user to update movie attributes, use the following function.
update(movie: Movie){
    return this.http
      .put(environment.api, JSON.stringify(movie), {headers: this.getHeaders()})
      .map(res => {
        return res
      })
}

1.4 CI/CD Workflow for API Gateway Documentation

To implement a CI/CD workflow with either CircleCI, Jenkins, or CodePipeline to automate the generation and deployment of the API Gateway documentation, use the following code:

def bucket = 'movies-api-documentation'
def api_id = ''
node('slaves'){
  stage('Generate'){
    if (env.BRANCH_NAME == 'master') {
      sh "aws apigateway get-export --rest-api-id ${api_id} \
        --stage-name production \
        --export-type swagger swagger.json"
    }
    else if (env.BRANCH_NAME == 'preprod') {
      sh "aws apigateway get-export --rest-api-id ${api_id} \
        --stage-name staging \
        --export-type swagger swagger.json"
    } else {
      sh "aws apigateway get-export --rest-api-id ${api_id} \
        --stage-name sandbox \
        --export-type swagger swagger.json"
    }
  }
  stage('Publish'){
    sh "aws s3 cp swagger.json s3://${bucket}"
  }
}

2. Testing Serverless Application

2.1 Unit Tests for Lambda Functions

  • UpdateMovie Lambda Function :
package main
import (
  "testing"
  "github.com/stretchr/testify/assert"
  "github.com/aws/aws-lambda-go/events"
)
func TestUpdate_InvalidPayLoad(t *testing.T) {
  input := events.APIGatewayProxyRequest{
    Body: "{'name': 'avengers'}",
  }
  expected := events.APIGatewayProxyResponse{
    StatusCode: 400,
    Body: "Invalid payload",
  }
  response, _ := update(input)
  assert.Equal(t, expected, response)
}
func TestUpdate_ValidPayload(t *testing.T) {
  input := events.APIGatewayProxyRequest{
    Body: "{\"id\":\"40\", \"name\":\"Thor\", \"description\":\"Marvel movie\", \"cover\":\"poster url\"}",
  }
  expected := events.APIGatewayProxyResponse{
    Body: "{\"id\":\"40\", \"name\":\"Thor\", \"description\":\"Marvel movie\", \"cover\":\"poster url\"}",
    StatusCode: 200,
    Headers: map[string]string{
      "Content-Type": "application/json",
      "Access-Control-Allow-Origin": "*",
    },
  }
  response, _ := update(input)
  assert.Equal(t, expected, response)
}
  • DeleteMovie Lambda Function :
package main
import (
  "testing"
  "github.com/stretchr/testify/assert"
  "github.com/aws/aws-lambda-go/events"
)
func TestDelete_InvalidPayLoad(t *testing.T) {
  input := events.APIGatewayProxyRequest{
    Body: "{'name': 'avengers'}",
  }
  expected := events.APIGatewayProxyResponse{
    StatusCode: 400,
    Body: "Invalid payload",
  }
  response, _ := delete(input)
  assert.Equal(t, expected, response)
}
func TestDelete_ValidPayload(t *testing.T) {
  input := events.APIGatewayProxyRequest{
    Body: "{\"id\":\"40\", \"name\":\"Thor\", \"description\":\"Marvel movie\", \"cover\":\"poster url\"}",
  }
  expected := events.APIGatewayProxyResponse{
    StatusCode: 200,
    Headers: map[string]string{
      "Content-Type": "application/json",
      "Access-Control-Allow-Origin": "*",
    },
  }
  response, _ := delete(input)
  assert.Equal(t, expected, response)
}

2.2 Modifying Jenkinsfile for Unit Tests

To modify the Jenkinsfile to include the execution of automated unit tests, use the following code:

def bucket = 'movies-api-deployment-packages'
node('slave-golang'){
  stage('Checkout'){
    checkout scm
  }
  stage('Test'){
    sh 'go get -u github.com/golang/lint/golint'
    sh 'go get -t ./...'
    sh 'golint -set_exit_status'
    sh 'go vet .'
    sh 'go test .'
  }
  stage('Build'){
    sh 'GOOS=linux go build -o main main.go'
    sh "zip ${commitID()}.zip main"
  }
  stage('Push'){
    sh "aws s3 cp ${commitID()}.zip s3://${bucket}"
  }
  stage('Deploy'){
    sh "aws lambda update-function-code --function-name FindAllMovies \
      --s3-bucket ${bucket} \
      --s3-key ${commitID()}.zip \
      --region us-east-1"
  }
}
def commitID() {
  sh 'git rev-parse HEAD > .git/commitID'
  def commitID = readFile('.git/commitID').trim()
  sh 'rm .git/commitID'
  commitID
}

2.3 Modifying buildspec.yml for Unit Tests

To modify the buildspec.yml definition file to include the execution of unit tests before pushing the deployment package to S3 using AWS CodeBuild, use the following code:

version: 0.2
env:
  variables:
    S3_BUCKET: "movies-api-deployment-packages"
    PACKAGE: "github.com/mlabouardy/lambda-codepipeline"
phases:
  install:
    commands:
      - mkdir -p "/go/src/$(dirname ${PACKAGE})"
      - ln -s "${CODEBUILD_SRC_DIR}" "/go/src/${PACKAGE}"
      - go get -u github.com/golang/lint/golint
  pre_build:
    commands:
      - cd "/go/src/${PACKAGE}"
      - go get -t ./...
      - golint -set_exit_status
      - go vet .
      - go test .
  build:
    commands:
      - GOOS=linux go build -o main
      - zip $CODEBUILD_RESOLVED_SOURCE_VERSION.zip main
      - aws s3 cp $CODEBUILD_RESOLVED_SOURCE_VERSION.zip s3://$S3_BUCKET/
  post_build:
    commands:
      - aws lambda update-function-code --function-name FindAllMovies --s3-bucket 
$S3_BUCKET --s3-key $CODEBUILD_RESOLVED_SOURCE_VERSION.zip

2.4 SAM Template for Lambda Function

The following is a SAM template file for the FindAllMovies Lambda function:

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Parameters:
  StageName:
    Type: String
    Default: staging
    Description: The API Gateway deployment stage
Resources:
  FindAllMovies:
    Type: AWS::Serverless::Function
    Properties:
      Handler: main
      Runtime: go1.x
      Role: !GetAtt FindAllMoviesRole.Arn 
      CodeUri: ./findall/deployment.zip
      Environment:
        Variables: 
          TABLE_NAME: !Ref MoviesTable
      Events:
        AnyRequest:
          Type: Api
          Properties:
            Path: /movies
            Method: GET
            RestApiId:
              Ref: MoviesAPI

  FindAllMoviesRole:
   Type: "AWS::IAM::Role"
   Properties:
     Path: "/"
     ManagedPolicyArns:
         - "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
     AssumeRolePolicyDocument:
       Version: "2012-10-17"
       Statement:
         -
           Effect: "Allow"
           Action:
             - "sts:AssumeRole"
           Principal:
             Service:
               - "lambda.amazonaws.com"
     Policies: 
        - 
          PolicyName: "PushCloudWatchLogsPolicy"
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Effect: Allow
                Action:
                - logs:CreateLogGroup
                - logs:CreateLogStream
                - logs:PutLogEvents
                Resource: "*"
        - 
          PolicyName: "ScanDynamoDBTablePolicy"
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Effect: Allow
                Action:
                - dynamodb:Scan
                Resource: "*"
  MoviesTable: 
     Type: AWS::Serverless::SimpleTable
     Properties:
       PrimaryKey:
         Name: ID
         Type: String
       ProvisionedThroughput:
         ReadCapacityUnits: 5
         WriteCapacityUnits: 5
  MoviesAPI:
    Type: 'AWS::Serverless::Api'
    Properties:
      StageName: !Ref StageName
      DefinitionBody:
        swagger: 2.0
        info:
          title: !Sub API-${StageName}
        paths:
          /movies:
            x-amazon-apigateway-any-method:
              produces:
                - application/json
              x-amazon-apigateway-integration:
                uri:
                  !Sub "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-
31/functions/${FindAllMovies.Arn}:current/invocations"
                passthroughBehavior: when_no_match
                httpMethod: POST
                type: aws_proxy

The following mermaid flowchart shows the general process of the CI/CD workflow for API Gateway documentation:

graph LR
    A[Start] --> B{Branch Name}
    B -- master --> C[Generate Swagger for Production]
    B -- preprod --> D[Generate Swagger for Staging]
    B -- other --> E[Generate Swagger for Sandbox]
    C --> F[Publish to S3]
    D --> F
    E --> F
    F --> G[End]

3. Securing Serverless Application

3.1 Integrating Social Logins

  • Facebook Login :
    • Create Facebook Application : Go to the Facebook Developers portal and create a new application.
    • Copy App ID and Secret : After creating the application, copy the App ID and secret.
    • Configure in Amazon Cognito : In the Amazon Cognito Console, configure Facebook as a provider.
    • Add Login Button : Follow the Facebook Guide to add the Facebook login button to the web application.
    • Fetch JWT Token : Once the user is authenticated, a Facebook session token will be returned. Add this token to the Amazon Cognito credentials provider to fetch a JWT token.
    • Add to API Request : Finally, add the JWT token to the API Gateway request Authorization header.
  • Twitter Login : Amazon Cognito does not support Twitter as an authentication provider out of the box. You will need to use OpenID Connect to extend Amazon Cognito.
  • Google Login :
    • Create Google Project : Create a new project from the Google Developers Console .
    • Enable API and Create Client ID : Enable the Google API under APIs and auth, and then create an OAuth 2.0 client ID.
    • Configure in Amazon Cognito : Configure Google in the Amazon Cognito Console.
    • Add Sign - in Button : Follow the Google documentation for Web to add the Google sign - in button.
    • Retrieve JWT Token : After user authentication, an authentication token will be generated, which can be used to retrieve the JWT token.

3.2 Account Creation and Password Reset

  • Account Creation : A Go - based Lambda function can be created to handle the account creation workflow.
package main
import (
  "os"
  "github.com/aws/aws - lambda - go/lambda"
  "github.com/aws/aws - sdk - go - v2/aws"
  "github.com/aws/aws - sdk - go - v2/aws/external"
  "github.com/aws/aws - sdk - go - v2/service/cognitoidentityprovider"
)
type Account struct {
  Username string `json:"username"`
  Password string `json:"password"`
}
func signUp(account Account) error {
  cfg, err := external.LoadDefaultAWSConfig()
  if err != nil {
    return err
  }
  cognito := cognitoidentityprovider.New(cfg)
  req := cognito.SignUpRequest(&cognitoidentityprovider.SignUpInput{
    ClientId: aws.String(os.Getenv("COGNITO_CLIENT_ID")),
    Username: aws.String(account.Username),
    Password: aws.String(account.Password),
  })
  _, err = req.Send()
  if err != nil {
    return err
  }
  return nil
}
func main() {
  lambda.Start(signUp)
}
  • Password Reset : A Go - based Lambda function can be created to reset the user password.
package main
import (
  "os"
  "github.com/aws/aws - lambda - go/lambda"
  "github.com/aws/aws - sdk - go - v2/aws"
  "github.com/aws/aws - sdk - go - v2/aws/external"
  "github.com/aws/aws - sdk - go - v2/service/cognitoidentityprovider"
)
type Account struct {
  Username string `json:"username"`
}
func forgotPassword(account Account) error {
  cfg, err := external.LoadDefaultAWSConfig()
  if err != nil {
    return err
  }
  cognito := cognitoidentityprovider.New(cfg)
  req := cognito.ForgotPasswordRequest(&cognitoidentityprovider.ForgotPasswordInput{
    ClientId: aws.String(os.Getenv("COGNITO_CLIENT_ID")),
    Username: aws.String(account.Username),
  })
  _, err = req.Send()
  if err != nil {
    return err
  }
  return nil
}
func main() {
  lambda.Start(forgotPassword)
}

4. Infrastructure as Code

4.1 Terraform Template for InsertMovie Lambda

# Setup execution role for the Lambda function
resource "aws_iam_role" "role" {
  name = "InsertMovieRole"
  assume_role_policy = "${file("assume - role - policy.json")}"
}
resource "aws_iam_policy" "cloudwatch_policy" {
  name = "PushCloudWatchLogsPolicy"
  policy = "${file("cloudwatch - policy.json")}"
}
resource "aws_iam_policy" "dynamodb_policy" {
  name = "ScanDynamoDBPolicy"
  policy = "${file("dynamodb - policy.json")}"
}
resource "aws_iam_policy_attachment" "cloudwatch - attachment" {
  name = "cloudwatch - lambda - attchment"
  roles = ["${aws_iam_role.role.name}"]
  policy_arn = "${aws_iam_policy.cloudwatch_policy.arn}"
}
resource "aws_iam_policy_attachment" "dynamodb - attachment" {
  name = "dynamodb - lambda - attchment"
  roles = ["${aws_iam_role.role.name}"]
  policy_arn = "${aws_iam_policy.dynamodb_policy.arn}"
}
# Create the Lambda function
resource "aws_lambda_function" "insert" {
  function_name = "InsertMovie"
  handler = "main"
  filename = "function/deployment.zip"
  runtime = "go1.x"
  role = "${aws_iam_role.role.arn}"
  environment {
    variables {
      TABLE_NAME = "movies"
    }
  }
}
# Expose a POST method on /movies resources in the REST API
resource "aws_api_gateway_method" "proxy" {
  rest_api_id = "${var.rest_api_id}"
  resource_id = "${var.resource_id}"
  http_method = "POST"
  authorization = "NONE"
}
resource "aws_api_gateway_integration" "lambda" {
  rest_api_id = "${var.rest_api_id}"
  resource_id = "${var.resource_id}"
  http_method = "${aws_api_gateway_method.proxy.http_method}"
  integration_http_method = "POST"
  type = "AWS_PROXY"
  uri = "${aws_lambda_function.insert.invoke_arn}"
}
resource "aws_lambda_permission" "apigw" {
  statement_id = "AllowAPIGatewayInvoke"
  action = "lambda:InvokeFunction"
  function_name = "${aws_lambda_function.insert.arn}"
  principal = "apigateway.amazonaws.com"
  source_arn ="${var.execution_arn}/*/*"
}

4.2 Updating CloudFormation Template

Add the following properties to the Resources section of the CloudFormation template to trigger the defined Lambda function with API Gateway in response to incoming HTTP requests.

API:
    Type: 'AWS::ApiGateway::RestApi'
    Properties:
        Name: API
        FailOnWarnings: 'true'
DemoResource:
    Type: 'AWS::ApiGateway::Resource'
    Properties:
        ParentId:
            'Fn::GetAtt': [API, RootResourceId]
        PathPart: demo
        RestApiId:
            Ref: API
DisplayMessageMethod:
    Type: 'AWS::ApiGateway::Method'
    Properties:
        HttpMethod: GET
        AuthorizationType: NONE
        ResourceId:
            Ref: DemoResource
        RestApiId:
            Ref: API
        Integration:
            Type: AWS
            Uri: {'Fn::Join': ["", "- \"arn:aws:apigateway:\"\n-!Ref \"AWS::Region\"\n- \":lambda:path/\"\n- \"/2015 - 03 - 31/functions/\"\n- Fn::GetAtt:\n - HelloWorldFunction\n - Arn\n- \"/invocations\""]}
            IntegrationHttpMethod: GET

4.3 SAM File for Serverless API

Resources:
  FindAllMovies:
    Type: AWS::Serverless::Function
    Properties:
      Handler: main
      Runtime: go1.x
      Role:!GetAtt FindAllMoviesRole.Arn 
      CodeUri:./findall/deployment.zip
      Environment:
        Variables: 
          TABLE_NAME:!Ref MoviesTable
      Events:
        AnyRequest:
          Type: Api
          Properties:
            Path: /movies
            Method: GET
            RestApiId:
              Ref: MoviesAPI
  InsertMovie:
    Type: AWS::Serverless::Function
    Properties:
      Handler: main
      Runtime: go1.x
      Role:!GetAtt InsertMovieRole.Arn 
      CodeUri:./insert/deployment.zip
      Environment:
        Variables: 
          TABLE_NAME:!Ref MoviesTable
      Events:
        AnyRequest:
          Type: Api
          Properties:
            Path: /movies
            Method: POST
            RestApiId:
              Ref: MoviesAPI
  DeleteMovie:
    Type: AWS::Serverless::Function
    Properties:
      Handler: main
      Runtime: go1.x
      Role:!GetAtt DeleteMovieRole.Arn 
      CodeUri:./delete/deployment.zip
      Environment:
        Variables: 
          TABLE_NAME:!Ref MoviesTable
      Events:
        AnyRequest:
          Type: Api
          Properties:
            Path: /movies
            Method: DELETE
            RestApiId:
              Ref: MoviesAPI
  UpdateMovie:
    Type: AWS::Serverless::Function
    Properties:
      Handler: main
      Runtime: go1.x
      Role:!GetAtt UpdateMovieRole.Arn 
      CodeUri:./update/deployment.zip
      Environment:
        Variables: 
          TABLE_NAME:!Ref MoviesTable
      Events:
        AnyRequest:
          Type: Api
          Properties:
            Path: /movies
            Method: PUT
            RestApiId:
              Ref: MoviesAPI

4.4 Configuring Terraform Remote State

  • Create S3 Bucket : Use the following AWS CLI command to create an S3 bucket.
aws s3 mb s3://terraform - state - files --region us - east - 1
  • Enable Server - Side Encryption :
aws s3api put - bucket - encryption --bucket terraform - state - files \
    --server - side - encryption - configuration file://config.json

The config.json file should have the following content:

{
  "Rules": [
    {
      "ApplyServerSideEncryptionByDefault": {
        "SSEAlgorithm": "AES256"
      }
    }
  ]
}
  • Configure Terraform :
terraform {
  backend "s3" {
    bucket = "terraform - state - files"
    key = "KEY_NAME"
    region = "us - east - 1"
  }
}

4.5 CloudFormation Template for Serverless API

AWSTemplateFormatVersion: "2010 - 09 - 09"
Description: "Simple Lambda Function"
Parameters:
  BucketName:
    Description: "S3 Bucket name"
    Type: "String"
  TableName:
    Description: "DynamoDB Table Name"
    Type: "String"
    Default: "movies"
Resources:
  FindAllMoviesRole:
    Type: "AWS::IAM::Role"
    Properties:
      AssumeRolePolicyDocument:
        Version: "2012 - 10 - 17"
        Statement:
          - 
            Effect: "Allow"
            Principal:
              Service:
                - "lambda.amazonaws.com"
            Action:
              - "sts:AssumeRole"
      Policies:
        - 
          PolicyName: "PushCloudWatchLogsPolicy"
          PolicyDocument:
            Version: "2012 - 10 - 17"
            Statement:
              - Effect: Allow
                Action:
                - logs:CreateLogGroup
                - logs:CreateLogStream
                - logs:PutLogEvents
                Resource: "*"
        - 
          PolicyName: "ScanDynamoDBTablePolicy"
          PolicyDocument:
            Version: "2012 - 10 - 17"
            Statement:
              - Effect: Allow
                Action:
                - dynamodb:Scan
                Resource: "*"
  FindAllMovies:
    Type: "AWS::Lambda::Function" 
    Properties:
      Code:
        S3Bucket:!Ref BucketName
        S3Key: findall - deployment.zip
      FunctionName: "FindAllMovies"
      Handler: "main"
      Runtime: "go1.x"
      Role:!GetAtt FindAllMoviesRole.Arn
      Environment:
        Variables:
          TABLE_NAME:!Ref TableName
  InsertMovieRole:
    Type: "AWS::IAM::Role"
    Properties:
      AssumeRolePolicyDocument:
        Version: "2012 - 10 - 17"
        Statement:
          - 
            Effect: "Allow"
            Principal:
              Service:
                - "lambda.amazonaws.com"
            Action:
              - "sts:AssumeRole"
      Policies:
        - 
          PolicyName: "PushCloudWatchLogsPolicy"
          PolicyDocument:
            Version: "2012 - 10 - 17"
            Statement:
              - Effect: Allow
                Action:
                - logs:CreateLogGroup
                - logs:CreateLogStream
                - logs:PutLogEvents
                Resource: "*"
        - 
          PolicyName: "PutItemDynamoDBTablePolicy"
          PolicyDocument:
            Version: "2012 - 10 - 17"
            Statement:
              - Effect: Allow
                Action:
                - dynamodb:PutItem
                Resource: "*"
  InsertMovie:
    Type: "AWS::Lambda::Function"
    Properties:
      Code:
        S3Bucket:!Ref BucketName
        S3Key: insert - deployment.zip
      FunctionName: "InsertMovie"
      Handler: "main"
      Runtime: "go1.x"
      Role:!GetAtt InsertMovieRole.Arn
      Environment:
        Variables:
          TABLE_NAME:!Ref TableName
  UpdateMovieRole:
    Type: "AWS::IAM::Role"
    Properties:
      AssumeRolePolicyDocument:
        Version: "2012 - 10 - 17"
        Statement:
          - 
            Effect: "Allow"
            Principal:
              Service:
                - "lambda.amazonaws.com"
            Action:
              - "sts:AssumeRole"
      Policies:
        - 
          PolicyName: "PushCloudWatchLogsPolicy"
          PolicyDocument:
            Version: "2012 - 10 - 17"
            Statement:
              - Effect: Allow
                Action:
                - logs:CreateLogGroup
                - logs:CreateLogStream
                - logs:PutLogEvents
                Resource: "*"
        - 
          PolicyName: "PutItemDynamoDBTablePolicy"
          PolicyDocument:
            Version: "2012 - 10 - 17"
            Statement:
              - Effect: Allow
                Action:
                - dynamodb:PutItem
                Resource: "*"
  UpdateMovie:
    Type: "AWS::Lambda::Function"
    Properties:
      Code:
        S3Bucket:!Ref BucketName
        S3Key: update - deployment.zip
      FunctionName: "UpdateMovie"
      Handler: "main"
      Runtime: "go1.x"
      Role:!GetAtt UpdateMovieRole.Arn
      Environment:
        Variables:
          TABLE_NAME:!Ref TableName
  DeleteMovieRole:
    Type: "AWS::IAM::Role"
    Properties:
      AssumeRolePolicyDocument:
        Version: "2012 - 10 - 17"
        Statement:
          - 
            Effect: "Allow"
            Principal:
              Service:
                - "lambda.amazonaws.com"
            Action:
              - "sts:AssumeRole"
      Policies:
        - 
          PolicyName: "PushCloudWatchLogsPolicy"
          PolicyDocument:
            Version: "2012 - 10 - 17"
            Statement:
              - Effect: Allow
                Action:
                - logs:CreateLogGroup
                - logs:CreateLogStream
                - logs:PutLogEvents
                Resource: "*"
        - 
          PolicyName: "DeleteItemDynamoDBTablePolicy"
          PolicyDocument:
            Version: "2012 - 10 - 17"
            Statement:
              - Effect: Allow
                Action:
                - dynamodb:DeleteItem
                Resource: "*"
  DeleteMovie:
    Type: "AWS::Lambda::Function"
    Properties:
      Code:
        S3Bucket:!Ref BucketName
        S3Key: update - deployment.zip
      FunctionName: "DeleteMovie"
      Handler: "main"
      Runtime: "go1.x"
      Role:!GetAtt DeleteMovieRole.Arn
      Environment:
        Variables:
          TABLE_NAME:!Ref TableName
  MoviesApi:
    Type: "AWS::ApiGateway::RestApi"
    Properties:
      Name: "MoviesApi"
      FailOnWarnings: "true"
  MoviesResource:
    Type: "AWS::ApiGateway::Resource"
    Properties:
      ParentId:
        Fn::GetAtt:
          - "MoviesApi"
          - "RootResourceId"
      PathPart: "movies"
      RestApiId:
        Ref: MoviesApi
  CreateMovieMethod:
    Type: "AWS::ApiGateway::Method"
    Properties:
      HttpMethod: "POST"
      AuthorizationType: "NONE"
      ResourceId:
        Ref: MoviesResource
      RestApiId:
        Ref: MoviesApi
      Integration:
        Type: "AWS"
        Uri:
          Fn::Join:
            - ""
            - - "arn:aws:apigateway:"
              -!Ref "AWS::Region"
              - ":lambda:path/"
              - "/2015 - 03 - 31/functions/"
              - Fn::GetAtt:
                - InsertMovie
                - Arn
              - "/invocations"
        IntegrationHttpMethod: "POST"
  DeleteMovieMethod:
    Type: "AWS::ApiGateway::Method"
    Properties:
      HttpMethod: "DELETE"
      AuthorizationType: "NONE"
      ResourceId:
        Ref: MoviesResource
      RestApiId:
        Ref: MoviesApi
      Integration:
        Type: "AWS"
        Uri:
          Fn::Join:
            - ""
            - - "arn:aws:apigateway:"
              -!Ref "AWS::Region"
              - ":lambda:path/"
              - "/2015 - 03 - 31/functions/"
              - Fn::GetAtt:
                - DeleteMovie
                - Arn
              - "/invocations"
        IntegrationHttpMethod: "DELETE"
  UpdateMovieMethod:
    Type: "AWS::ApiGateway::Method"
    Properties:
      HttpMethod: "PUT"
      AuthorizationType: "NONE"
      ResourceId:
        Ref: MoviesResource
      RestApiId:
        Ref: MoviesApi
      Integration:
        Type: "AWS"
        Uri:
          Fn::Join:
            - ""
            - - "arn:aws:apigateway:"
              -!Ref "AWS::Region"
              - ":lambda:path/"
              - "/2015 - 03 - 31/functions/"
              - Fn::GetAtt:
                - UpdateMovie
                - Arn
              - "/invocations"
        IntegrationHttpMethod: "PUT"
  ListMoviesMethod:
    Type: "AWS::ApiGateway::Method"
    Properties:
      HttpMethod: "GET"
      AuthorizationType: "NONE"
      ResourceId:
        Ref: MoviesResource
      RestApiId:
        Ref: MoviesApi
      Integration:
        Type: "AWS"
        Uri:
          Fn::Join:
            - ""
            - - "arn:aws:apigateway:"
              -!Ref "AWS::Region"
              - ":lambda:path/"
              - "/2015 - 03 - 31/functions/"
              - Fn::GetAtt:
                - FindAllMovies
                - Arn
              - "/invocations"
        IntegrationHttpMethod: "GET"
  DynamoDBTable:
    Type: "AWS::DynamoDB::Table"
    Properties:
      TableName:!Ref TableName
      AttributeDefinitions:
        -
          AttributeName: "ID"
          AttributeType: "S"
      KeySchema:
        -
          AttributeName: "ID"
          KeyType: "HASH"
      ProvisionedThroughput:
        ReadCapacityUnits: 5
        WriteCapacityUnits: 5

4.6 Terraform Template for Serverless API

resource "aws_iam_role" "roles" {
  count = "${length(var.functions)}"
  name = "${element(var.functions, count.index)}Role"
  assume_role_policy = "${file("policies/assume - role - policy.json")}"
}
resource "aws_iam_policy" "policies" {
  count = "${length(var.functions)}"
  name = "${element(var.functions, count.index)}Policy"
  policy = "${file("policies/${element(var.functions, count.index)}-policy.json")}"
}
resource "aws_iam_policy_attachment" "policy - attachments" {
  count = "${length(var.functions)}"
  name = "${element(var.functions, count.index)}Attachment"
  roles = ["${element(aws_iam_role.roles.*.name, count.index)}"]
  policy_arn = "${element(aws_iam_policy.policies.*.arn, count.index)}"
}
resource "aws_lambda_function" "functions" {
  count = "${length(var.functions)}"
  function_name = "${element(var.functions, count.index)}"
  handler = "main"
  filename = "functions/${element(var.functions, count.index)}.zip"
  runtime = "go1.x"
  role = "${element(aws_iam_role.roles.*.arn, count.index)}"
  environment {
    variables {
      TABLE_NAME = "${var.table_name}"
    }
  }
}
resource "aws_api_gateway_rest_api" "api" {
  name = "MoviesAPI"
}
resource "aws_api_gateway_resource" "proxy" {
  rest_api_id = "${aws_api_gateway_rest_api.api.id}"
  parent_id = "${aws_api_gateway_rest_api.api.root_resource_id}"
  path_part = "movies"
}
resource "aws_api_gateway_deployment" "staging" {
  depends_on = ["aws_api_gateway_integration.integrations"]
  rest_api_id = "${aws_api_gateway_rest_api.api.id}"
  stage_name = "staging"
}
resource "aws_api_gateway_method" "proxies" {
  count = "${length(var.functions)}"
  rest_api_id = "${aws_api_gateway_rest_api.api.id}"
  resource_id = "${aws_api_gateway_resource.proxy.id}"
  http_method = "${lookup(var.methods, element(var.functions, count.index))}"
  authorization = "NONE"
}
resource "aws_api_gateway_integration" "integrations" {
  count = "${length(var.functions)}"
  rest_api_id = "${aws_api_gateway_rest_api.api.id}"
  resource_id = "${element(aws_api_gateway_method.proxies.*.resource_id, 
count.index)}"
  http_method = "${element(aws_api_gateway_method.proxies.*.http_method, 
count.index)}"
  integration_http_method = "POST"
  type = "AWS_PROXY"
  uri = "${element(aws_lambda_function.functions.*.invoke_arn, count.index)}"
}
resource "aws_lambda_permission" "permissions" {
  count = "${length(var.functions)}"
  statement_id = "AllowAPIGatewayInvoke"
  action = "lambda:InvokeFunction"
  function_name = "${element(aws_lambda_function.functions.*.arn, count.index)}"
  principal = "apigateway.amazonaws.com"
  source_arn = "${aws_api_gateway_deployment.staging.execution_arn}/*/*"
}

The following mermaid flowchart shows the general process of the Serverless API deployment using Terraform:

graph LR
    A[Start] --> B[Create IAM Roles]
    B --> C[Create IAM Policies]
    C --> D[Attach Policies to Roles]
    D --> E[Create Lambda Functions]
    E --> F[Create API Gateway]
    F --> G[Create API Resources and Methods]
    G --> H[Create API Integrations]
    H --> I[Set Lambda Permissions]
    I --> J[Deploy API]
    J --> K[End]

In conclusion, building a serverless movie API involves multiple aspects such as frontend development, testing, security, and infrastructure management. By following the steps and code snippets provided in this blog, you can create a robust and secure serverless application.

在数字化进程中,人工智能技术日益成为科技革新的关键驱动力,其中强化学习作为机器学习的重要分支,在解决复杂控制任务方面展现出显著潜力。本文聚焦于深度确定性策略梯度(DDPG)方法在移动机器人自主导航领域的应用研究。该算法通过构建双神经网络架构,有效克服了传统Q-learning在连续动作空间中的局限性,为高维环境下的决策问题提供了创新解决方案。 DDPG算法的核心架构包含策略网络与价值评估网络两大组件。策略网络负责根据环境状态生成连续动作指令,通过梯度上升方法不断优化策略以获取最大长期回报;价值评估网络则采用深度神经网络对状态-动作对的期望累积奖励进行量化估计,为策略优化提供方向性指导。这种双网络协作机制确保了算法在复杂环境中的决策精度。 为提升算法稳定性,DDPG引入了多项关键技术:经验回放机制通过建立数据缓冲区存储历史交互记录,采用随机采样方式打破样本间的时序关联性;目标网络系统通过参数软更新策略,以θ_target = τ·θ_current + (1-τ)·θ_target的更新方式确保训练过程的平稳性;探索噪声注入技术则通过在动作输出中添加随机扰动,维持了策略探索与利用的平衡。 在具体实施过程中,研究需依次完成以下关键步骤:首先建立符合马尔科夫决策过程的环境模型,精确描述机器人的运动学特性与环境动力学;随后设计深度神经网络结构,确定各层神经元数量、激活函数类型及参数优化算法;接着进行超参数配置,包括学习速率、批量采样规模、目标网络更新系数等关键数值的设定;最后构建完整的训练验证流程,通过周期性测试评估导航成功率、路径规划效率、障碍规避能力等核心指标。 该研究方法不仅为移动机器人自主导航提供了可靠的技术方案,其算法框架还可扩展应用于工业自动化、智能交通等需要精密控制的领域,具有重要的工程实践价值与理论借鉴意义。 资源来源于网络分享,仅用于学习交流使用,请勿用于商业,如有侵权请联系我删除!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值