AWSTemplateFormatVersion: 2010-09-09
Description: put text message to Lex bot
Resources:
  ApiGateway:
    Type: "AWS::ApiGateway::RestApi"
    Properties:
      Body:
        info:
          version: "1.0"
          title: !Ref "AWS::StackName"
        paths:
          /lex/text:
            post:
              x-amazon-apigateway-integration:
                httpMethod: POST
                type: aws_proxy
                uri: !Sub >-
                  arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${LambdaFunction.Arn}/invocations
              responses: {}
        openapi: 3.0.2
        x-amazon-apigateway-gateway-responses:
          DEFAULT_5XX:
            responseParameters:
              gatewayresponse.header.Access-Control-Allow-Methods: "'POST'"
              gatewayresponse.header.Access-Control-Allow-Headers: "'Content-Type'"
              gatewayresponse.header.Access-Control-Allow-Origin: "'*'"
            responseTemplates: {}
          DEFAULT_4XX:
            responseParameters:
              gatewayresponse.header.Access-Control-Allow-Methods: "'POST'"
              gatewayresponse.header.Access-Control-Allow-Headers: "'Content-Type'"
              gatewayresponse.header.Access-Control-Allow-Origin: "'*'"
            responseTemplates: {}
      EndpointConfiguration:
        Types:
          - REGIONAL
      Name: LexPutTextApi
      Parameters:
        endpointConfigurationTypes: REGIONAL
  ApiGatewayDeployment:
    Type: "AWS::ApiGateway::Deployment"
    Properties:
      RestApiId: !Ref ApiGateway
      Description: "RestApi deployment id: b0be12d13a6c3db47e3e468ed89c3103f0f112bd"
    DependsOn:
      - ProxyAnyGet
      - ProxyAnyHead
      - Proxy
  ApiGatewaydevStage:
    Type: "AWS::ApiGateway::Stage"
    Properties:
      DeploymentId: !Ref ApiGatewayDeployment
      RestApiId: !Ref ApiGateway
      StageName: dev
  Static:
    Type: "AWS::ApiGateway::Resource"
    Properties:
      ParentId: !GetAtt
        - ApiGateway
        - RootResourceId
      PathPart: static
      RestApiId: !Ref ApiGateway
  Proxy:
    Type: "AWS::ApiGateway::Resource"
    Properties:
      ParentId: !Ref Static
      PathPart: "{proxy+}"
      RestApiId: !Ref ApiGateway
  ProxyAnyGet:
    Type: "AWS::ApiGateway::Method"
    Properties:
      AuthorizationType: NONE
      HttpMethod: GET
      Integration:
        Type: AWS
        IntegrationHttpMethod: GET
        Credentials: !GetAtt
          - S3AccessRole
          - Arn
        Uri: !Join
          - ""
          - - "arn:aws:apigateway:"
            - !Ref "AWS::Region"
            - ":s3:path/"
            - !Ref Bucket
            - "/{proxy}"
        RequestParameters:
          integration.request.path.proxy: method.request.path.proxy
        IntegrationResponses:
          - StatusCode: 200
            ResponseParameters:
              method.response.header.content-type: integration.response.header.Content-Type
              method.response.header.api-stage: context.stage
          - StatusCode: 404
            ResponseTemplates:
              application/xml: '{"error":"Not found"}'
            SelectionPattern: "403"
      RequestParameters:
        method.request.path.proxy: false
      ResourceId: !Ref Proxy
      MethodResponses:
        - StatusCode: 200
          ResponseParameters:
            method.response.header.content-type: false
            method.response.header.api-stage: false
        - StatusCode: 400
        - StatusCode: 404
      RestApiId: !Ref ApiGateway
  ProxyAnyHead:
    Type: "AWS::ApiGateway::Method"
    Properties:
      AuthorizationType: NONE
      HttpMethod: HEAD
      Integration:
        Type: AWS
        IntegrationHttpMethod: HEAD
        Credentials: !GetAtt
          - S3AccessRole
          - Arn
        Uri: !Join
          - ""
          - - "arn:aws:apigateway:"
            - !Ref "AWS::Region"
            - ":s3:path/"
            - !Ref Bucket
            - "/{proxy}"
        RequestParameters:
          integration.request.path.proxy: method.request.path.proxy
        IntegrationResponses:
          - StatusCode: 200
            ResponseParameters:
              method.response.header.content-type: integration.response.header.Content-Type
              method.response.header.api-stage: context.stage
          - StatusCode: 404
            ResponseTemplates:
              application/xml: '{"error":"Not found"}'
            SelectionPattern: "403"
      RequestParameters:
        method.request.path.proxy: false
      ResourceId: !Ref Proxy
      MethodResponses:
        - StatusCode: 200
          ResponseParameters:
            method.response.header.content-type: false
            method.response.header.api-stage: false
        - StatusCode: 400
        - StatusCode: 404
      RestApiId: !Ref ApiGateway

  CFNLambda:
    Type: "AWS::Lambda::Function"
    Properties:
      Code:
        S3Bucket: lexhandson-202106
        S3Key: cftemplate/cfn.zip
      Handler: index.handler
      MemorySize: "3008"
      Role: !GetAtt
        - CFNLambdaRole
        - Arn
      Runtime: nodejs16.x
      Timeout: 180
      Tags:
        - Key: Type
          Value: CustomResource
  Bucket:
    Type: "AWS::S3::Bucket"
    DeletionPolicy: Delete
    Properties:
      WebsiteConfiguration:
        IndexDocument: index.html
  Clear:
    Type: "Custom::S3Clear"
    DependsOn:
      - CFNInvokePolicy
    Properties:
      ServiceToken: !GetAtt
        - CFNLambda
        - Arn
      Bucket: !Ref Bucket
  Unzip:
    Type: "Custom::S3Unzip"
    Properties:
      ServiceToken: !GetAtt
        - CFNLambda
        - Arn
      SrcBucket: lexhandson-202106
      Key: lex-chatbot-app/website.zip
      DstBucket: !Ref Bucket
      buildDate: "2021-06-06T14:08:00.271Z"
    DependsOn: Clear

  S3AccessRole:
    Type: "AWS::IAM::Role"
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service: apigateway.amazonaws.com
            Action: "sts:AssumeRole"
      Path: /
      Policies:
        - PolicyName: S3AccessPolicy
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Effect: Allow
                Action:
                  - "s3:GetObject"
                Resource:
                  - !Sub "arn:aws:s3:::${Bucket}/*"
  CFNInvokePolicy:
    Type: "AWS::IAM::ManagedPolicy"
    Properties:
      PolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Action:
              - "lambda:InvokeFunction"
            Resource:
              - !GetAtt
                - CFNLambda
                - Arn
      Roles:
        - !Ref CFNLambdaRole
  CFNLambdaRole:
    Type: "AWS::IAM::Role"
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service: lambda.amazonaws.com
            Action: "sts:AssumeRole"
      Path: /
      ManagedPolicyArns:
        - "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
        - "arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole"
      Policies:
        - PolicyName: access
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Effect: Allow
                Action:
                  - "es:*"
                Resource:
                  - "*"
              - Effect: Allow
                Action:
                  - "apigateway:*"
                Resource:
                  - "*"
              - Effect: Allow
                Action:
                  - "iam:PassRole"
                  - "iam:CreateServiceLinkedRole"
                Resource:
                  - "*"
              - Effect: Allow
                Action:
                  - "s3:*"
                Resource:
                  - !Sub "arn:aws:s3:::*"

  LambdaFunctionGetApiPermissiondev:
    Type: "AWS::Lambda::Permission"
    Properties:
      Action: "lambda:InvokeFunction"
      Principal: apigateway.amazonaws.com
      FunctionName: !Ref LambdaFunction
      SourceArn: !Sub
        - >-
          arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/POST/lex/text
        - __Stage__: "*"
          __ApiId__: !Ref ApiGateway
  LambdaFunctionRole:
    Type: "AWS::IAM::Role"
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Action:
              - "sts:AssumeRole"
            Effect: Allow
            Principal:
              Service:
                - lambda.amazonaws.com
      ManagedPolicyArns:
        - "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
        - "arn:aws:iam::aws:policy/AmazonLexFullAccess"
      Tags:
        - Value: SAM
          Key: "lambda:createdBy"
  LambdaFunction:
    Type: "AWS::Lambda::Function"
    Properties:
      Code:
        ZipFile: |
          import json
          import boto3
          import os
          import traceback
          from botocore.exceptions import ClientError

          client = boto3.client('lexv2-runtime')

          def lambda_handler(event, context):
              try:
                  payload = json.loads(event['body'])

                  #input fields
                  input_text = payload['text']
                  session_token = payload['sessionToken']

                  status_code = 200
                  res = {}

                  # put text to Lex bot
                  response = client.recognize_text(
                      botId=os.environ.get("BOT_ID"),
                      botAliasId=os.environ.get("BOT_ALIAS_ID"),
                      localeId="ja_JP",
                      sessionId= session_token,
                      text= input_text,
                  )

                  print(response)
                  res['sessionId'] = response.get('sessionId')
                  res['message'] = response.get('messages')[0]['content']

                  interpret = response.get('interpretations')[0]
                  if interpret['intent']['name']=='FallbackIntent':
                      res['intentName'] = interpret['intent']['name']
                  else:
                      res['intentName'] = interpret['intent']['name']
                      res['score'] = interpret['nluConfidence']['score']

                  print(res)
              except ClientError as e:
                  status_code = 400
                  res = {
                      'error_message' : e.response['Error']['Message'],
                      'error_code' : e.response['Error']['Code']
                  }
                  print(res)
              except Exception as e:
                  status_code = 400
                  print(traceback.format_exc())  #stack trace
                  res = {
                      'error': e.__class__.__name__,
                      'error_message' : str(e)
                  }
                  print(res)
              return {
                  'headers': {
                  'Access-Control-Allow-Headers': 'Content-Type',
                  'Access-Control-Allow-Origin': '*',
                  'Access-Control-Allow-Methods': 'POST'  
                  },
                  'statusCode': status_code,
                  'body': json.dumps(res)
              }
      FunctionName: LexPutText
      Tags:
        - Value: SAM
          Key: "lambda:createdBy"
      MemorySize: 256
      Handler: index.lambda_handler
      Role: !GetAtt
        - LambdaFunctionRole
        - Arn
      Timeout: 60
      Runtime: python3.9
      Environment:
        Variables:
          BOT_ALIAS_ID: dummy
          BOT_ID: dummy
