Resources:
  PreUpgradeExport:
    Type: "Custom::PreUpgradeExport"
    Properties:
      ServiceToken: !GetAtt
        - CFNLambda
        - Arn
      bucket: !Ref ExportBucket
      id: ExportAll_QnABot_v4.7.0.json
      index: !Sub "${Var.QnaIndex}"
      encryption: !Ref Encryption
  PreUpgradeExportMetrics:
    Type: "Custom::PreUpgradeExport"
    Properties:
      ServiceToken: !GetAtt
        - CFNLambda
        - Arn
      bucket: !Ref ExportBucket
      id: ExportAll_QnABot_v4.7.0_metrics.json
      index: !Sub "${Var.MetricsIndex}"
      encryption: !Ref Encryption
  PreUpgradeExportFeedback:
    Type: "Custom::PreUpgradeExport"
    Properties:
      ServiceToken: !GetAtt
        - CFNLambda
        - Arn
      bucket: !Ref ExportBucket
      id: ExportAll_QnABot_v4.7.0_feedback.json
      index: !Sub "${Var.FeedbackIndex}"
      encryption: !Ref Encryption
  AssetBucket:
    Type: "AWS::S3::Bucket"
    Properties:
      BucketEncryption: !If
        - Encrypted
        - ServerSideEncryptionConfiguration:
            - ServerSideEncryptionByDefault:
                SSEAlgorithm: AES256
        - !Ref "AWS::NoValue"
  AssetClear:
    Type: "Custom::S3Clear"
    DependsOn:
      - CFNInvokePolicy
    Condition: BuildExamples
    Properties:
      ServiceToken: !GetAtt
        - CFNLambda
        - Arn
      Bucket: !Ref AssetBucket
  AssetZipVersion:
    Condition: BuildExamples
    Type: "Custom::S3Version"
    Properties:
      ServiceToken: !GetAtt
        - CFNLambda
        - Arn
      Bucket: aws-bigdata-blog-replica-ap-northeast-1
      Key: !Join
        - ""
        - - artifacts/aws-ai-qna-bot
          - /assets.zip
      BuildDate: "2021-06-06T14:08:00.108Z"
  AssetUnzip:
    Type: "Custom::S3Unzip"
    Condition: BuildExamples
    DependsOn:
      - AssetClear
    Properties:
      ServiceToken: !GetAtt
        - CFNLambda
        - Arn
      SrcBucket: aws-bigdata-blog-replica-ap-northeast-1
      Key: !Join
        - ""
        - - artifacts/aws-ai-qna-bot
          - /assets.zip
      DstBucket: !Ref AssetBucket
      version: !Ref AssetZipVersion
  ExportBucket:
    Type: "AWS::S3::Bucket"
    Properties:
      LifecycleConfiguration:
        Rules:
          - NoncurrentVersionExpirationInDays: 1
            Status: Enabled
          - AbortIncompleteMultipartUpload:
              DaysAfterInitiation: 1
            Status: Enabled
      VersioningConfiguration:
        Status: Enabled
      CorsConfiguration:
        CorsRules:
          - AllowedHeaders:
              - "*"
            AllowedMethods:
              - GET
            AllowedOrigins:
              - "*"
      BucketEncryption: !If
        - Encrypted
        - ServerSideEncryptionConfiguration:
            - ServerSideEncryptionByDefault:
                SSEAlgorithm: AES256
        - !Ref "AWS::NoValue"
  ImportBucket:
    Type: "AWS::S3::Bucket"
    Properties:
      LifecycleConfiguration:
        Rules:
          - ExpirationInDays: 1
            Status: Enabled
      VersioningConfiguration:
        Status: Enabled
      CorsConfiguration:
        CorsRules:
          - AllowedHeaders:
              - "*"
            AllowedMethods:
              - PUT
            AllowedOrigins:
              - "*"
      BucketEncryption: !If
        - Encrypted
        - ServerSideEncryptionConfiguration:
            - ServerSideEncryptionByDefault:
                SSEAlgorithm: AES256
        - !Ref "AWS::NoValue"
  TestAllBucket:
    Type: "AWS::S3::Bucket"
    Properties:
      LifecycleConfiguration:
        Rules:
          - ExpirationInDays: 1
            Status: Enabled
      VersioningConfiguration:
        Status: Enabled
      CorsConfiguration:
        CorsRules:
          - AllowedHeaders:
              - "*"
            AllowedMethods:
              - GET
            AllowedOrigins:
              - "*"
      BucketEncryption: !If
        - Encrypted
        - ServerSideEncryptionConfiguration:
            - ServerSideEncryptionByDefault:
                SSEAlgorithm: AES256
        - !Ref "AWS::NoValue"
  VersionLambda:
    Type: "AWS::Lambda::Function"
    Properties:
      Code:
        ZipFile: >
          var aws=require('aws-sdk')

          aws.config.region=process.env.AWS_REGION

          var s3=new aws.S3()


          exports.handler = function(event, context) {
              console.log(JSON.stringify(event,null,2))
              if(event.RequestType!=="Delete"){
                  s3.headObject({
                      Bucket:event.ResourceProperties.Bucket,
                      Key:event.ResourceProperties.Key
                  }).promise()
                  .then(result=>send(event, context, SUCCESS,{
                      version:result.VersionId ? result.VersionId : 1
                  }))
                  .catch(x=>{
                      console.log(x)
                      send(event, context, FAILED)
                  })
              }else{
                  send(event, context, SUCCESS)
              }
          }



          const SUCCESS = "SUCCESS";

          const FAILED = "FAILED";


          function send(event, context, responseStatus, responseData,
          physicalResourceId, noEcho) {

              var responseBody = JSON.stringify({
                  Status: responseStatus,
                  Reason: "See the details in CloudWatch Log Stream: " + context.logStreamName,
                  PhysicalResourceId: physicalResourceId || context.logStreamName,
                  StackId: event.StackId,
                  RequestId: event.RequestId,
                  LogicalResourceId: event.LogicalResourceId,
                  NoEcho: noEcho || false,
                  Data: responseData
              });

              console.log("Response body:\n", responseBody);

              var https = require("https");
              var url = require("url");

              var parsedUrl = url.parse(event.ResponseURL);
              var options = {
                  hostname: parsedUrl.hostname,
                  port: 443,
                  path: parsedUrl.path,
                  method: "PUT",
                  headers: {
                      "content-type": "",
                      "content-length": responseBody.length
                  }
              };

              var request = https.request(options, function(response) {
                  console.log("Status code: " + response.statusCode);
                  console.log("Status message: " + response.statusMessage);
                  context.done();
              });

              request.on("error", function(error) {
                  console.log("send(..) failed executing https.request(..): " + error);
                  context.done();
              });

              request.write(responseBody);
              request.end();
          }
      Handler: index.handler
      MemorySize: "3008"
      Role: !GetAtt
        - CFNLambdaRole
        - Arn
      Runtime: nodejs16.x
      Timeout: 60
      VpcConfig: !If
        - VPCEnabled
        - SubnetIds:
            - !Ref "AWS::NoValue"
          SecurityGroupIds:
            - !Ref "AWS::NoValue"
        - !Ref "AWS::NoValue"
      TracingConfig: !If
        - XRAYEnabled
        - Mode: Active
        - !Ref "AWS::NoValue"
      Tags:
        - Key: Type
          Value: CustomResource
  CFNVersion:
    Type: "Custom::S3Version"
    Properties:
      ServiceToken: !GetAtt
        - VersionLambda
        - Arn
      Bucket: lexhandson-202106
      Key: cftemplate/cfn.zip
      BuildDate: "2021-06-06T14:08:00.109Z"
  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
      VpcConfig: !If
        - VPCEnabled
        - SubnetIds:
            - !Ref "AWS::NoValue"
          SecurityGroupIds:
            - !Ref "AWS::NoValue"
        - !Ref "AWS::NoValue"
      TracingConfig: !If
        - XRAYEnabled
        - Mode: Active
        - !Ref "AWS::NoValue"
      Tags:
        - Key: Type
          Value: CustomResource
  CFNInvokePolicy:
    Type: "AWS::IAM::ManagedPolicy"
    Properties:
      PolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Action:
              - "lambda:InvokeFunction"
            Resource:
              - !GetAtt
                - CFNLambda
                - Arn
      Roles:
        - !Ref CFNLambdaRole
  CognitoDomain:
    Type: "Custom::CognitoDomain"
    Properties:
      ServiceToken: !GetAtt
        - CFNLambda
        - Arn
      UserPool: !Ref UserPool
  CognitoLoginClient:
    Type: "Custom::CognitoLogin"
    Properties:
      ServiceToken: !GetAtt
        - CFNLambda
        - Arn
      UserPool: !Ref UserPool
      ClientId: !Ref ClientClient
      LoginCallbackUrls:
        - !GetAtt
          - Urls
          - Client
      CSS: >
        .logo-customizable{max-width:60%;max-height:30%}.banner-customizable{padding:25px
        0px 25px
        0px;background-color:lightgray}.label-customizable{font-weight:410}.textDescription-customizable{padding-top:10px;padding-bottom:10px;display:block;font-size:16px}.idpDescription-customizable{padding-top:10px;padding-bottom:10px;display:block;font-size:16px}.legalText-customizable{color:#747474;font-size:11px}.submitButton-customizable{font-size:14px;font-weight:bold;margin:20px
        0px 10px
        0px;height:40px;width:100%;color:#fff;background-color:#337ab7}.submitButton-customizable:hover{color:#fff;background-color:#286090}.errorMessage-customizable{padding:5px;font-size:14px;width:100%;background:#F5F5F5;border:2px
        solid
        #D64958;color:#D64958}.inputField-customizable{width:100%;height:34px;color:#555;background-color:#fff;border:1px
        solid
        #ccc}.inputField-customizable:focus{border-color:#66afe9;outline:0}.idpButton-customizable{height:41px;width:100%;width:100%;text-align:center;margin-bottom:15px;color:#fff;background-color:#5bc0de;border-color:#46b8da}.idpButton-customizable:hover{color:#fff;background-color:#31b0d5}.socialButton-customizable{height:40px;text-align:left;width:100%;margin-bottom:15px}.redirect-customizable{text-align:center}.passwordCheck-notValid-customizable{color:#DF3312}.passwordCheck-valid-customizable{color:#19BF00}.background-customizable{background-color:#fff}
  CognitoLoginDesigner:
    Type: "Custom::CognitoLogin"
    Properties:
      ServiceToken: !GetAtt
        - CFNLambda
        - Arn
      UserPool: !Ref UserPool
      ClientId: !Ref ClientDesigner
      LoginCallbackUrls:
        - !GetAtt
          - Urls
          - Designer
      CSS: >
        .logo-customizable{max-width:60%;max-height:30%}.banner-customizable{padding:25px
        0px 25px
        0px;background-color:lightgray}.label-customizable{font-weight:410}.textDescription-customizable{padding-top:10px;padding-bottom:10px;display:block;font-size:16px}.idpDescription-customizable{padding-top:10px;padding-bottom:10px;display:block;font-size:16px}.legalText-customizable{color:#747474;font-size:11px}.submitButton-customizable{font-size:14px;font-weight:bold;margin:20px
        0px 10px
        0px;height:40px;width:100%;color:#fff;background-color:#337ab7}.submitButton-customizable:hover{color:#fff;background-color:#286090}.errorMessage-customizable{padding:5px;font-size:14px;width:100%;background:#F5F5F5;border:2px
        solid
        #D64958;color:#D64958}.inputField-customizable{width:100%;height:34px;color:#555;background-color:#fff;border:1px
        solid
        #ccc}.inputField-customizable:focus{border-color:#66afe9;outline:0}.idpButton-customizable{height:41px;width:100%;width:100%;text-align:center;margin-bottom:15px;color:#fff;background-color:#5bc0de;border-color:#46b8da}.idpButton-customizable:hover{color:#fff;background-color:#31b0d5}.socialButton-customizable{height:40px;text-align:left;width:100%;margin-bottom:15px}.redirect-customizable{display:none;text-align:center}.passwordCheck-notValid-customizable{color:#DF3312}.passwordCheck-valid-customizable{color:#19BF00}.background-customizable{background-color:#fff}
  DesignerLogin:
    Type: "Custom::CognitoUrl"
    Properties:
      ServiceToken: !GetAtt
        - CFNLambda
        - Arn
      adad: adaad
      ClientId: !Ref ClientDesigner
      Domain: !Ref CognitoDomain
      LoginRedirectUrl: !GetAtt
        - Urls
        - Designer
      response_type: code
  ClientLogin:
    Type: "Custom::CognitoUrl"
    Properties:
      ServiceToken: !GetAtt
        - CFNLambda
        - Arn
      ClientId: !Ref ClientClient
      Domain: !Ref CognitoDomain
      LoginRedirectUrl: !GetAtt
        - Urls
        - Client
      response_type: token
  User:
    Type: "AWS::Cognito::UserPoolUser"
    DependsOn:
      - SignupPermision
      - MessagePermision
      - ElasticsearchDomainUpdate
      - KibanaRoleAttachment
      - RoleAttachment
    Properties:
      DesiredDeliveryMediums:
        - EMAIL
      UserAttributes:
        - Name: email
          Value: !Ref Email
      Username: !Ref Username
      UserPoolId: !Ref UserPool
  UserToGroup:
    Type: "AWS::Cognito::UserPoolUserToGroupAttachment"
    Properties:
      GroupName: !Ref Admins
      Username: !Ref User
      UserPoolId: !Ref UserPool
  IdPool:
    Type: "AWS::Cognito::IdentityPool"
    Properties:
      IdentityPoolName: !Join
        - "-"
        - - QnaBotIdPool
          - !Ref "AWS::StackName"
      AllowUnauthenticatedIdentities: true
      CognitoIdentityProviders:
        - ClientId: !Ref ClientDesigner
          ProviderName: !GetAtt
            - UserPool
            - ProviderName
          ServerSideTokenCheck: true
        - ClientId: !Ref ClientClient
          ProviderName: !GetAtt
            - UserPool
            - ProviderName
          ServerSideTokenCheck: true
  KibanaIdPool:
    Type: "AWS::Cognito::IdentityPool"
    Properties:
      IdentityPoolName: !Join
        - "-"
        - - KibanaIdPool
          - !Ref "AWS::StackName"
      AllowUnauthenticatedIdentities: false
  KibanaRoleAttachment:
    Type: "Custom::CognitoRole"
    Properties:
      ServiceToken: !GetAtt
        - CFNLambda
        - Arn
      IdentityPoolId: !Ref KibanaIdPool
      DomainName: !GetAtt
        - ESVar
        - ESDomain
      Roles:
        authenticated: !GetAtt
          - UserRole
          - Arn
        unauthenticated: !GetAtt
          - UnauthenticatedRole
          - Arn
      RoleMappings:
        - ClientId: !GetAtt
            - KibanaClient
            - ClientId
          UserPool: !Ref UserPool
          Type: Rules
          AmbiguousRoleResolution: Deny
          RulesConfiguration:
            Rules:
              - Claim: "cognito:groups"
                MatchType: Contains
                Value: Admin
                RoleARN: !GetAtt
                  - KibanaRole
                  - Arn
  RoleAttachment:
    Type: "Custom::CognitoRole"
    Properties:
      ServiceToken: !GetAtt
        - CFNLambda
        - Arn
      IdentityPoolId: !Ref IdPool
      Roles:
        authenticated: !GetAtt
          - UserRole
          - Arn
        unauthenticated: !GetAtt
          - UnauthenticatedRole
          - Arn
      RoleMappings:
        - ClientId: !Ref ClientClient
          UserPool: !Ref UserPool
          Type: Rules
          AmbiguousRoleResolution: AuthenticatedRole
          RulesConfiguration:
            Rules:
              - Claim: "cognito:groups"
                MatchType: Contains
                Value: Admin
                RoleARN: !GetAtt
                  - UserRole
                  - Arn
        - ClientId: !Ref ClientDesigner
          UserPool: !Ref UserPool
          Type: Rules
          AmbiguousRoleResolution: Deny
          RulesConfiguration:
            Rules:
              - Claim: "cognito:groups"
                MatchType: Contains
                Value: Admin
                RoleARN: !GetAtt
                  - AdminRole
                  - Arn
  UserPool:
    Type: "AWS::Cognito::UserPool"
    Properties:
      UserPoolName: !Join
        - "-"
        - - UserPool
          - !Ref "AWS::StackName"
      AdminCreateUserConfig:
        AllowAdminCreateUserOnly: !If
          - AdminSignUp
          - true
          - false
        InviteMessageTemplate:
          EmailMessage: !Sub >
            <p>Hello {username},

            <p>Welcome to QnABot! Your temporary password is:

            <p>     {####}

            <p>

            <p>When the CloudFormation stack is COMPLETE, use the link below to
            log in to QnABot Content Designer, set your permanent password, and
            start building your bot!

            <p>     ${ApiUrl.Name}/pages/designer

            <p>

            <p>Good luck!

            <p>QnABot (www.amazon.com/qnabot)
          EmailSubject: Welcome to QnABot!
      AliasAttributes:
        - email
      AutoVerifiedAttributes:
        - email
      Schema:
        - Required: true
          Name: email
          AttributeDataType: String
          Mutable: true
      LambdaConfig:
        CustomMessage: !GetAtt
          - MessageLambda
          - Arn
        PreSignUp: !GetAtt
          - SignupLambda
          - Arn
  KibanaClient:
    Type: "Custom::ESCognitoClient"
    DependsOn:
      - ElasticsearchDomainUpdate
    Properties:
      ServiceToken: !GetAtt
        - CFNLambda
        - Arn
      UserPool: !Ref UserPool
      DomainName: !GetAtt
        - ESVar
        - ESDomain
  ClientDesigner:
    Type: "AWS::Cognito::UserPoolClient"
    Properties:
      ClientName: !Join
        - "-"
        - - UserPool
          - !Ref "AWS::StackName"
          - designer
      GenerateSecret: false
      UserPoolId: !Ref UserPool
  ClientClient:
    Type: "AWS::Cognito::UserPoolClient"
    Properties:
      ClientName: !Join
        - "-"
        - - UserPool
          - !Ref "AWS::StackName"
          - client
      GenerateSecret: false
      UserPoolId: !Ref UserPool
  Users:
    Type: "AWS::Cognito::UserPoolGroup"
    Properties:
      GroupName: Users
      UserPoolId: !Ref UserPool
  Admins:
    Type: "AWS::Cognito::UserPoolGroup"
    Properties:
      GroupName: Admins
      UserPoolId: !Ref UserPool
  DefaultUserPoolJwksUrl:
    Type: "AWS::SSM::Parameter"
    Properties:
      Description: Default QnABot Setting - DO NOT MODIFY
      Type: String
      Value: !Join
        - ""
        - - "https://cognito-idp."
          - !Ref "AWS::Region"
          - .amazonaws.com/
          - !Ref UserPool
          - /.well-known/jwks.json
  DefaultQnABotSettings:
    Type: "AWS::SSM::Parameter"
    Properties:
      Description: Default QnABot Settings - DO NOT MODIFY
      Type: String
      Value: !Sub >-
        {"ENABLE_DEBUG_RESPONSES":"false","ES_USE_KEYWORD_FILTERS":"true","ES_EXPAND_CONTRACTIONS":"{\"you're\":\"you
        are\",\"I'm\":\"I
        am\",\"can't\":\"cannot\"}","ES_KEYWORD_SYNTAX_TYPES":"NOUN,PROPN,VERB,INTJ","ES_SYNTAX_CONFIDENCE_LIMIT":".20","ES_MINIMUM_SHOULD_MATCH":"2<75%","ES_NO_HITS_QUESTION":"no_hits","ES_USE_FUZZY_MATCH":"false","ES_PHRASE_BOOST":"4","ES_SCORE_ANSWER_FIELD":"false","ENABLE_SENTIMENT_SUPPORT":"true","ENABLE_MULTI_LANGUAGE_SUPPORT":"false","ENABLE_CUSTOM_TERMINOLOGY":"false","MINIMUM_CONFIDENCE_SCORE":0.6,"ALT_SEARCH_KENDRA_FALLBACK_CONFIDENCE_SCORE":"HIGH","ALT_SEARCH_KENDRA_FAQ_CONFIDENCE_SCORE":"HIGH","ALT_SEARCH_KENDRA_INDEXES":"${DefaultKendraIndexId}","ALT_SEARCH_KENDRA_S3_SIGNED_URLS":"true","ALT_SEARCH_KENDRA_S3_SIGNED_URL_EXPIRE_SECS":300,"ALT_SEARCH_KENDRA_MAX_DOCUMENT_COUNT":2,"ALT_SEARCH_KENDRA_TOP_ANSWER_MESSAGE":"Amazon
        Kendra suggested answer.","ALT_SEARCH_KENDRA_FAQ_MESSAGE":"Answer from
        Amazon Kendra FAQ.","ALT_SEARCH_KENDRA_ANSWER_MESSAGE":"While I did not
        find an exact answer, these search results from Amazon Kendra might be
        helpful.","KENDRA_FAQ_INDEX":"${DefaultKendraIndexId}","KENDRA_FAQ_CONFIG_MAX_RETRIES":8,"KENDRA_FAQ_CONFIG_RETRY_DELAY":600,"KENDRA_FAQ_ES_FALLBACK":"true","ENABLE_KENDRA_WEB_INDEXER":"false","KENDRA_INDEXER_URLS":"","KENDRA_INDEXER_SCHEDULE":"rate(1
        day)","KENDRA_WEB_PAGE_INDEX":"${DefaultKendraIndexId}","ERRORMESSAGE":"Unfortunately
        I encountered an error when searching for your answer. Please ask me
        again later.","EMPTYMESSAGE":"You stumped me! Sadly I do not know how to
        answer your question.","DEFAULT_ALEXA_LAUNCH_MESSAGE":"Hello, Please ask
        a question","DEFAULT_ALEXA_REPROMPT":"Please either answer the question,
        ask another question or say Goodbye to end the
        conversation.","DEFAULT_ALEXA_STOP_MESSAGE":"Goodbye","SMS_HINT_REMINDER_ENABLE":"true","SMS_HINT_REMINDER":"
        (Feedback? Reply THUMBS UP or THUMBS DOWN. Ask HELP ME at any
        time)","SMS_HINT_REMINDER_INTERVAL_HRS":"24","IDENTITY_PROVIDER_JWKS_URLS":[],"ENFORCE_VERIFIED_IDENTITY":"false","NO_VERIFIED_IDENTITY_QUESTION":"no_verified_identity","ELICIT_RESPONSE_MAX_RETRIES":3,"ELICIT_RESPONSE_RETRY_MESSAGE":"Please
        try again?","ELICIT_RESPONSE_BOT_FAILURE_MESSAGE":"Your response was not
        understood. Please start again.","ELICIT_RESPONSE_DEFAULT_MSG":"Ok.
        ","CONNECT_IGNORE_WORDS":"","CONNECT_ENABLE_VOICE_RESPONSE_INTERRUPT":"false","CONNECT_NEXT_PROMPT_VARNAME":"connect_nextPrompt","ENABLE_REDACTING":"false","REDACTING_REGEX":"\\b\\d{4}\\b(?![-])|\\b\\d{9}\\b|\\b\\d{3}-\\d{2}-\\d{4}\\b","PII_REJECTION_ENABLED":false,"PII_REJECTION_QUESTION":"pii_rejection_question","PII_REJECTION_WITH_COMPREHEND":true,"PII_REJECTION_REGEX":"\\b\\d{4}\\b(?![-])|\\b\\d{9}\\b|\\b\\d{3}-\\d{2}-\\d{4}\\b","PII_REJECTION_IGNORE_TYPES":"Name,Address","DISABLE_CLOUDWATCH_LOGGING":"false","MINIMAL_ES_LOGGING":"false","S3_PUT_REQUEST_ENCRYPTION":"","BOT_ROUTER_WELCOME_BACK_MSG":"Welcome
        back to
        QnABot.","BOT_ROUTER_EXIT_MSGS":"exit,quit,goodbye,leave","RUN_LAMBDAHOOK_FROM_QUERY_STEP":"true"}
  CustomQnABotSettings:
    Type: "AWS::SSM::Parameter"
    Properties:
      Description: >-
        Custom QnABot Settings - Modify to override defaults, or to add new
        settings
      Type: String
      Value: "{}"
  UsersTable:
    Type: "AWS::DynamoDB::Table"
    Properties:
      BillingMode: PAY_PER_REQUEST
      AttributeDefinitions:
        - AttributeName: UserId
          AttributeType: S
      KeySchema:
        - AttributeName: UserId
          KeyType: HASH
  ElasticsearchDomain:
    Type: "AWS::Elasticsearch::Domain"
    DependsOn:
      - PreUpgradeExport
    Condition: CreateDomain
    Properties:
      ElasticsearchClusterConfig:
        DedicatedMasterEnabled: false
        InstanceCount: !Ref ElasticSearchNodeCount
        InstanceType: !If
          - Encrypted
          - c5.large.elasticsearch
          - t3.small.elasticsearch
        ZoneAwarenessEnabled: "true"
      EBSOptions:
        EBSEnabled: true
        VolumeSize: 10
        VolumeType: gp2
      ElasticsearchVersion: "7.9"
      SnapshotOptions:
        AutomatedSnapshotStartHour: "0"
      AdvancedOptions:
        rest.action.multi.allow_explicit_index: "true"
      EncryptionAtRestOptions:
        Enabled: !If
          - Encrypted
          - true
          - false
      NodeToNodeEncryptionOptions:
        Enabled: !If
          - Encrypted
          - true
          - false
      DomainEndpointOptions:
        EnforceHTTPS: !If
          - Encrypted
          - true
          - false
      VPCOptions: !If
        - VPCEnabled
        - SubnetIds:
            - !Ref "AWS::NoValue"
          SecurityGroupIds:
            - !Ref "AWS::NoValue"
        - !Ref "AWS::NoValue"
  ElasticsearchDomainUpdate:
    Type: "Custom::ElasticSearchUpdate"
    DependsOn:
      - CognitoDomain
    Properties:
      ServiceToken: !GetAtt
        - CFNLambda
        - Arn
      DomainName: !GetAtt
        - ESVar
        - ESDomain
      CognitoOptions:
        Enabled: true
        IdentityPoolId: !Ref KibanaIdPool
        RoleArn: !GetAtt
          - ESCognitoRole
          - Arn
        UserPoolId: !Ref UserPool
      AccessPolicies: !Sub >-
        {"Version":"2012-10-17","Statement":[{"Sid":"CognitoAuth","Principal":{"AWS":"${KibanaRole.Arn}"},"Effect":"Allow","Action":"es:ESHttp*","Resource":"${ESVar.ESArn}/*"}]}
  ESCognitoRole:
    Type: "AWS::IAM::Role"
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service: es.amazonaws.com
            Action: "sts:AssumeRole"
      Path: /
      ManagedPolicyArns:
        - "arn:aws:iam::aws:policy/AmazonESCognitoAccess"
  ESInfo:
    Type: "Custom::ESProxy"
    Condition: DontCreateDomain
    Properties:
      ServiceToken: !GetAtt
        - ESProxyLambda
        - Arn
      name: EMPTY
  ESInfoLambda:
    Type: "AWS::Lambda::Function"
    Condition: DontCreateDomain
    Properties:
      Code:
        ZipFile: >
          var aws=require('aws-sdk')

          aws.config.region=process.env.AWS_REGION

          var es=new aws.ES()


          exports.handler = function(event, context) {
              console.log(JSON.stringify(event,null,2))

              if(event.RequestType!=="Delete"){
                  es.describeElasticsearchDomain({
                      DomainName:event.ResourceProperties.name
                  }).promise()
                  .then(info=>{
                      send(event, context, SUCCESS,{
                          Name:DomainStatus.DomainName,
                          Arn:DomainStatus.ARN,
                          Endpoint:DomainStatus.Endpoints
                      })
                  })
                  .catch(x=>{
                      console.log(x)
                      send(event, context, FAILED)
                  })
                 
              }else{
                  send(event, context, SUCCESS)
              }
          }



          const SUCCESS = "SUCCESS";

          const FAILED = "FAILED";


          function send(event, context, responseStatus, responseData,
          physicalResourceId, noEcho) {

              var responseBody = JSON.stringify({
                  Status: responseStatus,
                  Reason: "See the details in CloudWatch Log Stream: " + context.logStreamName,
                  PhysicalResourceId: physicalResourceId || context.logStreamName,
                  StackId: event.StackId,
                  RequestId: event.RequestId,
                  LogicalResourceId: event.LogicalResourceId,
                  NoEcho: noEcho || false,
                  Data: responseData
              });

              console.log("Response body:\n", responseBody);

              var https = require("https");
              var url = require("url");

              var parsedUrl = url.parse(event.ResponseURL);
              var options = {
                  hostname: parsedUrl.hostname,
                  port: 443,
                  path: parsedUrl.path,
                  method: "PUT",
                  headers: {
                      "content-type": "",
                      "content-length": responseBody.length
                  }
              };

              var request = https.request(options, function(response) {
                  console.log("Status code: " + response.statusCode);
                  console.log("Status message: " + response.statusMessage);
                  context.done();
              });

              request.on("error", function(error) {
                  console.log("send(..) failed executing https.request(..): " + error);
                  context.done();
              });

              request.write(responseBody);
              request.end();
          }
      Handler: index.handler
      MemorySize: "128"
      Role: !GetAtt
        - ESProxyLambdaRole
        - Arn
      Runtime: nodejs16.x
      Timeout: 300
      VpcConfig: !If
        - VPCEnabled
        - SubnetIds:
            - !Ref "AWS::NoValue"
          SecurityGroupIds:
            - !Ref "AWS::NoValue"
        - !Ref "AWS::NoValue"
      TracingConfig: !If
        - XRAYEnabled
        - Mode: Active
        - !Ref "AWS::NoValue"
      Tags:
        - Key: Type
          Value: CustomResource
  FeedbackFirehose:
    Type: "AWS::KinesisFirehose::DeliveryStream"
    Properties:
      DeliveryStreamType: DirectPut
      DeliveryStreamEncryptionConfigurationInput:
        KeyType: AWS_OWNED_CMK
      ElasticsearchDestinationConfiguration:
        BufferingHints:
          IntervalInSeconds: 60
          SizeInMBs: 5
        DomainARN: !GetAtt
          - ESVar
          - ESArn
        IndexName: !Sub "${Var.FeedbackIndex}"
        IndexRotationPeriod: NoRotation
        RetryOptions:
          DurationInSeconds: 300
        RoleARN: !GetAtt
          - FirehoseESS3Role
          - Arn
        S3BackupMode: AllDocuments
        S3Configuration:
          BucketARN: !GetAtt
            - MetricsBucket
            - Arn
          BufferingHints:
            IntervalInSeconds: 60
            SizeInMBs: 5
          Prefix: feedback/
          CompressionFormat: UNCOMPRESSED
          RoleARN: !GetAtt
            - FirehoseESS3Role
            - Arn
        TypeName: ""
        VpcConfiguration: !If
          - VPCEnabled
          - RoleARN: !GetAtt
              - FirehoseESS3Role
              - Arn
            SubnetIds:
              - !Ref "AWS::NoValue"
            SecurityGroupIds:
              - !Ref "AWS::NoValue"
          - !Ref "AWS::NoValue"
  GeneralFirehose:
    Type: "AWS::KinesisFirehose::DeliveryStream"
    Properties:
      DeliveryStreamType: DirectPut
      DeliveryStreamEncryptionConfigurationInput:
        KeyType: AWS_OWNED_CMK
      ElasticsearchDestinationConfiguration:
        BufferingHints:
          IntervalInSeconds: 60
          SizeInMBs: 5
        DomainARN: !GetAtt
          - ESVar
          - ESArn
        IndexName: !Sub "${Var.MetricsIndex}"
        IndexRotationPeriod: NoRotation
        RetryOptions:
          DurationInSeconds: 300
        RoleARN: !GetAtt
          - FirehoseESS3Role
          - Arn
        S3BackupMode: AllDocuments
        S3Configuration:
          BucketARN: !GetAtt
            - MetricsBucket
            - Arn
          Prefix: metrics/
          BufferingHints:
            IntervalInSeconds: 60
            SizeInMBs: 5
          CompressionFormat: UNCOMPRESSED
          RoleARN: !GetAtt
            - FirehoseESS3Role
            - Arn
        TypeName: ""
        VpcConfiguration: !If
          - VPCEnabled
          - RoleARN: !GetAtt
              - FirehoseESS3Role
              - Arn
            SubnetIds:
              - !Ref "AWS::NoValue"
            SecurityGroupIds:
              - !Ref "AWS::NoValue"
          - !Ref "AWS::NoValue"
  MetricsBucket:
    Type: "AWS::S3::Bucket"
    DeletionPolicy: Delete
    Properties:
      BucketEncryption: !If
        - Encrypted
        - ServerSideEncryptionConfiguration:
            - ServerSideEncryptionByDefault:
                SSEAlgorithm: AES256
        - !Ref "AWS::NoValue"
      Tags:
        - Key: Use
          Value: Metrics
  MetricsBucketClear:
    Type: "Custom::S3Clear"
    DependsOn:
      - CFNInvokePolicy
    Properties:
      ServiceToken: !GetAtt
        - CFNLambda
        - Arn
      Bucket: !Ref MetricsBucket
  FirehoseESS3Role:
    Type: "AWS::IAM::Role"
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service: firehose.amazonaws.com
            Action: "sts:AssumeRole"
      Path: /
      Policies:
        - PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Sid: ""
                Effect: Allow
                Action:
                  - "s3:AbortMultipartUpload"
                  - "s3:GetBucketLocation"
                  - "s3:GetObject"
                  - "s3:ListBucket"
                  - "s3:ListBucketMultipartUploads"
                  - "s3:PutObject"
                Resource:
                  - !GetAtt
                    - MetricsBucket
                    - Arn
                  - !Join
                    - ""
                    - - !GetAtt
                        - MetricsBucket
                        - Arn
                      - /*
              - Sid: ""
                Effect: Allow
                Action:
                  - "lambda:InvokeFunction"
                  - "lambda:GetFunctionConfiguration"
                Resource:
                  - !Join
                    - ""
                    - - "arn:aws:lambda:"
                      - !Ref "AWS::Region"
                      - ":"
                      - !Ref "AWS::AccountId"
                      - >-
                        :function:%FIREHOSE_DEFAULT_FUNCTION%:%FIREHOSE_DEFAULT_VERSION%
              - Sid: ""
                Effect: Allow
                Action:
                  - "es:DescribeElasticsearchDomain"
                  - "es:DescribeElasticsearchDomains"
                  - "es:DescribeElasticsearchDomainConfig"
                  - "es:ESHttpPost"
                  - "es:ESHttpPut"
                  - "es:ESHttpGet"
                Resource:
                  - !GetAtt
                    - ESVar
                    - ESArn
                  - !Join
                    - ""
                    - - !GetAtt
                        - ESVar
                        - ESArn
                      - /*
              - Sid: ""
                Effect: Allow
                Action:
                  - "logs:PutLogEvents"
                Resource:
                  - !Join
                    - ""
                    - - "arn:aws:logs:"
                      - !Ref "AWS::Region"
                      - ":"
                      - !Ref "AWS::AccountId"
                      - ":log-group:/aws/kinesisfirehose/*"
              - Sid: ""
                Effect: Allow
                Action:
                  - "ec2:DescribeVpcs"
                  - "ec2:DescribeVpcAttribute"
                  - "ec2:DescribeSubnets"
                  - "ec2:DescribeSecurityGroups"
                  - "ec2:DescribeNetworkInterfaces"
                  - "ec2:CreateNetworkInterface"
                  - "ec2:CreateNetworkInterfacePermission"
                  - "ec2:DeleteNetworkInterface"
                Resource: "*"
          PolicyName: QnAFirehose
  ESCFNProxyLambda:
    Type: "AWS::Lambda::Function"
    Properties:
      Code:
        S3Bucket: aws-bigdata-blog-replica-ap-northeast-1
        S3Key: !Sub artifacts/aws-ai-qna-bot/lambda/proxy-es.zip
        S3ObjectVersion: !Ref ESProxyCodeVersion
      Environment:
        Variables:
          DEFAULT_SETTINGS_PARAM: !Ref DefaultQnABotSettings
          CUSTOM_SETTINGS_PARAM: !Ref CustomQnABotSettings
      Handler: index.resource
      MemorySize: "1408"
      Role: !GetAtt
        - ESProxyLambdaRole
        - Arn
      Runtime: nodejs16.x
      Timeout: 300
      VpcConfig: !If
        - VPCEnabled
        - SubnetIds:
            - !Ref "AWS::NoValue"
          SecurityGroupIds:
            - !Ref "AWS::NoValue"
        - !Ref "AWS::NoValue"
      TracingConfig: !If
        - XRAYEnabled
        - Mode: Active
        - !Ref "AWS::NoValue"
      Tags:
        - Key: Type
          Value: CustomResource
  MetricsIndex:
    Type: "Custom::ESProxy"
    Properties:
      ServiceToken: !GetAtt
        - ESCFNProxyLambda
        - Arn
      create:
        index: !Sub "${Var.MetricsIndex}"
        endpoint: !GetAtt
          - ESVar
          - ESAddress
        body: !Sub '{"settings":{}}'
  FeedbackIndex:
    Type: "Custom::ESProxy"
    Properties:
      ServiceToken: !GetAtt
        - ESCFNProxyLambda
        - Arn
      create:
        index: !Sub "${Var.FeedbackIndex}"
        endpoint: !GetAtt
          - ESVar
          - ESAddress
        body: !Sub '{"settings":{}}'
  Index:
    Type: "Custom::ESProxy"
    Properties:
      ServiceToken: !GetAtt
        - ESCFNProxyLambda
        - Arn
      create:
        index: !Sub "${Var.QnaIndex}"
        endpoint: !GetAtt
          - ESVar
          - ESAddress
        body: !Sub >-
          {"settings":{"number_of_shards":"1","analysis":{"filter":{"english_stop":{"type":"stop","stopwords":["a","an","and","are","as","at","be","but","by","for","if","in","into","is","it","not","of","on","or","such","that","the","their","then","there","these","they","this","to","was","will","with"]},"english_keywords":{"type":"keyword_marker","keywords":["example"]},"english_stemmer":{"type":"stemmer","language":"english"},"english_possessive_stemmer":{"type":"stemmer","language":"possessive_english"}},"analyzer":{"custom_english":{"type":"custom","tokenizer":"standard","filter":["english_possessive_stemmer","lowercase","english_stop","english_keywords","english_stemmer"]},"custom_english_unique":{"type":"custom","tokenizer":"standard","filter":["english_possessive_stemmer","lowercase","english_stop","english_keywords","english_stemmer","unique"]}}}},"mappings":{"properties":{"qid":{"type":"keyword"},"quniqueterms":{"type":"text","analyzer":"custom_english_unique"},"questions":{"type":"nested","properties":{"q":{"type":"text","analyzer":"custom_english"}}},"a":{"type":"text","analyzer":"custom_english"},"t":{"type":"text","analyzer":"whitespace"},"r":{"properties":{"imageUrl":{"type":"keyword"},"title":{"type":"text"}}},"l":{"type":"keyword"},"question":{"type":"text","analyzer":"custom_english"},"incorrectAnswers":{"type":"text","analyzer":"custom_english"},"correctAnswers":{"type":"text","analyzer":"custom_english"}}}}
  KibanaDashboards:
    Type: "Custom::ESProxy"
    DependsOn:
      - Index
    Properties:
      ServiceToken: !GetAtt
        - ESCFNProxyLambda
        - Arn
      create:
        endpoint: !GetAtt
          - ESVar
          - ESAddress
        path: /_plugin/kibana/api/kibana/dashboards/import?force=true
        method: POST
        headers:
          kbn-xsrf: kibana
        body:
          version: 7.9.1
          objects:
            - id: 052b1350-a37d-11ea-8370-0f1df276cae1
              type: dashboard
              namespaces:
                - default
              updated_at: "2021-04-19T22:23:40.946Z"
              version: WzcwNywxXQ==
              attributes:
                hits: "0"
                timeFrom: now/w
                timeTo: now/w
                refreshInterval:
                  value: "0"
                  pause: "true"
                description: >-
                  Visualize QnABot usage, see what your users are asking, and
                  use the "No Hits" and "Feedback" charts to assess where you
                  should add or tune QnABot content to make the bot smarter.
                title: QnABot Dashboard
                timeRestore: "false"
                version: "1"
                panelsJSON: >-
                  [{"embeddableConfig":{"legendOpen":false,"vis":{"legendOpen":true}},"gridData":{"h":15,"i":"fb115451-3b8a-436f-b916-8a04db4e9d70","w":17,"x":0,"y":0},"panelIndex":"fb115451-3b8a-436f-b916-8a04db4e9d70","version":"7.9.1","panelRefName":"panel_0"},{"embeddableConfig":{},"gridData":{"h":15,"i":"5e25d094-b045-4afe-953d-2d619b05b716","w":14,"x":34,"y":0},"panelIndex":"5e25d094-b045-4afe-953d-2d619b05b716","version":"7.9.1","panelRefName":"panel_1"},{"embeddableConfig":{"legendOpen":false,"vis":{"legendOpen":true}},"gridData":{"h":15,"i":"cf017f39-a5a3-4d3a-9561-862f4c2eb3c5","w":17,"x":17,"y":0},"panelIndex":"cf017f39-a5a3-4d3a-9561-862f4c2eb3c5","version":"7.9.1","panelRefName":"panel_2"},{"embeddableConfig":{},"gridData":{"h":15,"i":"b9b730b1-b3de-42f9-a4de-69197d934a93","w":24,"x":0,"y":15},"panelIndex":"b9b730b1-b3de-42f9-a4de-69197d934a93","version":"7.9.1","panelRefName":"panel_3"},{"embeddableConfig":{},"gridData":{"h":15,"i":"472ff8b6-83bf-4e4d-a8a5-44ce8f7e3dac","w":24,"x":24,"y":15},"panelIndex":"472ff8b6-83bf-4e4d-a8a5-44ce8f7e3dac","version":"7.9.1","panelRefName":"panel_4"},{"embeddableConfig":{},"gridData":{"h":15,"i":"92e5cbb2-fa56-4f15-b7b1-72c11e0bebfc","w":24,"x":0,"y":30},"panelIndex":"92e5cbb2-fa56-4f15-b7b1-72c11e0bebfc","version":"7.9.1","panelRefName":"panel_5"},{"embeddableConfig":{},"gridData":{"h":15,"i":"7ca7cdb0-2472-4eb0-bf7e-ae90f238f869","w":24,"x":24,"y":30},"panelIndex":"7ca7cdb0-2472-4eb0-bf7e-ae90f238f869","version":"7.9.1","panelRefName":"panel_6"},{"embeddableConfig":{},"gridData":{"h":8,"i":"cba70b74-3264-4153-87d2-68c24b552efa","w":10,"x":0,"y":45},"panelIndex":"cba70b74-3264-4153-87d2-68c24b552efa","version":"7.9.1","panelRefName":"panel_7"},{"embeddableConfig":{},"gridData":{"h":15,"i":"4fd7e920-26dd-4d02-8235-bcdff5725991","w":24,"x":10,"y":45},"panelIndex":"4fd7e920-26dd-4d02-8235-bcdff5725991","version":"7.9.1","panelRefName":"panel_8"}]
                optionsJSON: '{"hidePanelTitles":false,"useMargins":true}'
                kibanaSavedObjectMeta:
                  searchSourceJSON: '{"query":{"language":"kuery","query":""},"filter":[]}'
              references:
                - name: panel_0
                  id: a66d5ed0-a378-11ea-8370-0f1df276cae1
                  type: visualization
                - name: panel_1
                  id: d905b930-a37a-11ea-a346-0f81312f0c3c
                  type: visualization
                - name: panel_2
                  id: 12d24870-e16c-11ea-b423-5f0e2ad2220e
                  type: visualization
                - name: panel_3
                  id: 68d7c450-a37a-11ea-8370-0f1df276cae1
                  type: visualization
                - name: panel_4
                  id: d68ac390-a379-11ea-8370-0f1df276cae1
                  type: visualization
                - name: panel_5
                  id: 6759e170-a37b-11ea-8370-0f1df276cae1
                  type: visualization
                - name: panel_6
                  id: 985eb570-a37b-11ea-8370-0f1df276cae1
                  type: visualization
                - name: panel_7
                  id: 2031f610-a4c1-11ea-a012-c353d737e5ec
                  type: visualization
                - name: panel_8
                  id: 49e34620-9198-11eb-ab91-adc4ba11519d
                  type: visualization
              migrationVersion:
                dashboard: 7.3.0
            - id: a66d5ed0-a378-11ea-8370-0f1df276cae1
              type: visualization
              namespaces:
                - default
              updated_at: "2021-04-19T22:23:40.946Z"
              version: WzcwOCwxXQ==
              attributes:
                description: ""
                uiStateJSON: "{}"
                title: Requests
                version: "1"
                kibanaSavedObjectMeta:
                  searchSourceJSON: >-
                    {"query":{"language":"kuery","query":""},"filter":[],"indexRefName":"kibanaSavedObjectMeta.searchSourceJSON.index"}
                visState: >-
                  {"title":"Requests","type":"histogram","params":{"addLegend":true,"addTimeMarker":false,"addTooltip":true,"categoryAxes":[{"id":"CategoryAxis-1","labels":{"filter":true,"show":true,"truncate":100},"position":"bottom","scale":{"type":"linear"},"show":true,"style":{},"title":{},"type":"category"}],"dimensions":{"x":{"accessor":0,"format":{"id":"date","params":{"pattern":"HH:mm:ss"}},"params":{"date":true,"interval":"PT30S","format":"HH:mm:ss","bounds":{"min":"2020-07-06T21:55:15.220Z","max":"2020-07-06T22:25:15.220Z"}},"aggType":"date_histogram"},"y":[{"accessor":1,"format":{"id":"number"},"params":{},"aggType":"cardinality"}]},"grid":{"categoryLines":false},"labels":{"show":false},"legendPosition":"right","seriesParams":[{"data":{"id":"1","label":"Count"},"drawLinesBetweenPoints":true,"mode":"stacked","show":"true","showCircles":true,"type":"histogram","valueAxis":"ValueAxis-1"}],"thresholdLine":{"color":"#34130C","show":false,"style":"full","value":10,"width":1},"times":[],"type":"histogram","valueAxes":[{"id":"ValueAxis-1","labels":{"filter":false,"rotate":0,"show":true,"truncate":100},"name":"LeftAxis-1","position":"left","scale":{"mode":"normal","type":"linear"},"show":true,"style":{},"title":{"text":"Count"},"type":"value"}]},"aggs":[{"id":"1","enabled":true,"type":"count","schema":"metric","params":{}},{"id":"2","enabled":true,"type":"date_histogram","schema":"segment","params":{"field":"datetime","timeRange":{"from":"now-30m","to":"now"},"useNormalizedEsInterval":true,"interval":"auto","drop_partials":false,"min_doc_count":1,"extended_bounds":{},"customLabel":"Requests"}},{"id":"3","enabled":true,"type":"terms","schema":"group","params":{"field":"entireRequest.sentiment.keyword","orderBy":"1","order":"desc","size":5,"otherBucket":false,"otherBucketLabel":"Other","missingBucket":false,"missingBucketLabel":"Missing","customLabel":"Sentiment"}}]}
              references:
                - name: kibanaSavedObjectMeta.searchSourceJSON.index
                  id: Metrics
                  type: index-pattern
              migrationVersion:
                visualization: 7.8.0
            - id: d905b930-a37a-11ea-a346-0f81312f0c3c
              type: visualization
              namespaces:
                - default
              updated_at: "2021-04-19T22:23:40.946Z"
              version: WzcwOSwxXQ==
              attributes:
                description: ""
                uiStateJSON: "{}"
                title: Client Types
                version: "1"
                kibanaSavedObjectMeta:
                  searchSourceJSON: >-
                    {"query":{"query":"","language":"kuery"},"filter":[],"indexRefName":"kibanaSavedObjectMeta.searchSourceJSON.index"}
                visState: >-
                  {"title":"Client
                  Types","type":"pie","params":{"type":"pie","addTooltip":true,"addLegend":true,"legendPosition":"right","isDonut":false,"labels":{"show":false,"values":true,"last_level":true,"truncate":100},"dimensions":{"metric":{"accessor":1,"format":{"id":"number"},"params":{},"aggType":"count"},"buckets":[{"accessor":0,"format":{"id":"terms","params":{"id":"string","otherBucketLabel":"Other","missingBucketLabel":"Missing"}},"params":{},"aggType":"terms"}]}},"aggs":[{"id":"1","enabled":true,"type":"count","schema":"metric","params":{}},{"id":"2","enabled":true,"type":"terms","schema":"segment","params":{"field":"clientType.keyword","orderBy":"1","order":"desc","size":5,"otherBucket":false,"otherBucketLabel":"Other","missingBucket":false,"missingBucketLabel":"Missing","customLabel":"Client
                  Types"}}]}
              references:
                - name: kibanaSavedObjectMeta.searchSourceJSON.index
                  id: Metrics
                  type: index-pattern
              migrationVersion:
                visualization: 7.8.0
            - id: 12d24870-e16c-11ea-b423-5f0e2ad2220e
              type: visualization
              namespaces:
                - default
              updated_at: "2021-04-19T22:23:40.946Z"
              version: WzcxMCwxXQ==
              attributes:
                description: ""
                uiStateJSON: "{}"
                title: Requests AnswerSource
                version: "1"
                kibanaSavedObjectMeta:
                  searchSourceJSON: >-
                    {"query":{"language":"kuery","query":""},"filter":[],"indexRefName":"kibanaSavedObjectMeta.searchSourceJSON.index"}
                visState: >-
                  {"type":"histogram","aggs":[{"id":"1","enabled":true,"type":"count","schema":"metric","params":{}},{"id":"2","enabled":true,"type":"date_histogram","schema":"segment","params":{"field":"datetime","timeRange":{"from":"2020-08-18T15:44:48.334Z","to":"2020-08-18T15:59:17.582Z"},"useNormalizedEsInterval":true,"scaleMetricValues":false,"interval":"auto","drop_partials":false,"min_doc_count":1,"extended_bounds":{},"customLabel":"Requests"}},{"id":"3","enabled":true,"type":"terms","schema":"group","params":{"field":"entireResponse.answerSource.keyword","orderBy":"1","order":"desc","size":5,"otherBucket":false,"otherBucketLabel":"Other","missingBucket":false,"missingBucketLabel":"Missing","customLabel":"Source"}}],"params":{"addLegend":true,"addTimeMarker":false,"addTooltip":true,"categoryAxes":[{"id":"CategoryAxis-1","labels":{"filter":true,"show":true,"truncate":100},"position":"bottom","scale":{"type":"linear"},"show":true,"style":{},"title":{},"type":"category"}],"dimensions":{"x":{"accessor":0,"aggType":"date_histogram","format":{"id":"date","params":{"pattern":"HH:mm:ss"}},"params":{"bounds":{"max":"2020-07-06T22:25:15.220Z","min":"2020-07-06T21:55:15.220Z"},"date":true,"format":"HH:mm:ss","interval":"PT30S"}},"y":[{"accessor":1,"aggType":"cardinality","format":{"id":"number"},"params":{}}]},"grid":{"categoryLines":false},"labels":{"show":false},"legendPosition":"right","seriesParams":[{"data":{"id":"1","label":"Count"},"drawLinesBetweenPoints":true,"mode":"stacked","show":"true","showCircles":true,"type":"histogram","valueAxis":"ValueAxis-1"}],"thresholdLine":{"color":"#34130C","show":false,"style":"full","value":10,"width":1},"times":[],"type":"histogram","valueAxes":[{"id":"ValueAxis-1","labels":{"filter":false,"rotate":0,"show":true,"truncate":100},"name":"LeftAxis-1","position":"left","scale":{"mode":"normal","type":"linear"},"show":true,"style":{},"title":{"text":"Count"},"type":"value"}]},"title":"Requests
                  AnswerSource"}
              references:
                - name: kibanaSavedObjectMeta.searchSourceJSON.index
                  id: Metrics
                  type: index-pattern
              migrationVersion:
                visualization: 7.8.0
            - id: 68d7c450-a37a-11ea-8370-0f1df276cae1
              type: visualization
              namespaces:
                - default
              updated_at: "2021-04-19T22:23:40.946Z"
              version: WzcxMSwxXQ==
              attributes:
                description: ""
                uiStateJSON: "{}"
                title: Logged Utterances
                version: "1"
                kibanaSavedObjectMeta:
                  searchSourceJSON: >-
                    {"query":{"query":"","language":"kuery"},"filter":[],"indexRefName":"kibanaSavedObjectMeta.searchSourceJSON.index"}
                visState: >-
                  {"title":"Logged
                  Utterances","type":"tagcloud","params":{"scale":"linear","orientation":"single","minFontSize":18,"maxFontSize":72,"showLabel":true,"metric":{"type":"vis_dimension","accessor":0,"format":{"id":"string","params":{}}}},"aggs":[{"id":"1","enabled":true,"type":"count","schema":"metric","params":{}},{"id":"2","enabled":true,"type":"terms","schema":"segment","params":{"field":"utterance.keyword","orderBy":"1","order":"desc","size":1000,"otherBucket":false,"otherBucketLabel":"Other","missingBucket":false,"missingBucketLabel":"Missing","customLabel":"Logged
                  Utterances"}}]}
              references:
                - name: kibanaSavedObjectMeta.searchSourceJSON.index
                  id: Metrics
                  type: index-pattern
              migrationVersion:
                visualization: 7.8.0
            - id: d68ac390-a379-11ea-8370-0f1df276cae1
              type: visualization
              namespaces:
                - default
              updated_at: "2021-04-19T22:23:40.946Z"
              version: WzcxMiwxXQ==
              attributes:
                description: ""
                uiStateJSON: "{}"
                title: No Hits
                version: "1"
                kibanaSavedObjectMeta:
                  searchSourceJSON: >-
                    {"query":{"query":"entireResponse.got_hits:0","language":"kuery"},"filter":[],"indexRefName":"kibanaSavedObjectMeta.searchSourceJSON.index"}
                visState: >-
                  {"title":"No
                  Hits","type":"tagcloud","params":{"scale":"linear","orientation":"single","minFontSize":18,"maxFontSize":72,"showLabel":true,"metric":{"type":"vis_dimension","accessor":0,"format":{"id":"string","params":{}}}},"aggs":[{"id":"1","enabled":true,"type":"count","schema":"metric","params":{}},{"id":"2","enabled":true,"type":"terms","schema":"segment","params":{"field":"utterance.keyword","orderBy":"1","order":"desc","size":1000,"otherBucket":false,"otherBucketLabel":"Other","missingBucket":false,"missingBucketLabel":"Missing","customLabel":"No
                  Hits"}}]}
              references:
                - name: kibanaSavedObjectMeta.searchSourceJSON.index
                  id: Metrics
                  type: index-pattern
              migrationVersion:
                visualization: 7.8.0
            - id: 6759e170-a37b-11ea-8370-0f1df276cae1
              type: visualization
              namespaces:
                - default
              updated_at: "2021-04-19T22:23:40.946Z"
              version: WzcxMywxXQ==
              attributes:
                description: ""
                uiStateJSON: "{}"
                title: Positive Feedback
                version: "1"
                kibanaSavedObjectMeta:
                  searchSourceJSON: >-
                    {"query":{"query":"feedback=correct","language":"kuery"},"filter":[],"indexRefName":"kibanaSavedObjectMeta.searchSourceJSON.index"}
                visState: >-
                  {"title":"Positive
                  Feedback","type":"tagcloud","params":{"scale":"linear","orientation":"single","minFontSize":18,"maxFontSize":72,"showLabel":true,"metric":{"type":"vis_dimension","accessor":0,"format":{"id":"string","params":{}}}},"aggs":[{"id":"1","enabled":true,"type":"count","schema":"metric","params":{}},{"id":"2","enabled":true,"type":"terms","schema":"segment","params":{"field":"utterance.keyword","orderBy":"1","order":"desc","size":100,"otherBucket":false,"otherBucketLabel":"Other","missingBucket":false,"missingBucketLabel":"Missing","customLabel":"Thumbs
                  Up"}}]}
              references:
                - name: kibanaSavedObjectMeta.searchSourceJSON.index
                  id: Feedback
                  type: index-pattern
              migrationVersion:
                visualization: 7.8.0
            - id: 985eb570-a37b-11ea-8370-0f1df276cae1
              type: visualization
              namespaces:
                - default
              updated_at: "2021-04-19T22:23:40.946Z"
              version: WzcxNCwxXQ==
              attributes:
                description: ""
                uiStateJSON: "{}"
                title: Negative Feedback
                version: "1"
                kibanaSavedObjectMeta:
                  searchSourceJSON: >-
                    {"query":{"query":"feedback=incorrect","language":"kuery"},"filter":[],"indexRefName":"kibanaSavedObjectMeta.searchSourceJSON.index"}
                visState: >-
                  {"title":"Negative
                  Feedback","type":"tagcloud","params":{"scale":"linear","orientation":"single","minFontSize":18,"maxFontSize":72,"showLabel":true,"metric":{"type":"vis_dimension","accessor":1,"format":{"id":"string","params":{}}},"bucket":{"type":"vis_dimension","accessor":0,"format":{"id":"terms","params":{"id":"string","otherBucketLabel":"Other","missingBucketLabel":"Missing"}}}},"aggs":[{"id":"1","enabled":true,"type":"count","schema":"metric","params":{}},{"id":"2","enabled":true,"type":"terms","schema":"segment","params":{"field":"utterance.keyword","orderBy":"1","order":"desc","size":100,"otherBucket":false,"otherBucketLabel":"Other","missingBucket":false,"missingBucketLabel":"Missing","customLabel":"Thumbs
                  Down"}}]}
              references:
                - name: kibanaSavedObjectMeta.searchSourceJSON.index
                  id: Feedback
                  type: index-pattern
              migrationVersion:
                visualization: 7.8.0
            - id: 2031f610-a4c1-11ea-a012-c353d737e5ec
              type: visualization
              namespaces:
                - default
              updated_at: "2021-04-19T22:23:40.946Z"
              version: WzcxNSwxXQ==
              attributes:
                description: ""
                uiStateJSON: "{}"
                title: QnAItemCount
                version: "1"
                kibanaSavedObjectMeta:
                  searchSourceJSON: >-
                    {"query":{"query":"","language":"kuery"},"filter":[],"indexRefName":"kibanaSavedObjectMeta.searchSourceJSON.index"}
                visState: >-
                  {"title":"QnAItemCount","type":"metric","params":{"metric":{"percentageMode":false,"useRanges":false,"colorSchema":"Green
                  to
                  Red","metricColorMode":"None","colorsRange":[{"type":"range","from":0,"to":10000}],"labels":{"show":true},"invertColors":false,"style":{"bgFill":"#000","bgColor":false,"labelColor":false,"subText":"","fontSize":60}},"dimensions":{"metrics":[{"type":"vis_dimension","accessor":0,"format":{"id":"number","params":{}}}]},"addTooltip":true,"addLegend":false,"type":"metric"},"aggs":[{"id":"1","enabled":true,"type":"count","schema":"metric","params":{"customLabel":"QnA
                  Item Count"}}]}
              references:
                - name: kibanaSavedObjectMeta.searchSourceJSON.index
                  id: QnaItems
                  type: index-pattern
              migrationVersion:
                visualization: 7.8.0
            - id: 49e34620-9198-11eb-ab91-adc4ba11519d
              type: visualization
              namespaces:
                - default
              updated_at: "2021-04-19T22:23:40.946Z"
              version: WzcxNiwxXQ==
              attributes:
                description: ""
                uiStateJSON: >-
                  {"vis":{"params":{"sort":{"columnIndex":0,"direction":"asc"}}}}
                title: Answer Sources
                version: "1"
                kibanaSavedObjectMeta:
                  searchSourceJSON: >-
                    {"query":{"query":"","language":"kuery"},"filter":[],"indexRefName":"kibanaSavedObjectMeta.searchSourceJSON.index"}
                visState: >-
                  {"title":"Answer
                  Sources","type":"table","aggs":[{"id":"1","enabled":true,"type":"count","params":{},"schema":"metric"},{"id":"2","enabled":true,"type":"terms","params":{"field":"entireResponse.result.answersource.keyword","orderBy":"1","order":"desc","size":5,"otherBucket":false,"otherBucketLabel":"Other","missingBucket":false,"missingBucketLabel":"Missing","customLabel":"Answer
                  Source"},"schema":"bucket"}],"params":{"perPage":10,"showPartialRows":false,"showMetricsAtAllLevels":false,"sort":{"columnIndex":null,"direction":null},"showTotal":false,"totalFunc":"sum","percentageCol":""}}
              references:
                - name: kibanaSavedObjectMeta.searchSourceJSON.index
                  id: Metrics
                  type: index-pattern
              migrationVersion:
                visualization: 7.8.0
            - id: Metrics
              type: index-pattern
              namespaces:
                - default
              updated_at: "2021-04-19T22:23:40.946Z"
              version: WzcxNywxXQ==
              attributes:
                timeFieldName: datetime
                fields: >-
                  [{"name":"_id","type":"string","esTypes":["_id"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":false},{"name":"_index","type":"string","esTypes":["_index"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":false},{"name":"_score","type":"number","count":0,"scripted":false,"searchable":false,"aggregatable":false,"readFromDocValues":false},{"name":"_source","type":"_source","esTypes":["_source"],"count":0,"scripted":false,"searchable":false,"aggregatable":false,"readFromDocValues":false},{"name":"_type","type":"string","esTypes":["_type"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":false},{"name":"answer","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"answer.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"answer"}}},{"name":"clientType","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"clientType.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"clientType"}}},{"name":"datetime","type":"date","esTypes":["date"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true},{"name":"entireRequest._clientType","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"entireRequest._clientType.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"entireRequest._clientType"}}},{"name":"entireRequest._event.bot.alias","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"entireRequest._event.bot.alias.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"entireRequest._event.bot.alias"}}},{"name":"entireRequest._event.bot.name","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"entireRequest._event.bot.name.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"entireRequest._event.bot.name"}}},{"name":"entireRequest._event.bot.version","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"entireRequest._event.bot.version.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"entireRequest._event.bot.version"}}},{"name":"entireRequest._event.currentIntent.confirmationStatus","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"entireRequest._event.currentIntent.confirmationStatus.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"entireRequest._event.currentIntent.confirmationStatus"}}},{"name":"entireRequest._event.currentIntent.name","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"entireRequest._event.currentIntent.name.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"entireRequest._event.currentIntent.name"}}},{"name":"entireRequest._event.currentIntent.slotDetails.slot.originalValue","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"entireRequest._event.currentIntent.slotDetails.slot.originalValue.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"entireRequest._event.currentIntent.slotDetails.slot.originalValue"}}},{"name":"entireRequest._event.currentIntent.slotDetails.slot.resolutions.value","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"entireRequest._event.currentIntent.slotDetails.slot.resolutions.value.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"entireRequest._event.currentIntent.slotDetails.slot.resolutions.value"}}},{"name":"entireRequest._event.currentIntent.slots.slot","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"entireRequest._event.currentIntent.slots.slot.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"entireRequest._event.currentIntent.slots.slot"}}},{"name":"entireRequest._event.errorFound","type":"boolean","esTypes":["boolean"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true},{"name":"entireRequest._event.inputTranscript","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"entireRequest._event.inputTranscript.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"entireRequest._event.inputTranscript"}}},{"name":"entireRequest._event.invocationSource","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"entireRequest._event.invocationSource.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"entireRequest._event.invocationSource"}}},{"name":"entireRequest._event.messageVersion","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"entireRequest._event.messageVersion.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"entireRequest._event.messageVersion"}}},{"name":"entireRequest._event.outputDialogMode","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"entireRequest._event.outputDialogMode.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"entireRequest._event.outputDialogMode"}}},{"name":"entireRequest._event.recentIntentSummaryView.confirmationStatus","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"entireRequest._event.recentIntentSummaryView.confirmationStatus.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"entireRequest._event.recentIntentSummaryView.confirmationStatus"}}},{"name":"entireRequest._event.recentIntentSummaryView.dialogActionType","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"entireRequest._event.recentIntentSummaryView.dialogActionType.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"entireRequest._event.recentIntentSummaryView.dialogActionType"}}},{"name":"entireRequest._event.recentIntentSummaryView.fulfillmentState","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"entireRequest._event.recentIntentSummaryView.fulfillmentState.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"entireRequest._event.recentIntentSummaryView.fulfillmentState"}}},{"name":"entireRequest._event.recentIntentSummaryView.intentName","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"entireRequest._event.recentIntentSummaryView.intentName.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"entireRequest._event.recentIntentSummaryView.intentName"}}},{"name":"entireRequest._event.recentIntentSummaryView.slots.slot","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"entireRequest._event.recentIntentSummaryView.slots.slot.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"entireRequest._event.recentIntentSummaryView.slots.slot"}}},{"name":"entireRequest._event.sessionAttributes.qnabot_gotanswer","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"entireRequest._event.sessionAttributes.qnabot_gotanswer.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"entireRequest._event.sessionAttributes.qnabot_gotanswer"}}},{"name":"entireRequest._event.sessionAttributes.qnabot_qid","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"entireRequest._event.sessionAttributes.qnabot_qid.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"entireRequest._event.sessionAttributes.qnabot_qid"}}},{"name":"entireRequest._event.sessionAttributes.qnabotcontext","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"entireRequest._event.sessionAttributes.qnabotcontext.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"entireRequest._event.sessionAttributes.qnabotcontext"}}},{"name":"entireRequest._event.userId","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"entireRequest._event.userId.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"entireRequest._event.userId"}}},{"name":"entireRequest._info.es.address","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"entireRequest._info.es.address.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"entireRequest._info.es.address"}}},{"name":"entireRequest._info.es.index","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"entireRequest._info.es.index.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"entireRequest._info.es.index"}}},{"name":"entireRequest._info.es.service.proxy","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"entireRequest._info.es.service.proxy.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"entireRequest._info.es.service.proxy"}}},{"name":"entireRequest._info.es.service.qid","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"entireRequest._info.es.service.qid.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"entireRequest._info.es.service.qid"}}},{"name":"entireRequest._info.es.type","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"entireRequest._info.es.type.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"entireRequest._info.es.type"}}},{"name":"entireRequest._preferredResponseType","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"entireRequest._preferredResponseType.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"entireRequest._preferredResponseType"}}},{"name":"entireRequest._settings.ALT_SEARCH_KENDRA_INDEXES","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"entireRequest._settings.ALT_SEARCH_KENDRA_INDEXES.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"entireRequest._settings.ALT_SEARCH_KENDRA_INDEXES"}}},{"name":"entireRequest._settings.DEFAULT_ALEXA_LAUNCH_MESSAGE","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"entireRequest._settings.DEFAULT_ALEXA_LAUNCH_MESSAGE.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"entireRequest._settings.DEFAULT_ALEXA_LAUNCH_MESSAGE"}}},{"name":"entireRequest._settings.DEFAULT_ALEXA_STOP_MESSAGE","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"entireRequest._settings.DEFAULT_ALEXA_STOP_MESSAGE.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"entireRequest._settings.DEFAULT_ALEXA_STOP_MESSAGE"}}},{"name":"entireRequest._settings.DEFAULT_USER_POOL_JWKS_URL","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"entireRequest._settings.DEFAULT_USER_POOL_JWKS_URL.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"entireRequest._settings.DEFAULT_USER_POOL_JWKS_URL"}}},{"name":"entireRequest._settings.ELICIT_RESPONSE_BOT_FAILURE_MESSAGE","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"entireRequest._settings.ELICIT_RESPONSE_BOT_FAILURE_MESSAGE.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"entireRequest._settings.ELICIT_RESPONSE_BOT_FAILURE_MESSAGE"}}},{"name":"entireRequest._settings.ELICIT_RESPONSE_DEFAULT_MSG","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"entireRequest._settings.ELICIT_RESPONSE_DEFAULT_MSG.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"entireRequest._settings.ELICIT_RESPONSE_DEFAULT_MSG"}}},{"name":"entireRequest._settings.ELICIT_RESPONSE_MAX_RETRIES","type":"number","esTypes":["long"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true},{"name":"entireRequest._settings.ELICIT_RESPONSE_RETRY_MESSAGE","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"entireRequest._settings.ELICIT_RESPONSE_RETRY_MESSAGE.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"entireRequest._settings.ELICIT_RESPONSE_RETRY_MESSAGE"}}},{"name":"entireRequest._settings.EMPTYMESSAGE","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"entireRequest._settings.EMPTYMESSAGE.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"entireRequest._settings.EMPTYMESSAGE"}}},{"name":"entireRequest._settings.ENABLE_DEBUG_RESPONSES","type":"boolean","esTypes":["boolean"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true},{"name":"entireRequest._settings.ENABLE_MULTI_LANGUAGE_SUPPORT","type":"boolean","esTypes":["boolean"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true},{"name":"entireRequest._settings.ENABLE_REDACTING","type":"boolean","esTypes":["boolean"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true},{"name":"entireRequest._settings.ENABLE_SENTIMENT_SUPPORT","type":"boolean","esTypes":["boolean"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true},{"name":"entireRequest._settings.ENFORCE_VERIFIED_IDENTITY","type":"boolean","esTypes":["boolean"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true},{"name":"entireRequest._settings.ERRORMESSAGE","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"entireRequest._settings.ERRORMESSAGE.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"entireRequest._settings.ERRORMESSAGE"}}},{"name":"entireRequest._settings.ES_KEYWORD_SYNTAX_TYPES","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"entireRequest._settings.ES_KEYWORD_SYNTAX_TYPES.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"entireRequest._settings.ES_KEYWORD_SYNTAX_TYPES"}}},{"name":"entireRequest._settings.ES_MINIMUM_SHOULD_MATCH","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"entireRequest._settings.ES_MINIMUM_SHOULD_MATCH.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"entireRequest._settings.ES_MINIMUM_SHOULD_MATCH"}}},{"name":"entireRequest._settings.ES_NO_HITS_QUESTION","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"entireRequest._settings.ES_NO_HITS_QUESTION.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"entireRequest._settings.ES_NO_HITS_QUESTION"}}},{"name":"entireRequest._settings.ES_PHRASE_BOOST","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"entireRequest._settings.ES_PHRASE_BOOST.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"entireRequest._settings.ES_PHRASE_BOOST"}}},{"name":"entireRequest._settings.ES_SCORE_ANSWER_FIELD","type":"boolean","esTypes":["boolean"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true},{"name":"entireRequest._settings.ES_SYNTAX_CONFIDENCE_LIMIT","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"entireRequest._settings.ES_SYNTAX_CONFIDENCE_LIMIT.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"entireRequest._settings.ES_SYNTAX_CONFIDENCE_LIMIT"}}},{"name":"entireRequest._settings.ES_USE_FUZZY_MATCH","type":"boolean","esTypes":["boolean"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true},{"name":"entireRequest._settings.ES_USE_KEYWORD_FILTERS","type":"boolean","esTypes":["boolean"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true},{"name":"entireRequest._settings.KENDRA_FAQ_CONFIG_MAX_RETRIES","type":"number","esTypes":["long"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true},{"name":"entireRequest._settings.KENDRA_FAQ_CONFIG_RETRY_DELAY","type":"number","esTypes":["long"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true},{"name":"entireRequest._settings.KENDRA_FAQ_ES_FALLBACK","type":"boolean","esTypes":["boolean"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true},{"name":"entireRequest._settings.KENDRA_FAQ_INDEX","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"entireRequest._settings.KENDRA_FAQ_INDEX.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"entireRequest._settings.KENDRA_FAQ_INDEX"}}},{"name":"entireRequest._settings.MINIMUM_CONFIDENCE_SCORE","type":"number","esTypes":["float"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true},{"name":"entireRequest._settings.NO_VERIFIED_IDENTITY_QUESTION","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"entireRequest._settings.NO_VERIFIED_IDENTITY_QUESTION.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"entireRequest._settings.NO_VERIFIED_IDENTITY_QUESTION"}}},{"name":"entireRequest._settings.REDACTING_REGEX","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"entireRequest._settings.REDACTING_REGEX.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"entireRequest._settings.REDACTING_REGEX"}}},{"name":"entireRequest._settings.SMS_HINT_REMINDER","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"entireRequest._settings.SMS_HINT_REMINDER.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"entireRequest._settings.SMS_HINT_REMINDER"}}},{"name":"entireRequest._settings.SMS_HINT_REMINDER_ENABLE","type":"boolean","esTypes":["boolean"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true},{"name":"entireRequest._settings.SMS_HINT_REMINDER_INTERVAL_HRS","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"entireRequest._settings.SMS_HINT_REMINDER_INTERVAL_HRS.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"entireRequest._settings.SMS_HINT_REMINDER_INTERVAL_HRS"}}},{"name":"entireRequest._type","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"entireRequest._type.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"entireRequest._type"}}},{"name":"entireRequest._userId","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"entireRequest._userId.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"entireRequest._userId"}}},{"name":"entireRequest._userInfo.FirstSeen","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"entireRequest._userInfo.FirstSeen.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"entireRequest._userInfo.FirstSeen"}}},{"name":"entireRequest._userInfo.InteractionCount","type":"number","esTypes":["long"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true},{"name":"entireRequest._userInfo.LastSeen","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"entireRequest._userInfo.LastSeen.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"entireRequest._userInfo.LastSeen"}}},{"name":"entireRequest._userInfo.TimeSinceLastInteraction","type":"number","esTypes":["float"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true},{"name":"entireRequest._userInfo.UserId","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"entireRequest._userInfo.UserId.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"entireRequest._userInfo.UserId"}}},{"name":"entireRequest._userInfo.isVerifiedIdentity","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"entireRequest._userInfo.isVerifiedIdentity.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"entireRequest._userInfo.isVerifiedIdentity"}}},{"name":"entireRequest.kendraResultsCached","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"entireRequest.kendraResultsCached.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"entireRequest.kendraResultsCached"}}},{"name":"entireRequest.question","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"entireRequest.question.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"entireRequest.question"}}},{"name":"entireRequest.sentiment","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"entireRequest.sentiment.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"entireRequest.sentiment"}}},{"name":"entireRequest.session.qnabot_gotanswer","type":"boolean","esTypes":["boolean"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true},{"name":"entireRequest.session.qnabot_qid","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"entireRequest.session.qnabot_qid.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"entireRequest.session.qnabot_qid"}}},{"name":"entireRequest.session.qnabotcontext.kendra.kendraIndexId","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"entireRequest.session.qnabotcontext.kendra.kendraIndexId.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"entireRequest.session.qnabotcontext.kendra.kendraIndexId"}}},{"name":"entireRequest.session.qnabotcontext.kendra.kendraQueryId","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"entireRequest.session.qnabotcontext.kendra.kendraQueryId.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"entireRequest.session.qnabotcontext.kendra.kendraQueryId"}}},{"name":"entireRequest.session.qnabotcontext.kendra.kendraResponsibleQid","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"entireRequest.session.qnabotcontext.kendra.kendraResponsibleQid.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"entireRequest.session.qnabotcontext.kendra.kendraResponsibleQid"}}},{"name":"entireRequest.session.qnabotcontext.kendra.kendraResultId","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"entireRequest.session.qnabotcontext.kendra.kendraResultId.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"entireRequest.session.qnabotcontext.kendra.kendraResultId"}}},{"name":"entireRequest.session.qnabotcontext.navigation.hasParent","type":"boolean","esTypes":["boolean"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true},{"name":"entireRequest.session.qnabotcontext.navigation.next","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"entireRequest.session.qnabotcontext.navigation.next.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"entireRequest.session.qnabotcontext.navigation.next"}}},{"name":"entireRequest.session.qnabotcontext.previous.a","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"entireRequest.session.qnabotcontext.previous.a.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"entireRequest.session.qnabotcontext.previous.a"}}},{"name":"entireRequest.session.qnabotcontext.previous.q","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"entireRequest.session.qnabotcontext.previous.q.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"entireRequest.session.qnabotcontext.previous.q"}}},{"name":"entireRequest.session.qnabotcontext.previous.qid","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"entireRequest.session.qnabotcontext.previous.qid.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"entireRequest.session.qnabotcontext.previous.qid"}}},{"name":"entireResponse._userInfo.FirstSeen","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"entireResponse._userInfo.FirstSeen.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"entireResponse._userInfo.FirstSeen"}}},{"name":"entireResponse._userInfo.InteractionCount","type":"number","esTypes":["long"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true},{"name":"entireResponse._userInfo.LastSeen","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"entireResponse._userInfo.LastSeen.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"entireResponse._userInfo.LastSeen"}}},{"name":"entireResponse._userInfo.TimeSinceLastInteraction","type":"number","esTypes":["float"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true},{"name":"entireResponse._userInfo.UserId","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"entireResponse._userInfo.UserId.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"entireResponse._userInfo.UserId"}}},{"name":"entireResponse._userInfo.isVerifiedIdentity","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"entireResponse._userInfo.isVerifiedIdentity.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"entireResponse._userInfo.isVerifiedIdentity"}}},{"name":"entireResponse.answerSource","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"entireResponse.answerSource.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"entireResponse.answerSource"}}},{"name":"entireResponse.card.send","type":"boolean","esTypes":["boolean"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true},{"name":"entireResponse.card.text","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"entireResponse.card.text.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"entireResponse.card.text"}}},{"name":"entireResponse.card.title","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"entireResponse.card.title.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"entireResponse.card.title"}}},{"name":"entireResponse.card.url","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"entireResponse.card.url.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"entireResponse.card.url"}}},{"name":"entireResponse.got_hits","type":"number","esTypes":["long"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true},{"name":"entireResponse.kendraResultsCached.QueryId","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"entireResponse.kendraResultsCached.QueryId.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"entireResponse.kendraResultsCached.QueryId"}}},{"name":"entireResponse.kendraResultsCached.ResultItems.AdditionalAttributes.Key","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"entireResponse.kendraResultsCached.ResultItems.AdditionalAttributes.Key.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"entireResponse.kendraResultsCached.ResultItems.AdditionalAttributes.Key"}}},{"name":"entireResponse.kendraResultsCached.ResultItems.AdditionalAttributes.Value.TextWithHighlightsValue.Highlights.BeginOffset","type":"number","esTypes":["long"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true},{"name":"entireResponse.kendraResultsCached.ResultItems.AdditionalAttributes.Value.TextWithHighlightsValue.Highlights.EndOffset","type":"number","esTypes":["long"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true},{"name":"entireResponse.kendraResultsCached.ResultItems.AdditionalAttributes.Value.TextWithHighlightsValue.Highlights.TopAnswer","type":"boolean","esTypes":["boolean"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true},{"name":"entireResponse.kendraResultsCached.ResultItems.AdditionalAttributes.Value.TextWithHighlightsValue.Text","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"entireResponse.kendraResultsCached.ResultItems.AdditionalAttributes.Value.TextWithHighlightsValue.Text.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"entireResponse.kendraResultsCached.ResultItems.AdditionalAttributes.Value.TextWithHighlightsValue.Text"}}},{"name":"entireResponse.kendraResultsCached.ResultItems.AdditionalAttributes.ValueType","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"entireResponse.kendraResultsCached.ResultItems.AdditionalAttributes.ValueType.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"entireResponse.kendraResultsCached.ResultItems.AdditionalAttributes.ValueType"}}},{"name":"entireResponse.kendraResultsCached.ResultItems.DocumentAttributes.Key","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"entireResponse.kendraResultsCached.ResultItems.DocumentAttributes.Key.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"entireResponse.kendraResultsCached.ResultItems.DocumentAttributes.Key"}}},{"name":"entireResponse.kendraResultsCached.ResultItems.DocumentAttributes.Value.StringValue","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"entireResponse.kendraResultsCached.ResultItems.DocumentAttributes.Value.StringValue.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"entireResponse.kendraResultsCached.ResultItems.DocumentAttributes.Value.StringValue"}}},{"name":"entireResponse.kendraResultsCached.ResultItems.DocumentExcerpt.Highlights.BeginOffset","type":"number","esTypes":["long"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true},{"name":"entireResponse.kendraResultsCached.ResultItems.DocumentExcerpt.Highlights.EndOffset","type":"number","esTypes":["long"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true},{"name":"entireResponse.kendraResultsCached.ResultItems.DocumentExcerpt.Highlights.TopAnswer","type":"boolean","esTypes":["boolean"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true},{"name":"entireResponse.kendraResultsCached.ResultItems.DocumentExcerpt.Text","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"entireResponse.kendraResultsCached.ResultItems.DocumentExcerpt.Text.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"entireResponse.kendraResultsCached.ResultItems.DocumentExcerpt.Text"}}},{"name":"entireResponse.kendraResultsCached.ResultItems.DocumentId","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"entireResponse.kendraResultsCached.ResultItems.DocumentId.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"entireResponse.kendraResultsCached.ResultItems.DocumentId"}}},{"name":"entireResponse.kendraResultsCached.ResultItems.DocumentTitle.Highlights.BeginOffset","type":"number","esTypes":["long"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true},{"name":"entireResponse.kendraResultsCached.ResultItems.DocumentTitle.Highlights.EndOffset","type":"number","esTypes":["long"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true},{"name":"entireResponse.kendraResultsCached.ResultItems.DocumentTitle.Highlights.TopAnswer","type":"boolean","esTypes":["boolean"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true},{"name":"entireResponse.kendraResultsCached.ResultItems.DocumentTitle.Text","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"entireResponse.kendraResultsCached.ResultItems.DocumentTitle.Text.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"entireResponse.kendraResultsCached.ResultItems.DocumentTitle.Text"}}},{"name":"entireResponse.kendraResultsCached.ResultItems.DocumentURI","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"entireResponse.kendraResultsCached.ResultItems.DocumentURI.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"entireResponse.kendraResultsCached.ResultItems.DocumentURI"}}},{"name":"entireResponse.kendraResultsCached.ResultItems.Id","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"entireResponse.kendraResultsCached.ResultItems.Id.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"entireResponse.kendraResultsCached.ResultItems.Id"}}},{"name":"entireResponse.kendraResultsCached.ResultItems.Type","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"entireResponse.kendraResultsCached.ResultItems.Type.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"entireResponse.kendraResultsCached.ResultItems.Type"}}},{"name":"entireResponse.kendraResultsCached.TotalNumberOfResults","type":"number","esTypes":["long"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true},{"name":"entireResponse.kendraResultsCached.originalKendraIndexId","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"entireResponse.kendraResultsCached.originalKendraIndexId.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"entireResponse.kendraResultsCached.originalKendraIndexId"}}},{"name":"entireResponse.message","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"entireResponse.message.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"entireResponse.message"}}},{"name":"entireResponse.plainMessage","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"entireResponse.plainMessage.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"entireResponse.plainMessage"}}},{"name":"entireResponse.result.a","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"entireResponse.result.a.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"entireResponse.result.a"}}},{"name":"entireResponse.result.answersource","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"entireResponse.result.answersource.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"entireResponse.result.answersource"}}},{"name":"entireResponse.result.autotranslate.a","type":"boolean","esTypes":["boolean"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true},{"name":"entireResponse.result.l","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"entireResponse.result.l.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"entireResponse.result.l"}}},{"name":"entireResponse.result.q","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"entireResponse.result.q.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"entireResponse.result.q"}}},{"name":"entireResponse.result.qid","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"entireResponse.result.qid.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"entireResponse.result.qid"}}},{"name":"entireResponse.result.questions.q","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"entireResponse.result.questions.q.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"entireResponse.result.questions.q"}}},{"name":"entireResponse.result.quniqueterms","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"entireResponse.result.quniqueterms.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"entireResponse.result.quniqueterms"}}},{"name":"entireResponse.result.type","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"entireResponse.result.type.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"entireResponse.result.type"}}},{"name":"entireResponse.session.appContext","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"entireResponse.session.appContext.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"entireResponse.session.appContext"}}},{"name":"entireResponse.session.qnabot_gotanswer","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"entireResponse.session.qnabot_gotanswer.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"entireResponse.session.qnabot_gotanswer"}}},{"name":"entireResponse.session.qnabot_qid","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"entireResponse.session.qnabot_qid.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"entireResponse.session.qnabot_qid"}}},{"name":"entireResponse.session.qnabotcontext","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"entireResponse.session.qnabotcontext.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"entireResponse.session.qnabotcontext"}}},{"name":"entireResponse.type","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"entireResponse.type.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"entireResponse.type"}}},{"name":"qid","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"qid.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"qid"}}},{"name":"topic","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"topic.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"topic"}}},{"name":"utterance","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"utterance.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"utterance"}}}]
                title: <INDEX_METRICS>
              references: []
              migrationVersion:
                index-pattern: 7.6.0
            - id: Feedback
              type: index-pattern
              namespaces:
                - default
              updated_at: "2021-04-19T22:23:40.946Z"
              version: WzcxOCwxXQ==
              attributes:
                timeFieldName: datetime
                fields: >-
                  [{"name":"_id","type":"string","esTypes":["_id"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":false},{"name":"_index","type":"string","esTypes":["_index"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":false},{"name":"_score","type":"number","count":0,"scripted":false,"searchable":false,"aggregatable":false,"readFromDocValues":false},{"name":"_source","type":"_source","esTypes":["_source"],"count":0,"scripted":false,"searchable":false,"aggregatable":false,"readFromDocValues":false},{"name":"_type","type":"string","esTypes":["_type"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":false},{"name":"alternate","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"alternate.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"alternate"}}},{"name":"answer","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"answer.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"answer"}}},{"name":"datetime","type":"date","esTypes":["date"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true},{"name":"feedback","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"feedback.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"feedback"}}},{"name":"qid","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"qid.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"qid"}}},{"name":"utterance","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"utterance.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"utterance"}}}]
                title: <INDEX_FEEDBACK>
              references: []
              migrationVersion:
                index-pattern: 7.6.0
            - id: QnaItems
              type: index-pattern
              namespaces:
                - default
              updated_at: "2021-04-19T22:23:40.946Z"
              version: WzcxOSwxXQ==
              attributes:
                fields: >-
                  [{"name":"_id","type":"string","esTypes":["_id"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":false},{"name":"_index","type":"string","esTypes":["_index"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":false},{"name":"_score","type":"number","count":0,"scripted":false,"searchable":false,"aggregatable":false,"readFromDocValues":false},{"name":"_source","type":"_source","esTypes":["_source"],"count":0,"scripted":false,"searchable":false,"aggregatable":false,"readFromDocValues":false},{"name":"_type","type":"string","esTypes":["_type"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":false},{"name":"a","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"alt.markdown","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"alt.markdown.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"alt.markdown"}}},{"name":"alt.ssml","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"alt.ssml.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"alt.ssml"}}},{"name":"args","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"args.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"args"}}},{"name":"conditionalChaining","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"conditionalChaining.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"conditionalChaining"}}},{"name":"correctAnswers","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"elicitResponse.response_sessionattr_namespace","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"elicitResponse.response_sessionattr_namespace.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"elicitResponse.response_sessionattr_namespace"}}},{"name":"elicitResponse.responsebot_hook","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"elicitResponse.responsebot_hook.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"elicitResponse.responsebot_hook"}}},{"name":"incorrectAnswers","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"l","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true},{"name":"next","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"next.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"next"}}},{"name":"qid","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true},{"name":"question","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"questions.q","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"quiz","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"quiz.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"quiz"}}},{"name":"quniqueterms","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"r.buttons.text","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"r.buttons.text.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"r.buttons.text"}}},{"name":"r.buttons.value","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"r.buttons.value.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"r.buttons.value"}}},{"name":"r.imageUrl","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true},{"name":"r.subTitle","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"r.subTitle.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"r.subTitle"}}},{"name":"r.text","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"r.text.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"r.text"}}},{"name":"r.title","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"r.url","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"r.url.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"r.url"}}},{"name":"responses.correct","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"responses.correct.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"responses.correct"}}},{"name":"responses.end","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"responses.end.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"responses.end"}}},{"name":"responses.incorrect","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"responses.incorrect.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"responses.incorrect"}}},{"name":"selected","type":"boolean","esTypes":["boolean"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true},{"name":"t","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"type","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"type.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"type"}}}]
                title: <INDEX_QNA>
              references: []
              migrationVersion:
                index-pattern: 7.6.0
        replaceTokenInBody:
          - f: <INDEX_QNA>
            r: !Sub "${Var.QnaIndex}"
          - f: <INDEX_METRICS>
            r: !Sub "${Var.MetricsIndex}"
          - f: <INDEX_FEEDBACK>
            r: !Sub "${Var.FeedbackIndex}"
  ExamplesStack:
    Type: "AWS::CloudFormation::Stack"
    Condition: BuildExamples
    Properties:
      TemplateURL: !Sub >-
        https://lexhandson-202106.s3.ap-northeast-1.amazonaws.com/qnabot-app/examples.json
      Parameters:
        QnAType: !GetAtt
          - Var
          - QnAType
        QuizType: !GetAtt
          - Var
          - QuizType
        Index: !GetAtt
          - Var
          - QnaIndex
        ESAddress: !GetAtt
          - ESVar
          - ESAddress
        BootstrapBucket: aws-bigdata-blog-replica-ap-northeast-1
        BootstrapPrefix: artifacts/aws-ai-qna-bot
        FeedbackFirehose: !GetAtt
          - FeedbackFirehose
          - Arn
        FeedbackFirehoseName: !Ref FeedbackFirehose
        CFNLambda: !GetAtt
          - CFNLambda
          - Arn
        CFNLambdaRole: !GetAtt
          - CFNLambdaRole
          - Arn
        ApiUrlName: !GetAtt
          - ApiUrl
          - Name
        AssetBucket: !Ref AssetBucket
        FulfillmentLambdaRole: !Ref FulfillmentLambdaRole
        QIDLambdaArn: !GetAtt
          - ESQidLambda
          - Arn
        VPCSubnetIdList: !Join
          - ","
          - - !Ref "AWS::NoValue"
        VPCSecurityGroupIdList: !Join
          - ","
          - - !Ref "AWS::NoValue"
        LexBotVersion: !Ref LexBotVersion
        XraySetting: !Ref XraySetting
  ExportStack:
    Type: "AWS::CloudFormation::Stack"
    Properties:
      TemplateURL: !Sub >-
        https://lexhandson-202106.s3.ap-northeast-1.amazonaws.com/qnabot-app/export.json
      Parameters:
        CFNLambda: !GetAtt
          - CFNLambda
          - Arn
        CFNInvokePolicy: !Ref CFNInvokePolicy
        BootstrapBucket: aws-bigdata-blog-replica-ap-northeast-1
        BootstrapPrefix: artifacts/aws-ai-qna-bot
        VarIndex: !GetAtt
          - Var
          - QnaIndex
        EsEndpoint: !GetAtt
          - ESVar
          - ESAddress
        EsProxyLambda: !GetAtt
          - ESProxyLambda
          - Arn
        ExportBucket: !Ref ExportBucket
        VPCSubnetIdList: !Join
          - ","
          - - !Ref "AWS::NoValue"
        VPCSecurityGroupIdList: !Join
          - ","
          - - !Ref "AWS::NoValue"
        XraySetting: !Ref XraySetting
        Api: !Ref API
        ApiRootResourceId: !GetAtt
          - API
          - RootResourceId
        Encryption: !Ref Encryption
        Stage: !Ref Stage
        ApiDeploymentId: !Ref Deployment
        KendraCrawlerSnsTopic: !Ref KendraCrawlerSnsTopic
        DefaultQnABotSettings: !Ref DefaultQnABotSettings
        CustomQnABotSettings: !Ref CustomQnABotSettings
        LexVersion: !If
          - CreateLexV1Bots
          - V1
          - V2
        FallbackIntent: !If
          - CreateLexV1Bots
          - !Ref IntentFallback
          - LexV2Only_Mode
        Intent: !If
          - CreateLexV1Bots
          - !Ref Intent
          - LexV2Only_Mode
        BotName: !If
          - CreateLexV1Bots
          - !Ref LexBot
          - LexV2Only_Mode
        LexV2BotName: !GetAtt
          - LexV2Bot
          - botName
        LexV2BotId: !GetAtt
          - LexV2Bot
          - botId
        LexV2BotAlias: !GetAtt
          - LexV2Bot
          - botAlias
        LexV2BotAliasId: !GetAtt
          - LexV2Bot
          - botAliasId
        LexV2BotLocaleIds: !GetAtt
          - LexV2Bot
          - botLocaleIds
  ImportStack:
    Type: "AWS::CloudFormation::Stack"
    DependsOn:
      - PreUpgradeExport
      - ElasticsearchDomainUpdate
    Properties:
      TemplateURL: !Sub >-
        https://lexhandson-202106.s3.ap-northeast-1.amazonaws.com/qnabot-app/import.json
      Parameters:
        CFNLambda: !GetAtt
          - CFNLambda
          - Arn
        CFNInvokePolicy: !Ref CFNInvokePolicy
        BootstrapBucket: aws-bigdata-blog-replica-ap-northeast-1
        BootstrapPrefix: artifacts/aws-ai-qna-bot
        EsEndpoint: !GetAtt
          - ESVar
          - ESAddress
        EsProxyLambda: !GetAtt
          - ESProxyLambda
          - Arn
        ImportBucket: !Ref ImportBucket
        ExportBucket: !Ref ExportBucket
        VarIndex: !GetAtt
          - Var
          - QnaIndex
        MetricsIndex: !GetAtt
          - Var
          - MetricsIndex
        FeedbackIndex: !GetAtt
          - Var
          - FeedbackIndex
        Encryption: !Ref Encryption
        VPCSubnetIdList: !Join
          - ","
          - - !Ref "AWS::NoValue"
        VPCSecurityGroupIdList: !Join
          - ","
          - - !Ref "AWS::NoValue"
        XraySetting: !Ref XraySetting
  KendraCrawlerSnsTopic:
    Type: "AWS::SNS::Topic"
  QNAInvokePermission:
    Type: "AWS::Lambda::Permission"
    Properties:
      Action: "lambda:InvokeFunction"
      FunctionName: !GetAtt
        - FulfillmentLambda
        - Arn
      Principal: lex.amazonaws.com
  SlotType:
    Type: "Custom::LexSlotType"
    Condition: CreateLexV1Bots
    Properties:
      ServiceToken: !GetAtt
        - CFNLambda
        - Arn
      createVersion: true
      description: custom slot type 4.7.0 - v1
      enumerationValues:
        - value: what is a chat bot
        - value: exit
        - value: quit
        - value: start the quiz
        - value: how much does it cost
        - value: what is a data warehouse
        - value: what is coronary artery disease
        - value: what is an electric vehicle
        - value: tell me about amazon alexa
        - value: tell me about body mass index
        - value: tell me about healthy eating
        - value: how do I know if I am a linux expert
        - value: should I stop smoking
        - value: how bad is excessive alcohol consumption
        - value: do I get enough sleep
        - value: tell me about cloud computing
        - value: do I get enough exercise
        - value: what is a good training program for windows
        - value: is programming bad for you
        - value: is bacon good for you
        - value: what is a stack overflow
        - value: should I use a document store
        - value: give me advice on regular training
        - value: what are the benfits of a cardio workout
        - value: should I learn javascript programming
        - value: is working in support good for my career
        - value: where can i learn python
        - value: how quickly will I recover from triple bypass surgery
        - value: how quickly will I find employment after graduating
        - value: how serious is a core dump
        - value: what is a graphics processing unit
        - value: what should I do when i see errors in the logfile
        - value: tell me about serverless computing
        - value: are computer viruses serious
        - value: what is a slam dunk
        - value: what is a first down
        - value: what is normal blood pressure
        - value: how many people live in Ireland
        - value: how do I measure my volume
        - value: what car is the fastest from zero to sixty
        - value: tell me the benefits of green vegetables
        - value: when should I call emergency services
        - value: where is the nearest computer repair store
        - value: when should I hire a consultant
        - value: tell me about artificial intelligence
        - value: does my family history affect my risk
        - value: is is important to run regular backups
        - value: do I have an unusual appearance
        - value: is there a gene associated with musical ability
        - value: what is data science
        - value: how do I donate money
        - value: how do I volunteer time
        - value: tell me about the phrase life is good
        - value: will robots rule the world
        - value: hello
        - value: feedback
        - value: One
        - value: Two
        - value: Three
        - value: next
        - value: previous
        - value: help
        - value: help me
        - value: "yes"
        - value: "no"
        - value: thumbs up
        - value: thumbs down
        - value: a
        - value: b
        - value: c
        - value: d
        - value: e
        - value: f
        - value: g
        - value: A
        - value: B
        - value: C
        - value: D
        - value: E
        - value: F
        - value: G
        - value: "1234567890"
  Intent:
    Type: "Custom::LexIntent"
    Condition: CreateLexV1Bots
    Properties:
      ServiceToken: !GetAtt
        - CFNLambda
        - Arn
      prefix: fulfilment
      description: custom intent 4.7.0 - v1
      createVersion: true
      sampleUtterances:
        - "{slot}"
      slots:
        - name: slot
          slotType: !Ref SlotType
          slotConstraint: Optional
          slotTypeVersion: QNABOT-AUTO-ASSIGNED
          priority: 1
      fulfillmentActivity:
        type: CodeHook
        codeHook:
          uri: !GetAtt
            - FulfillmentLambda
            - Arn
          messageVersion: "1.0"
    DependsOn: QNAInvokePermission
  IntentFallback:
    Type: "Custom::LexIntent"
    Condition: CreateLexV1Bots
    Properties:
      ServiceToken: !GetAtt
        - CFNLambda
        - Arn
      prefix: qnabotfallbackfulfilment
      description: custom fallback intent 4.7.0 - v1
      createVersion: true
      fulfillmentActivity:
        type: CodeHook
        codeHook:
          uri: !GetAtt
            - FulfillmentLambda
            - Arn
          messageVersion: "1.0"
      parentIntentSignature: AMAZON.FallbackIntent
    DependsOn: QNAInvokePermission
  LexBot:
    Type: "Custom::LexBot"
    Condition: CreateLexV1Bots
    Properties:
      ServiceToken: !GetAtt
        - CFNLambda
        - Arn
      name: !Sub "${AWS::StackName}-Bot"
      description: QnABot primary bot 4.7.0 - v1
      locale: en-US
      voiceId: Joanna
      childDirected: false
      createVersion: true
      intents:
        - intentName: !Ref Intent
        - intentName: !Ref IntentFallback
      abortStatement:
        messages:
          - content: "Sorry, I did not understand that"
            contentType: PlainText
  VersionAlias:
    Type: "Custom::LexAlias"
    Condition: CreateLexV1Bots
    DependsOn: LexBot
    Properties:
      ServiceToken: !GetAtt
        - CFNLambda
        - Arn
      botName: !Ref LexBot
      name: live
      description: QnABot live alias 4.7.0 - v1
  LexV2Bot:
    Type: "Custom::LexV2Bot"
    Properties:
      ServiceToken: !GetAtt
        - Lexv2BotLambda
        - Arn
      description: QnABot Lex V2 Bot4.7.0 - v1
      BuildDate: "2021-06-06T14:08:00.252Z"
      localIds: !Ref LexV2BotLocaleIds
      utterances:
        - what is a chat bot
        - exit
        - quit
        - start the quiz
        - how much does it cost
        - what is a data warehouse
        - what is coronary artery disease
        - what is an electric vehicle
        - tell me about amazon alexa
        - tell me about body mass index
        - tell me about healthy eating
        - how do I know if I am a linux expert
        - should I stop smoking
        - how bad is excessive alcohol consumption
        - do I get enough sleep
        - tell me about cloud computing
        - do I get enough exercise
        - what is a good training program for windows
        - is programming bad for you
        - is bacon good for you
        - what is a stack overflow
        - should I use a document store
        - give me advice on regular training
        - what are the benfits of a cardio workout
        - should I learn javascript programming
        - is working in support good for my career
        - where can i learn python
        - how quickly will I recover from triple bypass surgery
        - how quickly will I find employment after graduating
        - how serious is a core dump
        - what is a graphics processing unit
        - what should I do when i see errors in the logfile
        - tell me about serverless computing
        - are computer viruses serious
        - what is a slam dunk
        - what is a first down
        - what is normal blood pressure
        - how many people live in Ireland
        - how do I measure my volume
        - what car is the fastest from zero to sixty
        - tell me the benefits of green vegetables
        - when should I call emergency services
        - where is the nearest computer repair store
        - when should I hire a consultant
        - tell me about artificial intelligence
        - does my family history affect my risk
        - is is important to run regular backups
        - do I have an unusual appearance
        - is there a gene associated with musical ability
        - what is data science
        - how do I donate money
        - how do I volunteer time
        - tell me about the phrase life is good
        - will robots rule the world
        - hello
        - feedback
        - One
        - Two
        - Three
        - next
        - previous
        - help
        - help me
        - "yes"
        - "no"
        - thumbs up
        - thumbs down
        - a
        - b
        - c
        - d
        - e
        - f
        - g
        - A
        - B
        - C
        - D
        - E
        - F
        - G
        - "1234567890"
  Alexa:
    Type: "AWS::Lambda::Permission"
    Properties:
      Action: "lambda:InvokeFunction"
      FunctionName: !GetAtt
        - FulfillmentLambda
        - Arn
      Principal: alexa-appkit.amazon.com
  FulfillmentCodeVersion:
    Type: "Custom::S3Version"
    Properties:
      ServiceToken: !GetAtt
        - CFNLambda
        - Arn
      Bucket: aws-bigdata-blog-replica-ap-northeast-1
      Key: !Sub artifacts/aws-ai-qna-bot/lambda/fulfillment.zip
      BuildDate: "2021-06-06T14:08:00.268Z"
  FulfillmentLambda:
    Type: "AWS::Lambda::Function"
    Properties:
      Code:
        S3Bucket: aws-bigdata-blog-replica-ap-northeast-1
        S3Key: !Sub artifacts/aws-ai-qna-bot/lambda/fulfillment.zip
        S3ObjectVersion: !Ref FulfillmentCodeVersion
      Environment:
        Variables:
          ES_TYPE: !GetAtt
            - Var
            - QnAType
          ES_INDEX: !GetAtt
            - Var
            - QnaIndex
          ES_ADDRESS: !GetAtt
            - ESVar
            - ESAddress
          LAMBDA_DEFAULT_QUERY: !Ref ESQueryLambda
          LAMBDA_LOG: !Ref ESLoggingLambda
          ES_SERVICE_QID: !Ref ESQidLambda
          ES_SERVICE_PROXY: !Ref ESProxyLambda
          DYNAMODB_USERSTABLE: !Ref UsersTable
          DEFAULT_USER_POOL_JWKS_PARAM: !Ref DefaultUserPoolJwksUrl
          DEFAULT_SETTINGS_PARAM: !Ref DefaultQnABotSettings
          CUSTOM_SETTINGS_PARAM: !Ref CustomQnABotSettings
          ExampleJSLambdaQuiz: !GetAtt
            - ExamplesStack
            - Outputs.ExampleJSLambdaQuiz
          ExampleJSLambdahook: !GetAtt
            - ExamplesStack
            - Outputs.ExampleJSLambdahook
          ExamplePYTHONLambdaBotBroker: !GetAtt
            - ExamplesStack
            - Outputs.ExamplePYTHONLambdaBotBroker
          ExamplePYTHONLambdaConnectCallback: !GetAtt
            - ExamplesStack
            - Outputs.ExamplePYTHONLambdaConnectCallback
          ExamplePYTHONLambdaFeedback: !GetAtt
            - ExamplesStack
            - Outputs.ExamplePYTHONLambdaFeedback
          ExamplePYTHONLambdaNext: !GetAtt
            - ExamplesStack
            - Outputs.ExamplePYTHONLambdaNext
          ExamplePYTHONLambdaPrevious: !GetAtt
            - ExamplesStack
            - Outputs.ExamplePYTHONLambdaPrevious
          ExamplePYTHONLambdahello: !GetAtt
            - ExamplesStack
            - Outputs.ExamplePYTHONLambdahello
          EXTCustomJSHook: !GetAtt
            - ExamplesStack
            - Outputs.EXTCustomJSHook
          EXTCustomPYHook: !GetAtt
            - ExamplesStack
            - Outputs.EXTCustomPYHook
          QNAYesNo: !GetAtt
            - ExamplesStack
            - Outputs.QNAYesNo
          QNAYesNoExit: !GetAtt
            - ExamplesStack
            - Outputs.QNAYesNoExit
          QNADate: !GetAtt
            - ExamplesStack
            - Outputs.QNADate
          QNADateNoConfirm: !GetAtt
            - ExamplesStack
            - Outputs.QNADateNoConfirm
          QNADayOfWeek: !GetAtt
            - ExamplesStack
            - Outputs.QNADayOfWeek
          QNAMonth: !GetAtt
            - ExamplesStack
            - Outputs.QNAMonth
          QNAMonthNoConfirm: !GetAtt
            - ExamplesStack
            - Outputs.QNAMonthNoConfirm
          QNANumber: !GetAtt
            - ExamplesStack
            - Outputs.QNANumber
          QNANumberNoConfirm: !GetAtt
            - ExamplesStack
            - Outputs.QNANumberNoConfirm
          QNAAge: !GetAtt
            - ExamplesStack
            - Outputs.QNAAge
          QNAPhoneNumber: !GetAtt
            - ExamplesStack
            - Outputs.QNAPhoneNumber
          QNAPhoneNumberNoConfirm: !GetAtt
            - ExamplesStack
            - Outputs.QNAPhoneNumberNoConfirm
          QNATime: !GetAtt
            - ExamplesStack
            - Outputs.QNATime
          QNAEmailAddress: !GetAtt
            - ExamplesStack
            - Outputs.QNAEmailAddress
          QNAName: !GetAtt
            - ExamplesStack
            - Outputs.QNAName
          QNAWage: !GetAtt
            - ExamplesStack
            - Outputs.QNAWage
          QNASocialSecurity: !GetAtt
            - ExamplesStack
            - Outputs.QNASocialSecurity
          QNAPin: !GetAtt
            - ExamplesStack
            - Outputs.QNAPin
          QNAPinNoConfirm: !GetAtt
            - ExamplesStack
            - Outputs.QNAPinNoConfirm
      Handler: index.handler
      MemorySize: "1408"
      Role: !GetAtt
        - FulfillmentLambdaRole
        - Arn
      Runtime: nodejs16.x
      Timeout: 300
      VpcConfig: !If
        - VPCEnabled
        - SubnetIds:
            - !Ref "AWS::NoValue"
          SecurityGroupIds:
            - !Ref "AWS::NoValue"
        - !Ref "AWS::NoValue"
      TracingConfig: !If
        - XRAYEnabled
        - Mode: Active
        - !Ref "AWS::NoValue"
      Tags:
        - Key: Type
          Value: Fulfillment
  InvokePolicy:
    Type: "AWS::IAM::ManagedPolicy"
    Properties:
      PolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Action:
              - "lambda:InvokeFunction"
            Resource:
              - "arn:aws:lambda:*:*:function:qna-*"
              - "arn:aws:lambda:*:*:function:QNA-*"
              - !GetAtt
                - ESQueryLambda
                - Arn
              - !GetAtt
                - ESProxyLambda
                - Arn
              - !GetAtt
                - ESLoggingLambda
                - Arn
              - !GetAtt
                - ESQidLambda
                - Arn
              - !GetAtt
                - ExamplesStack
                - Outputs.ExampleJSLambdaQuiz
              - !GetAtt
                - ExamplesStack
                - Outputs.ExampleJSLambdahook
              - !GetAtt
                - ExamplesStack
                - Outputs.ExamplePYTHONLambdaBotBroker
              - !GetAtt
                - ExamplesStack
                - Outputs.ExamplePYTHONLambdaConnectCallback
              - !GetAtt
                - ExamplesStack
                - Outputs.ExamplePYTHONLambdaFeedback
              - !GetAtt
                - ExamplesStack
                - Outputs.ExamplePYTHONLambdaNext
              - !GetAtt
                - ExamplesStack
                - Outputs.ExamplePYTHONLambdaPrevious
              - !GetAtt
                - ExamplesStack
                - Outputs.ExamplePYTHONLambdahello
              - !GetAtt
                - ExamplesStack
                - Outputs.EXTCustomJSHook
              - !GetAtt
                - ExamplesStack
                - Outputs.EXTCustomPYHook
      Roles:
        - !Ref FulfillmentLambdaRole
  LexBotPolicy:
    Type: "AWS::IAM::ManagedPolicy"
    Properties:
      PolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Action:
              - "lex:PostText"
              - "lex:RecognizeText"
            Resource:
              - "arn:aws:lex:*:*:bot:QNA*"
              - "arn:aws:lex:*:*:bot*"
      Roles:
        - !Ref FulfillmentLambdaRole
  FulfillmentLambdaRole:
    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"
        - "arn:aws:iam::aws:policy/AWSXRayDaemonWriteAccess"
        - "arn:aws:iam::aws:policy/TranslateReadOnly"
        - "arn:aws:iam::aws:policy/ComprehendReadOnly"
        - !Ref QueryPolicy
      Policies:
        - PolicyName: ParamStorePolicy
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Effect: Allow
                Action:
                  - "ssm:GetParameter"
                  - "ssm:GetParameters"
                Resource:
                  - !Join
                    - ""
                    - - "arn:aws:ssm:"
                      - !Sub "${AWS::Region}:"
                      - !Sub "${AWS::AccountId}:"
                      - parameter/
                      - !Ref DefaultQnABotSettings
                  - !Join
                    - ""
                    - - "arn:aws:ssm:"
                      - !Sub "${AWS::Region}:"
                      - !Sub "${AWS::AccountId}:"
                      - parameter/
                      - !Ref CustomQnABotSettings
                  - !Join
                    - ""
                    - - "arn:aws:ssm:"
                      - !Sub "${AWS::Region}:"
                      - !Sub "${AWS::AccountId}:"
                      - parameter/
                      - !Ref DefaultUserPoolJwksUrl
        - PolicyName: DynamoDBPolicy
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Effect: Allow
                Action:
                  - "dynamodb:GetItem"
                  - "dynamodb:PutItem"
                Resource:
                  - !GetAtt
                    - UsersTable
                    - Arn
        - PolicyName: S3QNABucketReadAccess
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Effect: Allow
                Action:
                  - "s3:GetObject"
                Resource:
                  - "arn:aws:s3:::QNA*/*"
                  - "arn:aws:s3:::qna*/*"
  ESWarmerLambda:
    Type: "AWS::Lambda::Function"
    Properties:
      Code:
        S3Bucket: aws-bigdata-blog-replica-ap-northeast-1
        S3Key: !Sub artifacts/aws-ai-qna-bot/lambda/fulfillment.zip
        S3ObjectVersion: !Ref FulfillmentCodeVersion
      Environment:
        Variables:
          REPEAT_COUNT: "4"
          TARGET_PATH: _doc/_search
          TARGET_INDEX: !GetAtt
            - Var
            - QnaIndex
          TARGET_URL: !GetAtt
            - ESVar
            - ESAddress
          DEFAULT_SETTINGS_PARAM: !Ref DefaultQnABotSettings
          CUSTOM_SETTINGS_PARAM: !Ref CustomQnABotSettings
      Handler: index.warmer
      MemorySize: "512"
      Role: !GetAtt
        - WarmerLambdaRole
        - Arn
      Runtime: nodejs16.x
      Timeout: 300
      VpcConfig: !If
        - VPCEnabled
        - SubnetIds:
            - !Ref "AWS::NoValue"
          SecurityGroupIds:
            - !Ref "AWS::NoValue"
        - !Ref "AWS::NoValue"
      TracingConfig: !If
        - XRAYEnabled
        - Mode: Active
        - !Ref "AWS::NoValue"
      Tags:
        - Key: Type
          Value: Warmer
  WarmerLambdaRole:
    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"
        - "arn:aws:iam::aws:policy/AWSXRayDaemonWriteAccess"
      Policies:
        - PolicyName: ParamStorePolicy
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Effect: Allow
                Action:
                  - "ssm:GetParameter"
                  - "ssm:GetParameters"
                Resource:
                  - !Join
                    - ""
                    - - "arn:aws:ssm:"
                      - !Sub "${AWS::Region}:"
                      - !Sub "${AWS::AccountId}:"
                      - parameter/
                      - !Ref DefaultQnABotSettings
                  - !Join
                    - ""
                    - - "arn:aws:ssm:"
                      - !Sub "${AWS::Region}:"
                      - !Sub "${AWS::AccountId}:"
                      - parameter/
                      - !Ref CustomQnABotSettings
              - Sid: AllowES
                Effect: Allow
                Action:
                  - "es:ESHttpGet"
                Resource:
                  - "*"
  ESWarmerRule:
    Type: "AWS::Events::Rule"
    Properties:
      ScheduleExpression: rate(1 minute)
      Targets:
        - Id: ESWarmerScheduler
          Arn: !GetAtt
            - ESWarmerLambda
            - Arn
  ESWarmerRuleInvokeLambdaPermission:
    Type: "AWS::Lambda::Permission"
    Properties:
      FunctionName: !GetAtt
        - ESWarmerLambda
        - Arn
      Action: "lambda:InvokeFunction"
      Principal: events.amazonaws.com
      SourceArn: !GetAtt
        - ESWarmerRule
        - Arn
  LexBuildLambda:
    Type: "AWS::Lambda::Function"
    Properties:
      Code:
        S3Bucket: aws-bigdata-blog-replica-ap-northeast-1
        S3Key: !Sub artifacts/aws-ai-qna-bot/lambda/lex-build.zip
        S3ObjectVersion: !Ref LexBuildCodeVersion
      Environment:
        Variables:
          UTTERANCE_BUCKET: !Ref AssetBucket
          UTTERANCE_KEY: default-utterances.json
          POLL_LAMBDA: !GetAtt
            - LexBuildLambdaPoll
            - Arn
          STATUS_BUCKET: !Ref BuildStatusBucket
          STATUS_KEY: !If
            - CreateLexV1Bots
            - status.json
            - !Ref "AWS::NoValue"
          LEXV2_STATUS_KEY: lexV2status.json
          BOTNAME: !If
            - CreateLexV1Bots
            - !Ref LexBot
            - !Ref "AWS::NoValue"
          BOTALIAS: !If
            - CreateLexV1Bots
            - !Ref VersionAlias
            - !Ref "AWS::NoValue"
          SLOTTYPE: !If
            - CreateLexV1Bots
            - !Ref SlotType
            - !Ref "AWS::NoValue"
          INTENT: !If
            - CreateLexV1Bots
            - !Ref Intent
            - !Ref "AWS::NoValue"
          INTENTFALLBACK: !If
            - CreateLexV1Bots
            - !Ref IntentFallback
            - !Ref "AWS::NoValue"
          LEXV2_BUILD_LAMBDA: !Ref Lexv2BotLambda
          ADDRESS: !Join
            - ""
            - - "https://"
              - !GetAtt
                - ESVar
                - ESAddress
          INDEX: !GetAtt
            - Var
            - index
      Handler: index.handler
      MemorySize: "1024"
      Role: !GetAtt
        - LexBuildLambdaRole
        - Arn
      Runtime: nodejs16.x
      Timeout: 900
      VpcConfig: !If
        - VPCEnabled
        - SubnetIds:
            - !Ref "AWS::NoValue"
          SecurityGroupIds:
            - !Ref "AWS::NoValue"
        - !Ref "AWS::NoValue"
      TracingConfig: !If
        - XRAYEnabled
        - Mode: Active
        - !Ref "AWS::NoValue"
      Tags:
        - Key: Type
          Value: Api
  LexBuildLambdaStart:
    Type: "AWS::Lambda::Function"
    Properties:
      Code:
        ZipFile: |+
          var aws=require('aws-sdk')
          aws.config.region=process.env.AWS_REGION
          var lambda=new aws.Lambda()
          var s3=new aws.S3()
          var crypto=require('crypto')

          exports.handler=async function(event,context,callback){
              var token=crypto.randomBytes(16).toString('base64');
              var bucket=process.env.STATUS_BUCKET;
              var lexV1StatusFile=process.env.STATUS_KEY;
              var lexV2StatusFile=process.env.LEXV2_STATUS_KEY;
              var functionName=process.env.BUILD_FUNCTION;
              var body=JSON.stringify({status:"Starting",token:token});

              if (lexV1StatusFile) {
                  console.log("Initializing ", bucket, lexV1StatusFile);
                  await s3.putObject({
                      Bucket:bucket,
                      Key:lexV1StatusFile,
                      Body:body
                  }).promise();
              }

              console.log("Initializing ", bucket, lexV2StatusFile);
              await s3.putObject({
                  Bucket:bucket,
                  Key:lexV2StatusFile,
                  Body:body
              }).promise();

              // The BUILD_FUNCTION takes care of rebuilding Lex V2 bot, and (unless QnABot is set to V2 only) Lex V1 bot
              console.log("Invoking ", functionName);
              await lambda.invoke({
                 FunctionName:functionName,
                 InvocationType:"Event",
                 Payload:"{}"
              }).promise();
              
              callback(null,{token});
          };


      Environment:
        Variables:
          STATUS_BUCKET: !Ref BuildStatusBucket
          STATUS_KEY: !If
            - CreateLexV1Bots
            - status.json
            - !Ref "AWS::NoValue"
          LEXV2_STATUS_KEY: lexV2status.json
          BUILD_FUNCTION: !GetAtt
            - LexBuildLambda
            - Arn
      Handler: index.handler
      MemorySize: "1024"
      Role: !GetAtt
        - LexBuildLambdaRole
        - Arn
      Runtime: nodejs16.x
      Timeout: 900
      VpcConfig: !If
        - VPCEnabled
        - SubnetIds:
            - !Ref "AWS::NoValue"
          SecurityGroupIds:
            - !Ref "AWS::NoValue"
        - !Ref "AWS::NoValue"
      TracingConfig: !If
        - XRAYEnabled
        - Mode: Active
        - !Ref "AWS::NoValue"
      Tags:
        - Key: Type
          Value: Api
  LexBuildLambdaPoll:
    Type: "AWS::Lambda::Function"
    Properties:
      Code:
        ZipFile: |
          var aws=require('aws-sdk')
          aws.config.region=process.env.AWS_REGION
          var lambda=new aws.Lambda()
          var lex=new aws.LexModelBuildingService
          var s3=new aws.S3()
          var crypto=require('crypto')

          exports.handler=function(event,context,callback){
              return s3.getObject({
                  Bucket:process.env.STATUS_BUCKET,
                  Key:process.env.STATUS_KEY
              }).promise()
              .then(x=>JSON.parse(x.Body.toString()))
              .then(status=>{
                  return lex.getBot({
                      name:process.env.BOT_NAME,
                      versionOrAlias:"$LATEST"
                  }).promise()
                  .then(result=>{
                      status.status=result.status
                      if(result.status==="BUILDING"){
                          return new Promise(function(res,rej){
                              setTimeout(()=>{
                                  lambda.invoke({
                                     FunctionName:process.env.AWS_LAMBDA_FUNCTION_NAME,
                                     InvocationType:"Event",
                                     Payload:JSON.stringify(event)
                                  }).promise()
                                  .then(res).catch(rej)
                              },2*1000    
                              )
                          })
                      }else{
                          return s3.putObject({
                              Bucket:process.env.STATUS_BUCKET,
                              Key:process.env.STATUS_KEY,
                              Body:JSON.stringify(status)
                          }).promise()
                      }
                  })
              })
              .then(()=>callback(null))
              .catch(callback)
          }
      Environment:
        Variables:
          STATUS_KEY: !If
            - CreateLexV1Bots
            - status.json
            - !Ref "AWS::NoValue"
          STATUS_BUCKET: !Ref BuildStatusBucket
          BOT_NAME: !If
            - CreateLexV1Bots
            - !Ref LexBot
            - !Ref "AWS::NoValue"
      Handler: index.handler
      MemorySize: "1024"
      Role: !GetAtt
        - LexBuildLambdaRole
        - Arn
      Runtime: nodejs16.x
      Timeout: 900
      VpcConfig: !If
        - VPCEnabled
        - SubnetIds:
            - !Ref "AWS::NoValue"
          SecurityGroupIds:
            - !Ref "AWS::NoValue"
        - !Ref "AWS::NoValue"
      TracingConfig: !If
        - XRAYEnabled
        - Mode: Active
        - !Ref "AWS::NoValue"
      Tags:
        - Key: Type
          Value: Api
  LexBuildCodeVersion:
    Type: "Custom::S3Version"
    Properties:
      ServiceToken: !GetAtt
        - CFNLambda
        - Arn
      Bucket: aws-bigdata-blog-replica-ap-northeast-1
      Key: !Sub artifacts/aws-ai-qna-bot/lambda/lex-build.zip
      BuildDate: "2021-06-06T14:08:00.269Z"
  LexBuildInvokePolicy:
    Type: "AWS::IAM::ManagedPolicy"
    Properties:
      PolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Action:
              - "lambda:InvokeFunction"
            Resource:
              - !GetAtt
                - LexBuildLambda
                - Arn
              - !GetAtt
                - LexBuildLambdaPoll
                - Arn
              - !GetAtt
                - Lexv2BotLambda
                - Arn
      Roles:
        - !Ref LexBuildLambdaRole
  LexBuildLambdaRole:
    Type: "AWS::IAM::Role"
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service: lambda.amazonaws.com
            Action: "sts:AssumeRole"
      Policies:
        - PolicyName: AssetBucketAccess
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Effect: Allow
                Action:
                  - "s3:Get*"
                Resource:
                  - !Sub "arn:aws:s3:::${AssetBucket}*"
                  - !Sub "arn:aws:s3:::${BuildStatusBucket}*"
              - Effect: Allow
                Action:
                  - "s3:Put*"
                Resource:
                  - !Sub "arn:aws:s3:::${BuildStatusBucket}*"
      Path: /
      ManagedPolicyArns:
        - "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
        - "arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole"
        - "arn:aws:iam::aws:policy/AWSXRayDaemonWriteAccess"
        - !Ref QueryPolicy
        - "arn:aws:iam::aws:policy/AmazonLexFullAccess"
  BuildStatusBucket:
    Type: "AWS::S3::Bucket"
    Properties:
      LifecycleConfiguration:
        Rules:
          - NoncurrentVersionExpirationInDays: 1
            Status: Enabled
          - AbortIncompleteMultipartUpload:
              DaysAfterInitiation: 1
            Status: Enabled
      VersioningConfiguration:
        Status: Enabled
      BucketEncryption: !If
        - Encrypted
        - ServerSideEncryptionConfiguration:
            - ServerSideEncryptionByDefault:
                SSEAlgorithm: AES256
        - !Ref "AWS::NoValue"
  BuildStatusClear:
    Type: "Custom::S3Clear"
    DependsOn:
      - CFNInvokePolicy
    Properties:
      ServiceToken: !GetAtt
        - CFNLambda
        - Arn
      Bucket: !Ref BuildStatusBucket
  Lexv2BotLambda:
    Type: "AWS::Lambda::Function"
    Properties:
      Code:
        S3Bucket: aws-bigdata-blog-replica-ap-northeast-1
        S3Key: !Sub artifacts/aws-ai-qna-bot/lambda/lexv2-build.zip
        S3ObjectVersion: !Ref Lexv2BotCodeVersion
      Environment:
        Variables:
          STACKNAME: !Ref "AWS::StackName"
          FULFILLMENT_LAMBDA_ARN: !GetAtt
            - FulfillmentLambda
            - Arn
          LOCALES: !Ref LexV2BotLocaleIds
          PYTHONPATH: "/var/task/py_modules:/var/runtime:/opt/python"
      Handler: handler.handler
      MemorySize: "1024"
      Role: !GetAtt
        - Lexv2BotLambdaRole
        - Arn
      Runtime: python3.9
      Timeout: 900
      VpcConfig: !If
        - VPCEnabled
        - SubnetIds:
            - !Ref "AWS::NoValue"
          SecurityGroupIds:
            - !Ref "AWS::NoValue"
        - !Ref "AWS::NoValue"
      TracingConfig: !If
        - XRAYEnabled
        - Mode: Active
        - !Ref "AWS::NoValue"
      Tags:
        - Key: Type
          Value: Api
  Lexv2BotCodeVersion:
    Type: "Custom::S3Version"
    Properties:
      ServiceToken: !GetAtt
        - CFNLambda
        - Arn
      Bucket: aws-bigdata-blog-replica-ap-northeast-1
      Key: !Sub artifacts/aws-ai-qna-bot/lambda/lexv2-build.zip
      BuildDate: "2021-06-06T14:08:00.269Z"
  Lexv2BotLambdaRole:
    Type: "AWS::IAM::Role"
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service: lambda.amazonaws.com
            Action: "sts:AssumeRole"
      Policies:
        - PolicyName: LexV2ServiceLinkedRole
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Effect: Allow
                Action:
                  - "iam:GetRole"
                  - "iam:DeleteRole"
                Resource:
                  - >-
                    arn:aws:iam::*:role/aws-service-role/lexv2.amazonaws.com/AWSServiceRoleForLexV2Bots*
              - Effect: Allow
                Action:
                  - "iam:CreateServiceLinkedRole"
                Resource:
                  - >-
                    arn:aws:iam::*:role/aws-service-role/lexv2.amazonaws.com/AWSServiceRoleForLexV2Bots*
                Condition:
                  StringLike:
                    "iam:AWSServiceName": lexv2.amazonaws.com
              - Action:
                  - "iam:PassRole"
                Effect: Allow
                Resource:
                  - >-
                    arn:aws:iam::*:role/aws-service-role/lexv2.amazonaws.com/AWSServiceRoleForLexV2Bots*
                Condition:
                  StringLike:
                    "iam:PassedToService":
                      - lexv2.amazonaws.com
              - Action:
                  - "translate:TranslateText"
                  - "comprehend:DetectDominantLanguage"
                Effect: Allow
                Resource: "*"
        - PolicyName: BuildStatusBucketAccess
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Effect: Allow
                Action:
                  - "s3:Get*"
                  - "s3:Put*"
                Resource:
                  - !Sub "arn:aws:s3:::${BuildStatusBucket}*"
      Path: /
      ManagedPolicyArns:
        - "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
        - "arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole"
        - "arn:aws:iam::aws:policy/AWSXRayDaemonWriteAccess"
        - !Ref QueryPolicy
        - "arn:aws:iam::aws:policy/AmazonLexFullAccess"
  LexAccessPolicy:
    Type: "AWS::IAM::ManagedPolicy"
    Properties:
      PolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Action:
              - "lex:PostContent"
              - "lex:PostText"
              - "lex:RecognizeText"
              - "lex:RecognizeUtterance"
            Resource:
              - "*"
          - Effect: Allow
            Action:
              - "polly:SynthesizeSpeech"
            Resource:
              - "*"
      Roles: !If
        - Public
        - - !Ref AdminRole
          - !Ref UnauthenticatedRole
          - !Ref UserRole
        - - !Ref AdminRole
          - !Ref UserRole
  ApiGatewayCloudWatchLogsRole:
    Type: "AWS::IAM::Role"
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - apigateway.amazonaws.com
            Action:
              - "sts:AssumeRole"
      Policies:
        - PolicyName: ApiGatewayLogsPolicy
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Effect: Allow
                Action:
                  - "logs:CreateLogGroup"
                  - "logs:CreateLogStream"
                  - "logs:DescribeLogGroups"
                  - "logs:DescribeLogStreams"
                  - "logs:PutLogEvents"
                  - "logs:GetLogEvents"
                  - "logs:FilterLogEvents"
                Resource: "*"
  ApiGatewayRole:
    Type: "AWS::IAM::Role"
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - apigateway.amazonaws.com
            Action:
              - "sts:AssumeRole"
  ESProxyCodeVersion:
    Type: "Custom::S3Version"
    Properties:
      ServiceToken: !GetAtt
        - CFNLambda
        - Arn
      Bucket: aws-bigdata-blog-replica-ap-northeast-1
      Key: !Sub artifacts/aws-ai-qna-bot/lambda/proxy-es.zip
      BuildDate: "2021-06-06T14:08:00.270Z"
  UtteranceLambda:
    Type: "AWS::Lambda::Function"
    Properties:
      Code:
        S3Bucket: aws-bigdata-blog-replica-ap-northeast-1
        S3Key: !Sub artifacts/aws-ai-qna-bot/lambda/proxy-es.zip
        S3ObjectVersion: !Ref ESProxyCodeVersion
      Environment:
        Variables:
          ES_INDEX: !GetAtt
            - Var
            - QnaIndex
          ES_ADDRESS: !Join
            - ""
            - - "https://"
              - !GetAtt
                - ESVar
                - ESAddress
          UTTERANCE_BUCKET: !Ref AssetBucket
          UTTERANCE_KEY: default-utterances.json
      Handler: index.utterances
      MemorySize: "1408"
      Role: !GetAtt
        - ESProxyLambdaRole
        - Arn
      Runtime: nodejs16.x
      Timeout: 300
      VpcConfig: !If
        - VPCEnabled
        - SubnetIds:
            - !Ref "AWS::NoValue"
          SecurityGroupIds:
            - !Ref "AWS::NoValue"
        - !Ref "AWS::NoValue"
      TracingConfig: !If
        - XRAYEnabled
        - Mode: Active
        - !Ref "AWS::NoValue"
      Tags:
        - Key: Type
          Value: Service
  ESQidLambda:
    Type: "AWS::Lambda::Function"
    Properties:
      Code:
        S3Bucket: aws-bigdata-blog-replica-ap-northeast-1
        S3Key: !Sub artifacts/aws-ai-qna-bot/lambda/proxy-es.zip
        S3ObjectVersion: !Ref ESProxyCodeVersion
      Environment:
        Variables:
          ES_INDEX: !GetAtt
            - Var
            - QnaIndex
          ES_ADDRESS: !GetAtt
            - ESVar
            - ESAddress
      Handler: index.qid
      MemorySize: "1408"
      Role: !GetAtt
        - ESProxyLambdaRole
        - Arn
      Runtime: nodejs16.x
      Timeout: 300
      VpcConfig: !If
        - VPCEnabled
        - SubnetIds:
            - !Ref "AWS::NoValue"
          SecurityGroupIds:
            - !Ref "AWS::NoValue"
        - !Ref "AWS::NoValue"
      TracingConfig: !If
        - XRAYEnabled
        - Mode: Active
        - !Ref "AWS::NoValue"
      Tags:
        - Key: Type
          Value: Service
  ESCleaningLambda:
    Type: "AWS::Lambda::Function"
    Properties:
      Code:
        S3Bucket: aws-bigdata-blog-replica-ap-northeast-1
        S3Key: !Sub artifacts/aws-ai-qna-bot/lambda/proxy-es.zip
        S3ObjectVersion: !Ref ESProxyCodeVersion
      Environment:
        Variables:
          ES_INDEX: !GetAtt
            - Var
            - QnaIndex
          ES_ADDRESS: !GetAtt
            - ESVar
            - ESAddress
          FEEDBACK_DELETE_RANGE_MINUTES: !Ref KibanaDashboardRetentionMinutes
          METRICS_DELETE_RANGE_MINUTES: !Ref KibanaDashboardRetentionMinutes
      Handler: index.cleanmetrics
      MemorySize: "1408"
      Role: !GetAtt
        - ESProxyLambdaRole
        - Arn
      Runtime: nodejs16.x
      Timeout: 300
      VpcConfig: !If
        - VPCEnabled
        - SubnetIds:
            - !Ref "AWS::NoValue"
          SecurityGroupIds:
            - !Ref "AWS::NoValue"
        - !Ref "AWS::NoValue"
      TracingConfig: !If
        - XRAYEnabled
        - Mode: Active
        - !Ref "AWS::NoValue"
      Tags:
        - Key: Type
          Value: Service
  ScheduledESCleaning:
    Type: "AWS::Events::Rule"
    Properties:
      Description: ""
      ScheduleExpression: rate(1 day)
      State: ENABLED
      Targets:
        - Arn: !GetAtt
            - ESCleaningLambda
            - Arn
          Id: ES_Cleaning_Function
  PermissionForEventsToInvokeLambda:
    Type: "AWS::Lambda::Permission"
    Properties:
      FunctionName: !Ref ESCleaningLambda
      Action: "lambda:InvokeFunction"
      Principal: events.amazonaws.com
      SourceArn: !GetAtt
        - ScheduledESCleaning
        - Arn
  ESLoggingLambda:
    Type: "AWS::Lambda::Function"
    Properties:
      Code:
        S3Bucket: aws-bigdata-blog-replica-ap-northeast-1
        S3Key: !Sub artifacts/aws-ai-qna-bot/lambda/proxy-es.zip
        S3ObjectVersion: !Ref ESProxyCodeVersion
      Environment:
        Variables:
          FIREHOSE_NAME: !Ref GeneralFirehose
      Handler: index.logging
      MemorySize: "1408"
      Role: !GetAtt
        - ESLoggingLambdaRole
        - Arn
      Runtime: nodejs16.x
      Timeout: 300
      VpcConfig: !If
        - VPCEnabled
        - SubnetIds:
            - !Ref "AWS::NoValue"
          SecurityGroupIds:
            - !Ref "AWS::NoValue"
        - !Ref "AWS::NoValue"
      TracingConfig: !If
        - XRAYEnabled
        - Mode: Active
        - !Ref "AWS::NoValue"
      Tags:
        - Key: Type
          Value: Logging
  ESQueryLambda:
    Type: "AWS::Lambda::Function"
    Properties:
      Code:
        S3Bucket: aws-bigdata-blog-replica-ap-northeast-1
        S3Key: !Sub artifacts/aws-ai-qna-bot/lambda/proxy-es.zip
        S3ObjectVersion: !Ref ESProxyCodeVersion
      Environment:
        Variables:
          DEFAULT_SETTINGS_PARAM: !Ref DefaultQnABotSettings
          CUSTOM_SETTINGS_PARAM: !Ref CustomQnABotSettings
          ExampleJSLambdaQuiz: !GetAtt
            - ExamplesStack
            - Outputs.ExampleJSLambdaQuiz
          ExampleJSLambdahook: !GetAtt
            - ExamplesStack
            - Outputs.ExampleJSLambdahook
          ExamplePYTHONLambdaBotBroker: !GetAtt
            - ExamplesStack
            - Outputs.ExamplePYTHONLambdaBotBroker
          ExamplePYTHONLambdaConnectCallback: !GetAtt
            - ExamplesStack
            - Outputs.ExamplePYTHONLambdaConnectCallback
          ExamplePYTHONLambdaFeedback: !GetAtt
            - ExamplesStack
            - Outputs.ExamplePYTHONLambdaFeedback
          ExamplePYTHONLambdaNext: !GetAtt
            - ExamplesStack
            - Outputs.ExamplePYTHONLambdaNext
          ExamplePYTHONLambdaPrevious: !GetAtt
            - ExamplesStack
            - Outputs.ExamplePYTHONLambdaPrevious
          ExamplePYTHONLambdahello: !GetAtt
            - ExamplesStack
            - Outputs.ExamplePYTHONLambdahello
          EXTCustomJSHook: !GetAtt
            - ExamplesStack
            - Outputs.EXTCustomJSHook
          EXTCustomPYHook: !GetAtt
            - ExamplesStack
            - Outputs.EXTCustomPYHook
      Handler: index.query
      MemorySize: "1408"
      Role: !GetAtt
        - ESProxyLambdaRole
        - Arn
      Runtime: nodejs16.x
      Timeout: 300
      VpcConfig: !If
        - VPCEnabled
        - SubnetIds:
            - !Ref "AWS::NoValue"
          SecurityGroupIds:
            - !Ref "AWS::NoValue"
        - !Ref "AWS::NoValue"
      TracingConfig: !If
        - XRAYEnabled
        - Mode: Active
        - !Ref "AWS::NoValue"
      Tags:
        - Key: Type
          Value: Query
  ESProxyLambda:
    Type: "AWS::Lambda::Function"
    Properties:
      Code:
        S3Bucket: aws-bigdata-blog-replica-ap-northeast-1
        S3Key: !Sub artifacts/aws-ai-qna-bot/lambda/proxy-es.zip
        S3ObjectVersion: !Ref ESProxyCodeVersion
      Environment:
        Variables:
          ES_TYPE: !GetAtt
            - Var
            - QnAType
          ES_INDEX: !GetAtt
            - Var
            - QnaIndex
          ES_ADDRESS: !GetAtt
            - ESVar
            - ESAddress
          DEFAULT_SETTINGS_PARAM: !Ref DefaultQnABotSettings
          CUSTOM_SETTINGS_PARAM: !Ref CustomQnABotSettings
      Handler: index.handler
      MemorySize: "1408"
      Role: !GetAtt
        - ESProxyLambdaRole
        - Arn
      Runtime: nodejs16.x
      Timeout: 300
      VpcConfig: !If
        - VPCEnabled
        - SubnetIds:
            - !Ref "AWS::NoValue"
          SecurityGroupIds:
            - !Ref "AWS::NoValue"
        - !Ref "AWS::NoValue"
      TracingConfig: !If
        - XRAYEnabled
        - Mode: Active
        - !Ref "AWS::NoValue"
      Tags:
        - Key: Type
          Value: Service
  ESProxyLambdaRole:
    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"
        - "arn:aws:iam::aws:policy/AWSXRayDaemonWriteAccess"
        - "arn:aws:iam::aws:policy/TranslateReadOnly"
        - !Ref QueryPolicy
        - "arn:aws:iam::aws:policy/AmazonLexFullAccess"
      Policies:
        - PolicyName: ParamStorePolicy
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Effect: Allow
                Action:
                  - "ssm:GetParameter"
                  - "ssm:GetParameters"
                Resource: "*"
        - PolicyName: S3QNABucketReadAccess
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Effect: Allow
                Action:
                  - "s3:GetObject"
                Resource:
                  - "arn:aws:s3:::QNA*/*"
                  - "arn:aws:s3:::qna*/*"
  QueryLambdaInvokePolicy:
    Type: "AWS::IAM::ManagedPolicy"
    Properties:
      PolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Action:
              - "lambda:InvokeFunction"
            Resource:
              - "arn:aws:lambda:*:*:function:qna*"
              - "arn:aws:lambda:*:*:function:QNA*"
              - !GetAtt
                - ExamplesStack
                - Outputs.ExampleJSLambdaQuiz
              - !GetAtt
                - ExamplesStack
                - Outputs.ExampleJSLambdahook
              - !GetAtt
                - ExamplesStack
                - Outputs.ExamplePYTHONLambdaBotBroker
              - !GetAtt
                - ExamplesStack
                - Outputs.ExamplePYTHONLambdaConnectCallback
              - !GetAtt
                - ExamplesStack
                - Outputs.ExamplePYTHONLambdaFeedback
              - !GetAtt
                - ExamplesStack
                - Outputs.ExamplePYTHONLambdaNext
              - !GetAtt
                - ExamplesStack
                - Outputs.ExamplePYTHONLambdaPrevious
              - !GetAtt
                - ExamplesStack
                - Outputs.ExamplePYTHONLambdahello
              - !GetAtt
                - ExamplesStack
                - Outputs.EXTCustomJSHook
              - !GetAtt
                - ExamplesStack
                - Outputs.EXTCustomPYHook
      Roles:
        - !Ref ESProxyLambdaRole
  ESLoggingLambdaRole:
    Type: "AWS::IAM::Role"
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service: lambda.amazonaws.com
            Action: "sts:AssumeRole"
      Path: /
      Policies:
        - PolicyName: LambdaGeneralFirehoseQNALambda
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Effect: Allow
                Action:
                  - "lambda:InvokeFunction"
                Resource:
                  - !Join
                    - ""
                    - - "arn:aws:lambda:"
                      - !Ref "AWS::Region"
                      - ":"
                      - !Ref "AWS::AccountId"
                      - ":function:qna-*"
                  - !Join
                    - ""
                    - - "arn:aws:lambda:"
                      - !Ref "AWS::Region"
                      - ":"
                      - !Ref "AWS::AccountId"
                      - ":function:QNA-*"
              - Effect: Allow
                Action:
                  - "firehose:PutRecord"
                  - "firehose:PutRecordBatch"
                Resource:
                  - !GetAtt
                    - GeneralFirehose
                    - Arn
      ManagedPolicyArns:
        - "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
        - "arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole"
        - "arn:aws:iam::aws:policy/AWSXRayDaemonWriteAccess"
  QueryPolicy:
    Type: "AWS::IAM::ManagedPolicy"
    Properties:
      PolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Action:
              - "es:*"
            Resource:
              - "*"
          - Effect: Allow
            Action:
              - "kendra:Query"
            Resource:
              - !Sub "arn:aws:kendra:${AWS::Region}:${AWS::AccountId}:index/*"
          - Effect: Allow
            Action:
              - "s3:Get*"
            Resource:
              - !Sub "arn:aws:s3:::${AssetBucket}*"
          - Effect: Allow
            Action:
              - "comprehend:DetectSyntax"
            Resource:
              - "*"
  LexProxyLambda:
    Type: "AWS::Lambda::Function"
    Properties:
      Code:
        ZipFile: |+
          var aws=require('aws-sdk')
          aws.config.region=process.env.AWS_REGION
          var lex=new aws.LexModelBuildingService()

          exports.handler = (event, context, callback) => {
              console.log('Received event:', JSON.stringify(event, null, 2));
              
              lex[event.fnc](event.params).promise()
              .then(function(x){
                  console.log("Response: "+JSON.stringify(x,null,2))
                  callback(null,x)
              })
              .catch(function(y){
                  console.log("Error: "+y)
                  callback(JSON.stringify({
                      type:"[InternalServiceError]",
                      data:y
                  }))
              })
          };

      Handler: index.handler
      MemorySize: "128"
      Role: !GetAtt
        - LexProxyLambdaRole
        - Arn
      Runtime: nodejs16.x
      Timeout: 300
      VpcConfig: !If
        - VPCEnabled
        - SubnetIds:
            - !Ref "AWS::NoValue"
          SecurityGroupIds:
            - !Ref "AWS::NoValue"
        - !Ref "AWS::NoValue"
      TracingConfig: !If
        - XRAYEnabled
        - Mode: Active
        - !Ref "AWS::NoValue"
      Tags:
        - Key: Type
          Value: Api
  LexStatusLambda:
    Type: "AWS::Lambda::Function"
    Properties:
      Code:
        ZipFile: |+
          var aws=require('aws-sdk')
          aws.config.region=process.env.AWS_REGION
          var s3=new aws.S3()
          const lexv2 = new aws.LexModelsV2();

          exports.handler = async (event, context, callback) => {
              console.log('Received event:', JSON.stringify(event, null, 2));
              
              let bucket=process.env.STATUS_BUCKET;
              let lexV1StatusFile=process.env.STATUS_KEY;
              let lexV2StatusFile=process.env.LEXV2_STATUS_KEY;
              let build = {"status":"READY","token":"token"};
              let response;

              try {
                  response = await s3.getObject({Bucket:bucket, Key:lexV2StatusFile}).promise();
                  build = JSON.parse(response.Body.toString());
                  // combine build status with v1 bot, if defined.. If both are READY then status is READY
                  if (lexV1StatusFile) {
                      response = await s3.getObject({Bucket:bucket, Key:lexV1StatusFile}).promise();
                      let v1build = JSON.parse(response.Body.toString());
                      if (v1build.status != "READY" || build.status != "READY" ) {
                          build.status = "LEX V2: " + build.status + " / LEX V1: " + v1build.status
                      }
                  }
              } catch(e) {
                  console.log("Unable to read S3 lex bot status file - perhaps it doesn't yet exist. Returning READY");
              }

              response = await lexv2.describeBot({
                      botId:process.env.LEXV2_BOT_ID,
                  }).promise() ;
              // Match LexV1 bot status for code compatibility (Available = READY)
              let botStatus = (response.botStatus == "Available") ? "READY" :  response.botStatus ;
              
              return {
                  "lambdaArn": process.env.FULFILLMENT_FUNCTION_ARN,
                  "lambdaRole":process.env.FULFILLMENT_FUNCTION_ROLE,
                  "botversion":"live",
                  "botname":process.env.LEXV1_BOT_NAME || "LEX V1 Bot not installed",
                  "intent":process.env.LEXV1_INTENT || "LEX V1 Bot not installed",
                  "intentFallback":process.env.LEXV1_INTENT_FALLBACK || "LEX V1 Bot not installed",
                  "lexV2botname":process.env.LEXV2_BOT_NAME || "LEX V2 Bot not installed",
                  "lexV2botid":process.env.LEXV2_BOT_ID || "LEX V2 Bot not installed",
                  "lexV2botalias":process.env.LEXV2_BOT_ALIAS || "LEX V2 Bot not installed",
                  "lexV2botaliasid":process.env.LEXV2_BOT_ALIAS_ID || "LEX V2 Bot not installed",
                  "lexV2intent":process.env.LEXV2_INTENT || "LEX V2 Bot not installed",
                  "lexV2intentFallback":process.env.LEXV2_INTENT_FALLBACK || "LEX V2 Bot not installed",
                  "lexV2localeids":process.env.LEXV2_BOT_LOCALE_IDS || "LEX V2 Bot not installed",
                  "status":botStatus,
                  "build":build,
              } ;
          };


      Environment:
        Variables:
          STATUS_BUCKET: !Ref BuildStatusBucket
          STATUS_KEY: !If
            - CreateLexV1Bots
            - status.json
            - !Ref "AWS::NoValue"
          LEXV2_STATUS_KEY: lexV2status.json
          FULFILLMENT_FUNCTION_ARN: !GetAtt
            - FulfillmentLambda
            - Arn
          FULFILLMENT_FUNCTION_ROLE: !Ref FulfillmentLambdaRole
          LEXV1_BOT_NAME: !If
            - CreateLexV1Bots
            - !Ref LexBot
            - !Ref "AWS::NoValue"
          LEXV1_INTENT: !If
            - CreateLexV1Bots
            - !Ref Intent
            - !Ref "AWS::NoValue"
          LEXV1_INTENT_FALLBACK: !If
            - CreateLexV1Bots
            - !Ref IntentFallback
            - !Ref "AWS::NoValue"
          LEXV2_BOT_NAME: !GetAtt
            - LexV2Bot
            - botName
          LEXV2_BOT_ID: !GetAtt
            - LexV2Bot
            - botId
          LEXV2_BOT_ALIAS: !GetAtt
            - LexV2Bot
            - botAlias
          LEXV2_BOT_ALIAS_ID: !GetAtt
            - LexV2Bot
            - botAliasId
          LEXV2_INTENT: !GetAtt
            - LexV2Bot
            - botIntent
          LEXV2_INTENT_FALLBACK: !GetAtt
            - LexV2Bot
            - botIntentFallback
          LEXV2_BOT_LOCALE_IDS: !GetAtt
            - LexV2Bot
            - botLocaleIds
      Handler: index.handler
      MemorySize: "128"
      Role: !GetAtt
        - LexProxyLambdaRole
        - Arn
      Runtime: nodejs16.x
      Timeout: 300
      VpcConfig: !If
        - VPCEnabled
        - SubnetIds:
            - !Ref "AWS::NoValue"
          SecurityGroupIds:
            - !Ref "AWS::NoValue"
        - !Ref "AWS::NoValue"
      TracingConfig: !If
        - XRAYEnabled
        - Mode: Active
        - !Ref "AWS::NoValue"
      Tags:
        - Key: Type
          Value: Api
  LexProxyLambdaRole:
    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"
        - "arn:aws:iam::aws:policy/AmazonLexFullAccess"
        - "arn:aws:iam::aws:policy/AWSXRayDaemonWriteAccess"
      Policies:
        - PolicyName: Access
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Effect: Allow
                Action:
                  - "s3:Get*"
                Resource:
                  - !Sub "arn:aws:s3:::${BuildStatusBucket}*"
  KibanaRole:
    Type: "AWS::IAM::Role"
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Federated: cognito-identity.amazonaws.com
            Action: "sts:AssumeRoleWithWebIdentity"
            Condition:
              StringEquals:
                "cognito-identity.amazonaws.com:aud": !Ref KibanaIdPool
      Path: /
      ManagedPolicyArns: []
      Policies: []
  AdminRole:
    Type: "AWS::IAM::Role"
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Federated: cognito-identity.amazonaws.com
            Action: "sts:AssumeRoleWithWebIdentity"
            Condition:
              StringEquals:
                "cognito-identity.amazonaws.com:aud": !Ref IdPool
      Path: /
      ManagedPolicyArns: []
      Policies:
        - PolicyName: apiAccess
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Effect: Allow
                Action:
                  - "es:ESHttp*"
                Resource:
                  - !GetAtt
                    - ESVar
                    - ESArn
              - Effect: Allow
                Action:
                  - "execute-api:*"
                Resource:
                  - !Sub >-
                    arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${API}/*/*/*
              - Effect: Allow
                Action:
                  - "s3:PutObject"
                Resource:
                  - !Sub "arn:aws:s3:::${ImportBucket}/data/*"
                  - !Sub "arn:aws:s3:::${TestAllBucket}/data/*"
              - Effect: Allow
                Action:
                  - "s3:GetObject"
                Resource:
                  - !Sub "arn:aws:s3:::${ExportBucket}/data/*"
                  - !Sub "arn:aws:s3:::${TestAllBucket}/data/*"
              - Effect: Allow
                Action:
                  - "ssm:GetParameters"
                  - "ssm:PutParameter"
                Resource:
                  - !Sub >-
                    arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:parameter/${DefaultQnABotSettings}
                  - !Sub >-
                    arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:parameter/${CustomQnABotSettings}
  UserRole:
    Type: "AWS::IAM::Role"
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Federated: cognito-identity.amazonaws.com
            Action: "sts:AssumeRoleWithWebIdentity"
            Condition:
              StringEquals:
                "cognito-identity.amazonaws.com:aud": !Ref IdPool
      Path: /
      ManagedPolicyArns: []
  UnauthenticatedRole:
    Type: "AWS::IAM::Role"
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Federated: cognito-identity.amazonaws.com
            Action: "sts:AssumeRoleWithWebIdentity"
            Condition:
              StringEquals:
                "cognito-identity.amazonaws.com:aud": !Ref IdPool
      Path: /
      ManagedPolicyArns: []
  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"
        - "arn:aws:iam::aws:policy/AWSXRayDaemonWriteAccess"
      Policies:
        - PolicyName: access
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Effect: Allow
                Action:
                  - "es:*"
                Resource:
                  - "*"
              - Effect: Allow
                Action:
                  - "lex:PutSlotType"
                  - "lex:GetSlotType"
                  - "lex:DeleteSlotType"
                  - "lex:PutIntent"
                  - "lex:GetIntent"
                  - "lex:DeleteIntent"
                  - "lex:PutBot"
                  - "lex:GetBot"
                  - "lex:DeleteBot"
                  - "lex:PutBotAlias"
                  - "lex:DeleteBotAlias"
                  - "lex:GetBotAlias"
                  - "lex:GetBotVersions"
                  - "lex:GetIntentVersions"
                  - "lex:GetSlotTypeVersions"
                Resource:
                  - "*"
              - Effect: Allow
                Action:
                  - "apigateway:*"
                Resource:
                  - "*"
              - Effect: Allow
                Action:
                  - "cognito-identity:SetIdentityPoolRoles"
                  - "cognito-identity:GetIdentityPoolRoles"
                  - "iam:PassRole"
                  - "iam:CreateServiceLinkedRole"
                Resource:
                  - "*"
              - Effect: Allow
                Action:
                  - "cognito-idp:*"
                Resource:
                  - "*"
              - Effect: Allow
                Action:
                  - "s3:*"
                Resource:
                  - !Sub "arn:aws:s3:::*"
  Bot:
    Type: "AWS::ApiGateway::Resource"
    Properties:
      ParentId: !GetAtt
        - API
        - RootResourceId
      PathPart: bot
      RestApiId: !Ref API
  AlexaApi:
    Type: "AWS::ApiGateway::Resource"
    Properties:
      ParentId: !Ref Bot
      PathPart: alexa
      RestApiId: !Ref API
  AlexaSchema:
    Type: "AWS::ApiGateway::Method"
    Properties:
      AuthorizationType: AWS_IAM
      HttpMethod: GET
      Integration:
        Type: AWS
        IntegrationHttpMethod: POST
        Uri: !Join
          - ""
          - - "arn:aws:apigateway:"
            - !Ref "AWS::Region"
            - ":lambda:path/2015-03-31/functions/"
            - !GetAtt
              - UtteranceLambda
              - Arn
            - /invocations
        IntegrationResponses:
          - StatusCode: 200
            ResponseTemplates:
              application/json: !Sub |
                #set($inputRoot = $input.path('$'))
                #set($utterances = $inputRoot.utterances)

                {
                  "interactionModel": {
                    "languageModel": {
                      "invocationName": "q and a",
                      "types": [
                        {
                          "name": "EXAMPLE_QUESTIONS",
                          "values": [
                            #foreach( $utterance in $utterances)
                                {"name":{
                                    "value":"$utterance" 
                                }}#if( $foreach.hasNext ),#end
                            #end
                          ]
                        }
                        ## {
                        ##     "name": "EXAMPLE_QUESTIONS",
                        ##     "values": [
                        ##         {
                        ##             "name": {
                        ##                 "value": "this is required"
                        ##             }
                        ##         }
                        ##     ]
                        ##   }
                      ],
                      "intents": [
                        {
                          "slots": [
                            {
                              "name": "QnA_slot",
                              "type": "EXAMPLE_QUESTIONS"
                            }
                          ],
                          "name": "Qna_intent",
                          "samples": [
                            "{QnA_slot}"
                          ]
                        },
                        {
                          "name": "AMAZON.StopIntent"
                        },
                        {
                          "name": "AMAZON.RepeatIntent"
                        },
                        {
                          "name": "AMAZON.FallbackIntent"
                        },
                        {
                          "name": "AMAZON.CancelIntent"
                        }
                      ]
                    }
                  }
                }
          - SelectionPattern: ".*[InternalServiceError].*"
            StatusCode: 500
            ResponseTemplates:
              application/json: >+
                #set ($errorMessageObj =
                $util.parseJson($input.path('$.errorMessage')))



                {
                    "type":"$errorMessageObj.type",
                    "message":"$errorMessageObj.message",
                    "data":"$errorMessageObj.data"
                }

          - SelectionPattern: ".*[BadRequest].*"
            StatusCode: 400
            ResponseTemplates:
              application/json: >+
                #set ($errorMessageObj =
                $util.parseJson($input.path('$.errorMessage')))



                {
                    "type":"$errorMessageObj.type",
                    "message":"$errorMessageObj.message",
                    "data":"$errorMessageObj.data"
                }

          - SelectionPattern: ".*[Conflict].*"
            StatusCode: 409
            ResponseTemplates:
              application/json: >+
                #set ($errorMessageObj =
                $util.parseJson($input.path('$.errorMessage')))



                {
                    "type":"$errorMessageObj.type",
                    "message":"$errorMessageObj.message",
                    "data":"$errorMessageObj.data"
                }

          - SelectionPattern: ".*[NotFound].*"
            StatusCode: 404
            ResponseTemplates:
              application/json: >+
                #set ($errorMessageObj =
                $util.parseJson($input.path('$.errorMessage')))



                {
                    "type":"$errorMessageObj.type",
                    "message":"$errorMessageObj.message",
                    "data":"$errorMessageObj.data"
                }

        RequestTemplates:
          application/json: !Sub |+
            {
            }                
                           

      ResourceId: !Ref AlexaApi
      MethodResponses:
        - StatusCode: 200
          ResponseParameters:
            method.response.header.date: true
        - StatusCode: 404
        - StatusCode: 500
      RestApiId: !Ref API
  BotPost:
    Type: "AWS::ApiGateway::Method"
    Properties:
      AuthorizationType: AWS_IAM
      HttpMethod: POST
      Integration:
        Type: AWS
        IntegrationHttpMethod: POST
        Uri: !Join
          - ""
          - - "arn:aws:apigateway:"
            - !Ref "AWS::Region"
            - ":lambda:path/2015-03-31/functions/"
            - !GetAtt
              - LexBuildLambdaStart
              - Arn
            - /invocations
        IntegrationResponses:
          - StatusCode: 200
            ResponseTemplates:
              application/json: !Sub |
                {"token":"$input.path('$.token')"}
          - SelectionPattern: ".*[InternalServiceError].*"
            StatusCode: 500
            ResponseTemplates:
              application/json: >+
                #set ($errorMessageObj =
                $util.parseJson($input.path('$.errorMessage')))



                {
                    "type":"$errorMessageObj.type",
                    "message":"$errorMessageObj.message",
                    "data":"$errorMessageObj.data"
                }

          - SelectionPattern: ".*[BadRequest].*"
            StatusCode: 400
            ResponseTemplates:
              application/json: >+
                #set ($errorMessageObj =
                $util.parseJson($input.path('$.errorMessage')))



                {
                    "type":"$errorMessageObj.type",
                    "message":"$errorMessageObj.message",
                    "data":"$errorMessageObj.data"
                }

          - SelectionPattern: ".*[Conflict].*"
            StatusCode: 409
            ResponseTemplates:
              application/json: >+
                #set ($errorMessageObj =
                $util.parseJson($input.path('$.errorMessage')))



                {
                    "type":"$errorMessageObj.type",
                    "message":"$errorMessageObj.message",
                    "data":"$errorMessageObj.data"
                }

          - SelectionPattern: ".*[NotFound].*"
            StatusCode: 404
            ResponseTemplates:
              application/json: >+
                #set ($errorMessageObj =
                $util.parseJson($input.path('$.errorMessage')))



                {
                    "type":"$errorMessageObj.type",
                    "message":"$errorMessageObj.message",
                    "data":"$errorMessageObj.data"
                }

      ResourceId: !Ref Bot
      MethodResponses:
        - StatusCode: 200
          ResponseParameters:
            method.response.header.date: true
        - StatusCode: 404
        - StatusCode: 500
      RestApiId: !Ref API
  BotGet:
    Type: "AWS::ApiGateway::Method"
    Properties:
      AuthorizationType: AWS_IAM
      HttpMethod: GET
      Integration:
        Type: AWS
        IntegrationHttpMethod: POST
        Uri: !Join
          - ""
          - - "arn:aws:apigateway:"
            - !Ref "AWS::Region"
            - ":lambda:path/2015-03-31/functions/"
            - !GetAtt
              - LexStatusLambda
              - Arn
            - /invocations
        IntegrationResponses:
          - StatusCode: 200
            ResponseTemplates:
              application/json: !Sub >+
                #set (
                $root="https://${!context.apiId}.execute-api.${!stageVariables.Region}.amazonaws.com/${!context.stage}")

                #set($inputRoot = $input.path('$'))


                {
                    "lambdaArn": "$inputRoot.lambdaArn",
                    "lambdaRole":"$inputRoot.lambdaRole",
                    "botversion":"$inputRoot.botversion",
                    "botname":"$inputRoot.botname",
                    "intent":"$inputRoot.intent",
                    "intentFallback":"$inputRoot.intentFallback",
                    "lexV2botname":"$inputRoot.lexV2botname",
                    "lexV2botid":"$inputRoot.lexV2botid",
                    "lexV2botalias":"$inputRoot.lexV2botalias",
                    "lexV2botaliasid":"$inputRoot.lexV2botaliasid",
                    "lexV2intent":"$inputRoot.lexV2intent",
                    "lexV2intentFallback":"$inputRoot.lexV2intentFallback",
                    "lexV2localeids":"$inputRoot.lexV2localeids",
                    "status":"$inputRoot.status",
                    "build":$input.json('$.build'),
                    "_links":{
                        "alexa":{
                            "href":"$root/bot/alexa"
                        }
                    }
                }

          - SelectionPattern: ".*[InternalServiceError].*"
            StatusCode: 500
            ResponseTemplates:
              application/json: >+
                #set ($errorMessageObj =
                $util.parseJson($input.path('$.errorMessage')))



                {
                    "type":"$errorMessageObj.type",
                    "message":"$errorMessageObj.message",
                    "data":"$errorMessageObj.data"
                }

          - SelectionPattern: ".*[BadRequest].*"
            StatusCode: 400
            ResponseTemplates:
              application/json: >+
                #set ($errorMessageObj =
                $util.parseJson($input.path('$.errorMessage')))



                {
                    "type":"$errorMessageObj.type",
                    "message":"$errorMessageObj.message",
                    "data":"$errorMessageObj.data"
                }

          - SelectionPattern: ".*[Conflict].*"
            StatusCode: 409
            ResponseTemplates:
              application/json: >+
                #set ($errorMessageObj =
                $util.parseJson($input.path('$.errorMessage')))



                {
                    "type":"$errorMessageObj.type",
                    "message":"$errorMessageObj.message",
                    "data":"$errorMessageObj.data"
                }

          - SelectionPattern: ".*[NotFound].*"
            StatusCode: 404
            ResponseTemplates:
              application/json: >+
                #set ($errorMessageObj =
                $util.parseJson($input.path('$.errorMessage')))



                {
                    "type":"$errorMessageObj.type",
                    "message":"$errorMessageObj.message",
                    "data":"$errorMessageObj.data"
                }

        RequestTemplates:
          application/json: !Sub |+
            {
                "fnc":"getBot"
            }                
                           

      ResourceId: !Ref Bot
      MethodResponses:
        - StatusCode: 200
          ResponseParameters:
            method.response.header.date: true
        - StatusCode: 404
        - StatusCode: 500
      RestApiId: !Ref API
  BotDoc:
    Type: "AWS::ApiGateway::DocumentationPart"
    Properties:
      Location:
        Type: RESOURCE
        Path: /bot
      Properties: '{"description":""}'
      RestApiId: !Ref API
  Health:
    Type: "AWS::ApiGateway::Resource"
    Properties:
      ParentId: !GetAtt
        - API
        - RootResourceId
      PathPart: health
      RestApiId: !Ref API
  HealthGet:
    Type: "AWS::ApiGateway::Method"
    Properties:
      AuthorizationType: AWS_IAM
      HttpMethod: GET
      Integration:
        Type: AWS
        IntegrationHttpMethod: POST
        Uri: !Join
          - ""
          - - "arn:aws:apigateway:"
            - !Ref "AWS::Region"
            - ":lambda:path/2015-03-31/functions/"
            - !GetAtt
              - ESProxyLambda
              - Arn
            - /invocations
        IntegrationResponses:
          - StatusCode: 200
            ResponseTemplates:
              application/json: !Sub |+
                {"status":"health"}

          - SelectionPattern: ".*[InternalServiceError].*"
            StatusCode: 500
            ResponseTemplates:
              application/json: >+
                #set ($errorMessageObj =
                $util.parseJson($input.path('$.errorMessage')))



                {
                    "type":"$errorMessageObj.type",
                    "message":"$errorMessageObj.message",
                    "data":"$errorMessageObj.data"
                }

          - SelectionPattern: ".*[BadRequest].*"
            StatusCode: 400
            ResponseTemplates:
              application/json: >+
                #set ($errorMessageObj =
                $util.parseJson($input.path('$.errorMessage')))



                {
                    "type":"$errorMessageObj.type",
                    "message":"$errorMessageObj.message",
                    "data":"$errorMessageObj.data"
                }

          - SelectionPattern: ".*[Conflict].*"
            StatusCode: 409
            ResponseTemplates:
              application/json: >+
                #set ($errorMessageObj =
                $util.parseJson($input.path('$.errorMessage')))



                {
                    "type":"$errorMessageObj.type",
                    "message":"$errorMessageObj.message",
                    "data":"$errorMessageObj.data"
                }

          - SelectionPattern: ".*[NotFound].*"
            StatusCode: 404
            ResponseTemplates:
              application/json: >+
                #set ($errorMessageObj =
                $util.parseJson($input.path('$.errorMessage')))



                {
                    "type":"$errorMessageObj.type",
                    "message":"$errorMessageObj.message",
                    "data":"$errorMessageObj.data"
                }

        RequestTemplates:
          application/json: !Sub |+
            {
                "endpoint":"${ESVar.ESAddress}",
                "method":"GET",
                "path":"/_cluster/health"
            }

      ResourceId: !Ref Health
      MethodResponses:
        - StatusCode: 200
          ResponseParameters:
            method.response.header.date: true
        - StatusCode: 404
        - StatusCode: 500
      RestApiId: !Ref API
  rootGet:
    Type: "AWS::ApiGateway::Method"
    Properties:
      AuthorizationType: NONE
      HttpMethod: GET
      Integration:
        Type: MOCK
        IntegrationResponses:
          - ResponseTemplates:
              application/json: !Sub >+
                #set (
                $root="https://${!context.apiId}.execute-api.${!stageVariables.Region}.amazonaws.com/${!context.stage}")


                {
                    "region":"${!stageVariables.Region}",
                    "Version":"${InfoVar.Version}",
                    "BuildDate":"${InfoVar.BuildDateString}",
                    "BotName":"Use LexV2 bot",
                    "BotVersion":"$LATEST",
                    "v2BotId": "${LexV2Bot.botId}",
                    "v2BotAliasId": "${LexV2Bot.botAliasId}",
                    "v2BotLocaleId": "${LexV2BotLocaleIds}",
                    "PoolId":"${IdPool}",
                    "StackName":"${AWS::StackName}",
                    "ClientIdClient":"${ClientClient}",
                    "ClientIdDesigner":"${ClientDesigner}",
                    "UserPool":"${UserPool}",
                    "DefaultQnABotSettings":"${DefaultQnABotSettings}",
                    "CustomQnABotSettings":"${CustomQnABotSettings}",
                    "KendraCrawlerSnsTopic":"${KendraCrawlerSnsTopic}",
                    "Id":"$stageVariables.Id",
                    "_links":{
                        "root":{
                            "href":"$root"
                        },
                        "questions":{
                            "href":"$root/questions"
                        },        
                        "crawler":{
                            "href":"$root/crawler"
                        },
                        "bot":{
                            "href":"$root/bot"
                        },
                        "jobs":{
                            "href":"$root/jobs"
                        },
                        "connect":{
                            "href":"$root/connect"
                        },
                        "translate":{
                            "href":"$root/translate"
                        },
                        "examples":{
                            "href":"$root/examples/documents"
                        },
                        "DesignerLogin":{
                            "href":"$stageVariables.DesignerLoginUrl"
                        },
                        "ClientLogin":{
                            "href":"$stageVariables.ClientLoginUrl"
                        },
                        "CognitoEndpoint":{
                            "href":"$stageVariables.CognitoEndpoint"
                        },
                        "Services":{
                            "href":"$root/services"
                        },
                        "Kibana":{
                            "href":"https://${Urls.Kibana}"
                        }
                    }
                }

            StatusCode: "200"
        RequestTemplates:
          application/json: '{"statusCode": 200}'
      ResourceId: !GetAtt
        - API
        - RootResourceId
      MethodResponses:
        - StatusCode: 200
      RestApiId: !Ref API
  Questions:
    Type: "AWS::ApiGateway::Resource"
    Properties:
      ParentId: !GetAtt
        - API
        - RootResourceId
      PathPart: questions
      RestApiId: !Ref API
  QuestionsGet:
    Type: "AWS::ApiGateway::Method"
    Properties:
      AuthorizationType: AWS_IAM
      HttpMethod: GET
      Integration:
        Type: AWS
        IntegrationHttpMethod: POST
        Uri: !Join
          - ""
          - - "arn:aws:apigateway:"
            - !Ref "AWS::Region"
            - ":lambda:path/2015-03-31/functions/"
            - !GetAtt
              - ESProxyLambda
              - Arn
            - /invocations
        IntegrationResponses:
          - StatusCode: 200
            ResponseTemplates:
              application/json: !Sub |+
                #set($inputRoot = $input.path('$'))

                {
                    "total":$inputRoot.hits.total.value,
                    "version":"1",
                    "qa":[
                        #foreach( $hit in $inputRoot.hits.hits)
                            {
                                #set($Scorepath =  '$.hits.hits['+$foreach.index+']._score')
                                "_score":$input.json($Scorepath),
                                #set($Bodypath =  '$.hits.hits['+$foreach.index+']._source')
                                #foreach($paramName in $input.path($Bodypath).keySet())
                                    #if( $paramName == 'questions')
                                        "q":[
                                            #foreach( $question in $input.path($Bodypath).get($paramName))
                                                "$question.q"
                                                #if($foreach.hasNext),#end
                                            #end
                                        ]
                                    #else
                                        #set( $body =  $Bodypath+"."+$paramName)
                                        "$paramName" :$input.json($body) 
                                    #end
                                #if($foreach.hasNext),#end
                                #end
                            }#if( $foreach.hasNext ),#end
                        #end
                    ]
                }

          - SelectionPattern: ".*[InternalServiceError].*"
            StatusCode: 500
            ResponseTemplates:
              application/json: >+
                #set ($errorMessageObj =
                $util.parseJson($input.path('$.errorMessage')))



                {
                    "type":"$errorMessageObj.type",
                    "message":"$errorMessageObj.message",
                    "data":"$errorMessageObj.data"
                }

          - SelectionPattern: ".*[BadRequest].*"
            StatusCode: 400
            ResponseTemplates:
              application/json: >+
                #set ($errorMessageObj =
                $util.parseJson($input.path('$.errorMessage')))



                {
                    "type":"$errorMessageObj.type",
                    "message":"$errorMessageObj.message",
                    "data":"$errorMessageObj.data"
                }

          - SelectionPattern: ".*[Conflict].*"
            StatusCode: 409
            ResponseTemplates:
              application/json: >+
                #set ($errorMessageObj =
                $util.parseJson($input.path('$.errorMessage')))



                {
                    "type":"$errorMessageObj.type",
                    "message":"$errorMessageObj.message",
                    "data":"$errorMessageObj.data"
                }

          - SelectionPattern: ".*[NotFound].*"
            StatusCode: 404
            ResponseTemplates:
              application/json: >+
                #set ($errorMessageObj =
                $util.parseJson($input.path('$.errorMessage')))



                {
                    "type":"$errorMessageObj.type",
                    "message":"$errorMessageObj.message",
                    "data":"$errorMessageObj.data"
                }

        RequestTemplates:
          application/json: !Sub |+
            #if ( $input.params('perpage').length()==0 )
                #set ( $perpage = 10 )
            #else
                #set ( $perpage = $input.params('perpage') )
            #end

            #if ( $input.params('from').length()==0)
                #set ( $from = 0 )
            #else
                #set ( $from = $input.params('from') )
            #end

            #if ( $input.params('order').length()==0 )
                #set ( $order = "asc" )
            #else
                #set ( $order = $input.params('order') )
            #end
                
            {
                "endpoint":"${ESVar.ESAddress}",
                "method":"POST",

                
                #if($input.params('query').length()>0)
                "path":"/${Var.QnaIndex}/_search?search_type=dfs_query_then_fetch",
                "question": "$util.urlDecode($input.params('query'))",
                #else
                "path":"/${Var.QnaIndex}/_search?search_type=dfs_query_then_fetch",
                "question": "",
                #end
                
                #if ($input.params('topic'))
                "topic": "$util.urlDecode($input.params('topic'))",
                #else
                "topic": "",
                #end

                "size":"$perpage",
                "from":"$from",


                "body":{
                    #if($input.params('query').length()>0)
                    "comment": "ES Query for test queries are now built dynamically by ESProxy Lambda handler."
                    #else
                    "size":"$perpage",
                    "from":"$from",
                    "query": {
                        "bool":{
                            #if($input.params('filter').length()==0)
                            "must":{"match_all":{}}
                            #else
                            "filter":{"regexp":{
                                "qid":"$util.urlDecode($input.params('filter'))"
                            }}
                            #end
                        }
                    }
                    ,"sort":{
                        "qid":{
                            "order":"$order" 
                        }
                    }
                    #end
                }
            }

      RequestParameters:
        method.request.querystring.query: false
        method.request.querystring.topic: false
        method.request.querystring.from: false
        method.request.querystring.filter: false
        method.request.querystring.order: false
        method.request.querystring.perpage: false
      ResourceId: !Ref Questions
      MethodResponses:
        - StatusCode: 200
          ResponseParameters:
            method.response.header.date: true
        - StatusCode: 404
        - StatusCode: 500
      RestApiId: !Ref API
  QuestionsDelete:
    Type: "AWS::ApiGateway::Method"
    Properties:
      AuthorizationType: AWS_IAM
      HttpMethod: DELETE
      Integration:
        Type: AWS
        IntegrationHttpMethod: POST
        Uri: !Join
          - ""
          - - "arn:aws:apigateway:"
            - !Ref "AWS::Region"
            - ":lambda:path/2015-03-31/functions/"
            - !GetAtt
              - ESProxyLambda
              - Arn
            - /invocations
        IntegrationResponses:
          - StatusCode: 204
            ResponseTemplates:
              application/json: !Sub |
                {
                    "message":"success",
                    "count":"$input.path('$.deleted')"
                }
          - SelectionPattern: ".*[InternalServiceError].*"
            StatusCode: 500
            ResponseTemplates:
              application/json: >+
                #set ($errorMessageObj =
                $util.parseJson($input.path('$.errorMessage')))



                {
                    "type":"$errorMessageObj.type",
                    "message":"$errorMessageObj.message",
                    "data":"$errorMessageObj.data"
                }

          - SelectionPattern: ".*[BadRequest].*"
            StatusCode: 400
            ResponseTemplates:
              application/json: >+
                #set ($errorMessageObj =
                $util.parseJson($input.path('$.errorMessage')))



                {
                    "type":"$errorMessageObj.type",
                    "message":"$errorMessageObj.message",
                    "data":"$errorMessageObj.data"
                }

          - SelectionPattern: ".*[Conflict].*"
            StatusCode: 409
            ResponseTemplates:
              application/json: >+
                #set ($errorMessageObj =
                $util.parseJson($input.path('$.errorMessage')))



                {
                    "type":"$errorMessageObj.type",
                    "message":"$errorMessageObj.message",
                    "data":"$errorMessageObj.data"
                }

          - SelectionPattern: ".*[NotFound].*"
            StatusCode: 404
            ResponseTemplates:
              application/json: >+
                #set ($errorMessageObj =
                $util.parseJson($input.path('$.errorMessage')))



                {
                    "type":"$errorMessageObj.type",
                    "message":"$errorMessageObj.message",
                    "data":"$errorMessageObj.data"
                }

        RequestTemplates:
          application/json: !Sub |+
            {
                "endpoint":"${ESVar.ESAddress}",
                "method":"POST",
                "path":"/${Var.QnaIndex}/_delete_by_query?refresh=true",
                "body":{
                    "query":{
                        #if($input.path('$.query').length()!=0)
                        "bool":{
                            "must":{"match_all":{}},
                            "filter":{"regexp":{
                                "qid":"$input.path('$.query')"
                            }}
                        }
                        #else
                        "terms":{
                            "qid":[
                            #foreach($qid in $input.path('$.list'))
                                "$qid"#if($foreach.hasNext),#end
                            #end]
                        }
                        #end
                    }
                }
            }


      ResourceId: !Ref Questions
      MethodResponses:
        - StatusCode: 204
          ResponseParameters:
            method.response.header.date: true
        - StatusCode: 404
        - StatusCode: 500
      RestApiId: !Ref API
  Question:
    Type: "AWS::ApiGateway::Resource"
    Properties:
      ParentId: !Ref Questions
      PathPart: "{ID}"
      RestApiId: !Ref API
  QuestionHead:
    Type: "AWS::ApiGateway::Method"
    Properties:
      AuthorizationType: AWS_IAM
      HttpMethod: HEAD
      Integration:
        Type: AWS
        IntegrationHttpMethod: POST
        Uri: !Join
          - ""
          - - "arn:aws:apigateway:"
            - !Ref "AWS::Region"
            - ":lambda:path/2015-03-31/functions/"
            - !GetAtt
              - ESProxyLambda
              - Arn
            - /invocations
        IntegrationResponses:
          - StatusCode: 200
            ResponseTemplates:
              application/json: !Sub |
                {"status":"exists"}
          - SelectionPattern: ".*[InternalServiceError].*"
            StatusCode: 500
            ResponseTemplates:
              application/json: >+
                #set ($errorMessageObj =
                $util.parseJson($input.path('$.errorMessage')))



                {
                    "type":"$errorMessageObj.type",
                    "message":"$errorMessageObj.message",
                    "data":"$errorMessageObj.data"
                }

          - SelectionPattern: ".*[BadRequest].*"
            StatusCode: 400
            ResponseTemplates:
              application/json: >+
                #set ($errorMessageObj =
                $util.parseJson($input.path('$.errorMessage')))



                {
                    "type":"$errorMessageObj.type",
                    "message":"$errorMessageObj.message",
                    "data":"$errorMessageObj.data"
                }

          - SelectionPattern: ".*[Conflict].*"
            StatusCode: 409
            ResponseTemplates:
              application/json: >+
                #set ($errorMessageObj =
                $util.parseJson($input.path('$.errorMessage')))



                {
                    "type":"$errorMessageObj.type",
                    "message":"$errorMessageObj.message",
                    "data":"$errorMessageObj.data"
                }

          - SelectionPattern: ".*[NotFound].*"
            StatusCode: 404
            ResponseTemplates:
              application/json: >+
                #set ($errorMessageObj =
                $util.parseJson($input.path('$.errorMessage')))



                {
                    "type":"$errorMessageObj.type",
                    "message":"$errorMessageObj.message",
                    "data":"$errorMessageObj.data"
                }

        RequestTemplates:
          application/json: !Sub |+
            {
                "endpoint":"${ESVar.ESAddress}",
                "method":"HEAD",
                "path":"/${Var.QnaIndex}/_all/$util.urlDecode($input.params('ID'))"
            }


      RequestParameters:
        method.request.path.Id: true
      ResourceId: !Ref Question
      MethodResponses:
        - StatusCode: 200
          ResponseParameters:
            method.response.header.date: true
        - StatusCode: 404
        - StatusCode: 500
      RestApiId: !Ref API
  QuestionPut:
    Type: "AWS::ApiGateway::Method"
    Properties:
      AuthorizationType: AWS_IAM
      HttpMethod: PUT
      Integration:
        Type: AWS
        IntegrationHttpMethod: POST
        Uri: !Join
          - ""
          - - "arn:aws:apigateway:"
            - !Ref "AWS::Region"
            - ":lambda:path/2015-03-31/functions/"
            - !GetAtt
              - ESProxyLambda
              - Arn
            - /invocations
        IntegrationResponses:
          - StatusCode: 201
            ResponseTemplates:
              application/json: !Sub |+
                #set($inputRoot = $input.path('$'))
                #set($Idpath =  '$._id')
                #set($Successpath =  '$._shards.successful')

                {
                    "result":"$inputRoot.result",
                    "id":$input.json($Idpath),
                    "success":$input.json($Successpath)
                }

          - SelectionPattern: ".*[InternalServiceError].*"
            StatusCode: 500
            ResponseTemplates:
              application/json: >+
                #set ($errorMessageObj =
                $util.parseJson($input.path('$.errorMessage')))



                {
                    "type":"$errorMessageObj.type",
                    "message":"$errorMessageObj.message",
                    "data":"$errorMessageObj.data"
                }

          - SelectionPattern: ".*[BadRequest].*"
            StatusCode: 400
            ResponseTemplates:
              application/json: >+
                #set ($errorMessageObj =
                $util.parseJson($input.path('$.errorMessage')))



                {
                    "type":"$errorMessageObj.type",
                    "message":"$errorMessageObj.message",
                    "data":"$errorMessageObj.data"
                }

          - SelectionPattern: ".*[Conflict].*"
            StatusCode: 409
            ResponseTemplates:
              application/json: >+
                #set ($errorMessageObj =
                $util.parseJson($input.path('$.errorMessage')))



                {
                    "type":"$errorMessageObj.type",
                    "message":"$errorMessageObj.message",
                    "data":"$errorMessageObj.data"
                }

          - SelectionPattern: ".*[NotFound].*"
            StatusCode: 404
            ResponseTemplates:
              application/json: >+
                #set ($errorMessageObj =
                $util.parseJson($input.path('$.errorMessage')))



                {
                    "type":"$errorMessageObj.type",
                    "message":"$errorMessageObj.message",
                    "data":"$errorMessageObj.data"
                }

        RequestTemplates:
          application/json: !Sub |+
            #set($inputRoot = $input.path('$'))

            #if($input.json('$.type').length())
                #set($type=$inputRoot.type)
            #else
                #set($type="qna")
            #end

            {
                "endpoint":"${ESVar.ESAddress}",
                "method":"PUT",
                "path":"/${Var.QnaIndex}/_doc/$input.params('ID')?refresh=wait_for",
                "body":{
                    #foreach($paramName in $inputRoot.keySet())
                        #if( $paramName == 'q' && $type=="qna")
                            ## generate quniqueterms field by concatenating questions in q array
                            "quniqueterms":" #foreach( $q in $inputRoot.get($paramName))$q #end ",
                            ## replace q array with nested questions array
                            "questions":[
                                #foreach( $q in $inputRoot.get($paramName))
                                    {"q":"$q"}
                                    #if($foreach.hasNext),#end
                                #end
                            ]
                            #if($foreach.hasNext),#end
                        #else
                            #set( $body =  '$.'+$paramName)
                            "$paramName" :$input.json($body) 
                            #if($foreach.hasNext),#end
                        #end
                    #end
                }
            }


      RequestParameters:
        method.request.path.Id: true
      ResourceId: !Ref Question
      MethodResponses:
        - StatusCode: 201
          ResponseParameters:
            method.response.header.date: true
        - StatusCode: 404
        - StatusCode: 500
      RestApiId: !Ref API
  QuestionsOptions:
    Type: "AWS::ApiGateway::Method"
    Properties:
      AuthorizationType: AWS_IAM
      HttpMethod: OPTIONS
      Integration:
        Type: AWS
        IntegrationHttpMethod: POST
        Uri: !Join
          - ""
          - - "arn:aws:apigateway:"
            - !Ref "AWS::Region"
            - ":lambda:path/2015-03-31/functions/"
            - !GetAtt
              - SchemaLambda
              - Arn
            - /invocations
        IntegrationResponses:
          - StatusCode: 200
          - SelectionPattern: ".*[InternalServiceError].*"
            StatusCode: 500
            ResponseTemplates:
              application/json: >+
                #set ($errorMessageObj =
                $util.parseJson($input.path('$.errorMessage')))



                {
                    "type":"$errorMessageObj.type",
                    "message":"$errorMessageObj.message",
                    "data":"$errorMessageObj.data"
                }

          - SelectionPattern: ".*[BadRequest].*"
            StatusCode: 400
            ResponseTemplates:
              application/json: >+
                #set ($errorMessageObj =
                $util.parseJson($input.path('$.errorMessage')))



                {
                    "type":"$errorMessageObj.type",
                    "message":"$errorMessageObj.message",
                    "data":"$errorMessageObj.data"
                }

          - SelectionPattern: ".*[Conflict].*"
            StatusCode: 409
            ResponseTemplates:
              application/json: >+
                #set ($errorMessageObj =
                $util.parseJson($input.path('$.errorMessage')))



                {
                    "type":"$errorMessageObj.type",
                    "message":"$errorMessageObj.message",
                    "data":"$errorMessageObj.data"
                }

          - SelectionPattern: ".*[NotFound].*"
            StatusCode: 404
            ResponseTemplates:
              application/json: >+
                #set ($errorMessageObj =
                $util.parseJson($input.path('$.errorMessage')))



                {
                    "type":"$errorMessageObj.type",
                    "message":"$errorMessageObj.message",
                    "data":"$errorMessageObj.data"
                }

        RequestTemplates:
          application/json: !Sub |+
            {
                "comment": "API mapping no-op since ES 7.x upgrade. Schema now returned directly from SchemaLambda, rather than from Elasticsearch metadata"
            }

      ResourceId: !Ref Questions
      MethodResponses:
        - StatusCode: 200
          ResponseParameters:
            method.response.header.date: true
        - StatusCode: 404
        - StatusCode: 500
      RestApiId: !Ref API
  QuestionDelete:
    Type: "AWS::ApiGateway::Method"
    Properties:
      AuthorizationType: AWS_IAM
      HttpMethod: DELETE
      Integration:
        Type: AWS
        IntegrationHttpMethod: POST
        Uri: !Join
          - ""
          - - "arn:aws:apigateway:"
            - !Ref "AWS::Region"
            - ":lambda:path/2015-03-31/functions/"
            - !GetAtt
              - ESProxyLambda
              - Arn
            - /invocations
        IntegrationResponses:
          - StatusCode: 204
            ResponseTemplates:
              application/json: !Sub |+
                #set($inputRoot = $input.path('$'))
                #set($Idpath =  '$._id')
                #set($Successpath =  '$._shards.successful')

                {
                    "result":"$inputRoot.result",
                    "id":$input.json($Idpath),
                    "success":$input.json($Successpath)
                }

          - SelectionPattern: ".*[InternalServiceError].*"
            StatusCode: 500
            ResponseTemplates:
              application/json: >+
                #set ($errorMessageObj =
                $util.parseJson($input.path('$.errorMessage')))



                {
                    "type":"$errorMessageObj.type",
                    "message":"$errorMessageObj.message",
                    "data":"$errorMessageObj.data"
                }

          - SelectionPattern: ".*[BadRequest].*"
            StatusCode: 400
            ResponseTemplates:
              application/json: >+
                #set ($errorMessageObj =
                $util.parseJson($input.path('$.errorMessage')))



                {
                    "type":"$errorMessageObj.type",
                    "message":"$errorMessageObj.message",
                    "data":"$errorMessageObj.data"
                }

          - SelectionPattern: ".*[Conflict].*"
            StatusCode: 409
            ResponseTemplates:
              application/json: >+
                #set ($errorMessageObj =
                $util.parseJson($input.path('$.errorMessage')))



                {
                    "type":"$errorMessageObj.type",
                    "message":"$errorMessageObj.message",
                    "data":"$errorMessageObj.data"
                }

          - SelectionPattern: ".*[NotFound].*"
            StatusCode: 404
            ResponseTemplates:
              application/json: >+
                #set ($errorMessageObj =
                $util.parseJson($input.path('$.errorMessage')))



                {
                    "type":"$errorMessageObj.type",
                    "message":"$errorMessageObj.message",
                    "data":"$errorMessageObj.data"
                }

        RequestTemplates:
          application/json: !Sub |+
            {
                "endpoint":"${ESVar.ESAddress}",
                "method":"POST",
                "path":"/${Var.QnaIndex}/_delete_by_query?refresh=true",
                "body":{
                    "query":{
                        "match":{
                            "qid":"$util.urlDecode($input.params('ID'))"
                        }
                    }
                }

            }


      RequestParameters:
        method.request.path.Id: true
      ResourceId: !Ref Question
      MethodResponses:
        - StatusCode: 204
          ResponseParameters:
            method.response.header.date: true
        - StatusCode: 404
        - StatusCode: 500
      RestApiId: !Ref API
  Static:
    Type: "AWS::ApiGateway::Resource"
    Properties:
      ParentId: !GetAtt
        - API
        - RootResourceId
      PathPart: static
      RestApiId: !Ref API
  Proxy:
    Type: "AWS::ApiGateway::Resource"
    Properties:
      ParentId: !Ref Static
      PathPart: "{proxy+}"
      RestApiId: !Ref API
  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 API
  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 API
  Login:
    Type: "AWS::ApiGateway::Resource"
    Properties:
      ParentId: !GetAtt
        - API
        - RootResourceId
      PathPart: pages
      RestApiId: !Ref API
  DesignerLoginResource:
    Type: "AWS::ApiGateway::Resource"
    Properties:
      ParentId: !Ref Login
      PathPart: designer
      RestApiId: !Ref API
  ClientLoginResource:
    Type: "AWS::ApiGateway::Resource"
    Properties:
      ParentId: !Ref Login
      PathPart: client
      RestApiId: !Ref API
  DesignerLoginResourceGet:
    Type: "AWS::ApiGateway::Method"
    Properties:
      AuthorizationType: NONE
      HttpMethod: GET
      Integration:
        Type: MOCK
        IntegrationResponses:
          - ResponseParameters:
              method.response.header.location: !Join
                - ""
                - - "'"
                  - !GetAtt
                    - DesignerLogin
                    - loginUrl
                  - "'"
            StatusCode: "302"
        RequestTemplates:
          application/json: '{"statusCode": 302}'
      ResourceId: !Ref DesignerLoginResource
      MethodResponses:
        - StatusCode: 302
          ResponseParameters:
            method.response.header.location: true
      RestApiId: !Ref API
  ClientLoginResourceGet:
    Type: "AWS::ApiGateway::Method"
    Properties:
      AuthorizationType: NONE
      HttpMethod: GET
      Integration:
        Type: MOCK
        IntegrationResponses:
          - ResponseParameters:
              method.response.header.location: !Join
                - ""
                - - "'"
                  - !GetAtt
                    - ClientLogin
                    - loginUrl
                  - "'"
            StatusCode: "302"
        RequestTemplates:
          application/json: '{"statusCode": 302}'
      ResourceId: !Ref ClientLoginResource
      MethodResponses:
        - StatusCode: 302
          ResponseParameters:
            method.response.header.location: true
      RestApiId: !Ref API
  Jobs:
    Type: "AWS::ApiGateway::Resource"
    Properties:
      ParentId: !GetAtt
        - API
        - RootResourceId
      PathPart: jobs
      RestApiId: !Ref API
  JobsGet:
    Type: "AWS::ApiGateway::Method"
    Properties:
      AuthorizationType: AWS_IAM
      HttpMethod: GET
      Integration:
        Type: MOCK
        IntegrationResponses:
          - ResponseTemplates:
              application/json: !Sub >+
                #set (
                $root="https://${!context.apiId}.execute-api.${!stageVariables.Region}.amazonaws.com/${!context.stage}")


                {
                    "_links":{
                        "imports":{
                            "href":"$root/jobs/imports",
                            "bucket":"${ImportBucket}",
                            "uploadPrefix":"data/",
                            "statusPrefix":"Status/"
                        },
                        "exports":{
                            "href":"$root/jobs/exports"
                        },
                        "testall":{
                            "href":"$root/jobs/testall",
                            "bucket":"${TestAllBucket}",
                            "statusPrefix":"Status/"
                        }
                    }
                }

            StatusCode: "200"
        RequestTemplates:
          application/json: '{"statusCode": 200}'
      ResourceId: !Ref Jobs
      MethodResponses:
        - StatusCode: 200
      RestApiId: !Ref API
  testalls:
    Type: "AWS::ApiGateway::Resource"
    Properties:
      ParentId: !Ref Jobs
      PathPart: testall
      RestApiId: !Ref API
  testallsList:
    Type: "AWS::ApiGateway::Method"
    Properties:
      AuthorizationType: AWS_IAM
      HttpMethod: GET
      Integration:
        Type: AWS
        IntegrationHttpMethod: POST
        Uri: !Join
          - ""
          - - "arn:aws:apigateway:"
            - !Ref "AWS::Region"
            - ":lambda:path/2015-03-31/functions/"
            - !GetAtt
              - S3ListLambda
              - Arn
            - /invocations
        IntegrationResponses:
          - StatusCode: 200
          - SelectionPattern: ".*[InternalServiceError].*"
            StatusCode: 500
            ResponseTemplates:
              application/json: >+
                #set ($errorMessageObj =
                $util.parseJson($input.path('$.errorMessage')))



                {
                    "type":"$errorMessageObj.type",
                    "message":"$errorMessageObj.message",
                    "data":"$errorMessageObj.data"
                }

          - SelectionPattern: ".*[BadRequest].*"
            StatusCode: 400
            ResponseTemplates:
              application/json: >+
                #set ($errorMessageObj =
                $util.parseJson($input.path('$.errorMessage')))



                {
                    "type":"$errorMessageObj.type",
                    "message":"$errorMessageObj.message",
                    "data":"$errorMessageObj.data"
                }

          - SelectionPattern: ".*[Conflict].*"
            StatusCode: 409
            ResponseTemplates:
              application/json: >+
                #set ($errorMessageObj =
                $util.parseJson($input.path('$.errorMessage')))



                {
                    "type":"$errorMessageObj.type",
                    "message":"$errorMessageObj.message",
                    "data":"$errorMessageObj.data"
                }

          - SelectionPattern: ".*[NotFound].*"
            StatusCode: 404
            ResponseTemplates:
              application/json: >+
                #set ($errorMessageObj =
                $util.parseJson($input.path('$.errorMessage')))



                {
                    "type":"$errorMessageObj.type",
                    "message":"$errorMessageObj.message",
                    "data":"$errorMessageObj.data"
                }

        RequestTemplates:
          application/json: !Sub >+
            #set (
            $root="https://${!context.apiId}.execute-api.${!stageVariables.Region}.amazonaws.com/${!context.stage}")


            {
                "bucket":"${TestAllBucket}",
                "prefix":"status/",
                "perpage":"$input.params('perpage')",
                "token":"$input.params('token')",
                "type":"testall",
                "root":"$root"
            }

      RequestParameters:
        method.request.querystring.perpage: false
        method.request.querystring.token: false
      ResourceId: !Ref testalls
      MethodResponses:
        - StatusCode: 200
          ResponseParameters:
            method.response.header.date: true
        - StatusCode: 404
        - StatusCode: 500
      RestApiId: !Ref API
  testall:
    Type: "AWS::ApiGateway::Resource"
    Properties:
      ParentId: !Ref testalls
      PathPart: "{proxy+}"
      RestApiId: !Ref API
  testallPut:
    Type: "AWS::ApiGateway::Method"
    Properties:
      AuthorizationType: AWS_IAM
      HttpMethod: PUT
      Integration:
        Type: AWS
        IntegrationHttpMethod: PUT
        Credentials: !GetAtt
          - S3AccessRole
          - Arn
        Uri: !Join
          - ""
          - - "arn:aws:apigateway:"
            - !Ref "AWS::Region"
            - ":s3:path/"
            - !Ref TestAllBucket
            - "/status/{proxy}"
        RequestParameters:
          integration.request.path.proxy: method.request.path.proxy
        RequestTemplates:
          application/json: !Sub |+
            #set($inputRoot = $input.path('$'))

            {
                "bucket":"${TestAllBucket}",
                "index":"${Var.QnaIndex}",
                "id":"$input.params('proxy')",
                "config":"status/$input.params('proxy')",
                "tmp":"tmp/$input.params('proxy')",
                "key":"data/$input.params('proxy')",
                "filter":"$inputRoot.get('filter')",
                "status":"Started"
            }

        IntegrationResponses:
          - StatusCode: 200
            ResponseParameters:
              method.response.header.content-type: integration.response.header.Content-Type
          - StatusCode: 404
            ResponseTemplates:
              application/xml: '{"error":"Job not found"}'
            SelectionPattern: "403"
      RequestParameters:
        method.request.path.proxy: false
      ResourceId: !Ref testall
      MethodResponses:
        - StatusCode: 200
          ResponseParameters:
            method.response.header.content-type: false
        - StatusCode: 400
        - StatusCode: 404
      RestApiId: !Ref API
  testallGet:
    Type: "AWS::ApiGateway::Method"
    Properties:
      AuthorizationType: AWS_IAM
      HttpMethod: GET
      Integration:
        Type: AWS
        IntegrationHttpMethod: GET
        Credentials: !GetAtt
          - S3AccessRole
          - Arn
        Uri: !Join
          - ""
          - - "arn:aws:apigateway:"
            - !Ref "AWS::Region"
            - ":s3:path/"
            - !Ref TestAllBucket
            - "/status/{proxy}"
        RequestParameters:
          integration.request.path.proxy: method.request.path.proxy
        IntegrationResponses:
          - StatusCode: 200
            ResponseParameters:
              method.response.header.content-type: integration.response.header.Content-Type
          - StatusCode: 404
            ResponseTemplates:
              application/xml: '{"error":"Job not found"}'
            SelectionPattern: "403"
      RequestParameters:
        method.request.path.proxy: false
      ResourceId: !Ref testall
      MethodResponses:
        - StatusCode: 200
          ResponseParameters:
            method.response.header.content-type: false
        - StatusCode: 400
        - StatusCode: 404
      RestApiId: !Ref API
  testallDelete:
    Type: "AWS::ApiGateway::Method"
    Properties:
      AuthorizationType: AWS_IAM
      HttpMethod: DELETE
      Integration:
        Type: AWS
        IntegrationHttpMethod: DELETE
        Credentials: !GetAtt
          - S3AccessRole
          - Arn
        Uri: !Join
          - ""
          - - "arn:aws:apigateway:"
            - !Ref "AWS::Region"
            - ":s3:path/"
            - !Ref TestAllBucket
            - "/status/{proxy}"
        RequestParameters:
          integration.request.path.proxy: method.request.path.proxy
        IntegrationResponses:
          - StatusCode: 200
            ResponseParameters:
              method.response.header.content-type: integration.response.header.Content-Type
          - StatusCode: 404
            ResponseTemplates:
              application/xml: '{"error":"Job not found"}'
            SelectionPattern: "403"
      RequestParameters:
        method.request.path.proxy: false
      ResourceId: !Ref testall
      MethodResponses:
        - StatusCode: 200
          ResponseParameters:
            method.response.header.content-type: false
        - StatusCode: 400
        - StatusCode: 404
      RestApiId: !Ref API
  exports:
    Type: "AWS::ApiGateway::Resource"
    Properties:
      ParentId: !Ref Jobs
      PathPart: exports
      RestApiId: !Ref API
  exportsList:
    Type: "AWS::ApiGateway::Method"
    Properties:
      AuthorizationType: AWS_IAM
      HttpMethod: GET
      Integration:
        Type: AWS
        IntegrationHttpMethod: POST
        Uri: !Join
          - ""
          - - "arn:aws:apigateway:"
            - !Ref "AWS::Region"
            - ":lambda:path/2015-03-31/functions/"
            - !GetAtt
              - S3ListLambda
              - Arn
            - /invocations
        IntegrationResponses:
          - StatusCode: 200
          - SelectionPattern: ".*[InternalServiceError].*"
            StatusCode: 500
            ResponseTemplates:
              application/json: >+
                #set ($errorMessageObj =
                $util.parseJson($input.path('$.errorMessage')))



                {
                    "type":"$errorMessageObj.type",
                    "message":"$errorMessageObj.message",
                    "data":"$errorMessageObj.data"
                }

          - SelectionPattern: ".*[BadRequest].*"
            StatusCode: 400
            ResponseTemplates:
              application/json: >+
                #set ($errorMessageObj =
                $util.parseJson($input.path('$.errorMessage')))



                {
                    "type":"$errorMessageObj.type",
                    "message":"$errorMessageObj.message",
                    "data":"$errorMessageObj.data"
                }

          - SelectionPattern: ".*[Conflict].*"
            StatusCode: 409
            ResponseTemplates:
              application/json: >+
                #set ($errorMessageObj =
                $util.parseJson($input.path('$.errorMessage')))



                {
                    "type":"$errorMessageObj.type",
                    "message":"$errorMessageObj.message",
                    "data":"$errorMessageObj.data"
                }

          - SelectionPattern: ".*[NotFound].*"
            StatusCode: 404
            ResponseTemplates:
              application/json: >+
                #set ($errorMessageObj =
                $util.parseJson($input.path('$.errorMessage')))



                {
                    "type":"$errorMessageObj.type",
                    "message":"$errorMessageObj.message",
                    "data":"$errorMessageObj.data"
                }

        RequestTemplates:
          application/json: !Sub >+
            #set (
            $root="https://${!context.apiId}.execute-api.${!stageVariables.Region}.amazonaws.com/${!context.stage}")


            {
                "bucket":"${ExportBucket}",
                "prefix":"status/",
                "perpage":"$input.params('perpage')",
                "token":"$input.params('token')",
                "type":"exports",
                "root":"$root"
            }

      RequestParameters:
        method.request.querystring.perpage: false
        method.request.querystring.token: false
      ResourceId: !Ref exports
      MethodResponses:
        - StatusCode: 200
          ResponseParameters:
            method.response.header.date: true
        - StatusCode: 404
        - StatusCode: 500
      RestApiId: !Ref API
  export:
    Type: "AWS::ApiGateway::Resource"
    Properties:
      ParentId: !Ref exports
      PathPart: "{proxy+}"
      RestApiId: !Ref API
  imports:
    Type: "AWS::ApiGateway::Resource"
    Properties:
      ParentId: !Ref Jobs
      PathPart: imports
      RestApiId: !Ref API
  exportPut:
    Type: "AWS::ApiGateway::Method"
    Properties:
      AuthorizationType: AWS_IAM
      HttpMethod: PUT
      Integration:
        Type: AWS
        IntegrationHttpMethod: PUT
        Credentials: !GetAtt
          - S3AccessRole
          - Arn
        Uri: !Join
          - ""
          - - "arn:aws:apigateway:"
            - !Ref "AWS::Region"
            - ":s3:path/"
            - !Ref ExportBucket
            - "/status/{proxy}"
        RequestParameters:
          integration.request.path.proxy: method.request.path.proxy
        RequestTemplates:
          application/json: !Sub |-
            #set($inputRoot = $input.path('$'))

            {
                "bucket":"${ExportBucket}",
                "index":"${Var.QnaIndex}",
                "id":"$input.params('proxy')",
                "config":"status/$input.params('proxy')",
                "tmp":"tmp/$input.params('proxy')",
                "key":"$inputRoot.get('prefix')data/$input.params('proxy')",
                "filter":"$inputRoot.get('filter')",
                "status":"Started"
            }
        IntegrationResponses:
          - StatusCode: 200
            ResponseParameters:
              method.response.header.content-type: integration.response.header.Content-Type
          - StatusCode: 404
            ResponseTemplates:
              application/xml: '{"error":"Job not found"}'
            SelectionPattern: "403"
      RequestParameters:
        method.request.path.proxy: false
      ResourceId: !Ref export
      MethodResponses:
        - StatusCode: 200
          ResponseParameters:
            method.response.header.content-type: false
        - StatusCode: 400
        - StatusCode: 404
      RestApiId: !Ref API
  exportGet:
    Type: "AWS::ApiGateway::Method"
    Properties:
      AuthorizationType: AWS_IAM
      HttpMethod: GET
      Integration:
        Type: AWS
        IntegrationHttpMethod: GET
        Credentials: !GetAtt
          - S3AccessRole
          - Arn
        Uri: !Join
          - ""
          - - "arn:aws:apigateway:"
            - !Ref "AWS::Region"
            - ":s3:path/"
            - !Ref ExportBucket
            - "/status/{proxy}"
        RequestParameters:
          integration.request.path.proxy: method.request.path.proxy
        IntegrationResponses:
          - StatusCode: 200
            ResponseParameters:
              method.response.header.content-type: integration.response.header.Content-Type
          - StatusCode: 404
            ResponseTemplates:
              application/xml: '{"error":"Job not found"}'
            SelectionPattern: "403"
      RequestParameters:
        method.request.path.proxy: false
      ResourceId: !Ref export
      MethodResponses:
        - StatusCode: 200
          ResponseParameters:
            method.response.header.content-type: false
        - StatusCode: 400
        - StatusCode: 404
      RestApiId: !Ref API
  exportDelete:
    Type: "AWS::ApiGateway::Method"
    Properties:
      AuthorizationType: AWS_IAM
      HttpMethod: DELETE
      Integration:
        Type: AWS
        IntegrationHttpMethod: DELETE
        Credentials: !GetAtt
          - S3AccessRole
          - Arn
        Uri: !Join
          - ""
          - - "arn:aws:apigateway:"
            - !Ref "AWS::Region"
            - ":s3:path/"
            - !Ref ExportBucket
            - "/status/{proxy}"
        RequestParameters:
          integration.request.path.proxy: method.request.path.proxy
        IntegrationResponses:
          - StatusCode: 200
            ResponseParameters:
              method.response.header.content-type: integration.response.header.Content-Type
          - StatusCode: 404
            ResponseTemplates:
              application/xml: '{"error":"Job not found"}'
            SelectionPattern: "403"
      RequestParameters:
        method.request.path.proxy: false
      ResourceId: !Ref export
      MethodResponses:
        - StatusCode: 200
          ResponseParameters:
            method.response.header.content-type: false
        - StatusCode: 400
        - StatusCode: 404
      RestApiId: !Ref API
  importsList:
    Type: "AWS::ApiGateway::Method"
    Properties:
      AuthorizationType: AWS_IAM
      HttpMethod: GET
      Integration:
        Type: AWS
        IntegrationHttpMethod: POST
        Uri: !Join
          - ""
          - - "arn:aws:apigateway:"
            - !Ref "AWS::Region"
            - ":lambda:path/2015-03-31/functions/"
            - !GetAtt
              - S3ListLambda
              - Arn
            - /invocations
        IntegrationResponses:
          - StatusCode: 200
          - SelectionPattern: ".*[InternalServiceError].*"
            StatusCode: 500
            ResponseTemplates:
              application/json: >+
                #set ($errorMessageObj =
                $util.parseJson($input.path('$.errorMessage')))



                {
                    "type":"$errorMessageObj.type",
                    "message":"$errorMessageObj.message",
                    "data":"$errorMessageObj.data"
                }

          - SelectionPattern: ".*[BadRequest].*"
            StatusCode: 400
            ResponseTemplates:
              application/json: >+
                #set ($errorMessageObj =
                $util.parseJson($input.path('$.errorMessage')))



                {
                    "type":"$errorMessageObj.type",
                    "message":"$errorMessageObj.message",
                    "data":"$errorMessageObj.data"
                }

          - SelectionPattern: ".*[Conflict].*"
            StatusCode: 409
            ResponseTemplates:
              application/json: >+
                #set ($errorMessageObj =
                $util.parseJson($input.path('$.errorMessage')))



                {
                    "type":"$errorMessageObj.type",
                    "message":"$errorMessageObj.message",
                    "data":"$errorMessageObj.data"
                }

          - SelectionPattern: ".*[NotFound].*"
            StatusCode: 404
            ResponseTemplates:
              application/json: >+
                #set ($errorMessageObj =
                $util.parseJson($input.path('$.errorMessage')))



                {
                    "type":"$errorMessageObj.type",
                    "message":"$errorMessageObj.message",
                    "data":"$errorMessageObj.data"
                }

        RequestTemplates:
          application/json: !Sub >+
            #set (
            $root="https://${!context.apiId}.execute-api.${!stageVariables.Region}.amazonaws.com/${!context.stage}")


            {
                "bucket":"${ImportBucket}",
                "prefix":"status/",
                "perpage":"$input.params('perpage')",
                "token":"$input.params('token')",
                "type":"imports",
                "root":"$root"
            }

      RequestParameters:
        method.request.querystring.perpage: false
        method.request.querystring.token: false
      ResourceId: !Ref imports
      MethodResponses:
        - StatusCode: 200
          ResponseParameters:
            method.response.header.date: true
        - StatusCode: 404
        - StatusCode: 500
      RestApiId: !Ref API
  import:
    Type: "AWS::ApiGateway::Resource"
    Properties:
      ParentId: !Ref imports
      PathPart: "{proxy+}"
      RestApiId: !Ref API
  importGet:
    Type: "AWS::ApiGateway::Method"
    Properties:
      AuthorizationType: AWS_IAM
      HttpMethod: GET
      Integration:
        Type: AWS
        IntegrationHttpMethod: GET
        Credentials: !GetAtt
          - S3AccessRole
          - Arn
        Uri: !Join
          - ""
          - - "arn:aws:apigateway:"
            - !Ref "AWS::Region"
            - ":s3:path/"
            - !Ref ImportBucket
            - "/status/{proxy}"
        RequestParameters:
          integration.request.path.proxy: method.request.path.proxy
        IntegrationResponses:
          - StatusCode: 200
            ResponseParameters:
              method.response.header.content-type: integration.response.header.Content-Type
          - StatusCode: 404
            ResponseTemplates:
              application/xml: '{"error":"Job not found"}'
            SelectionPattern: "403"
      RequestParameters:
        method.request.path.proxy: false
      ResourceId: !Ref import
      MethodResponses:
        - StatusCode: 200
          ResponseParameters:
            method.response.header.content-type: false
        - StatusCode: 400
        - StatusCode: 404
      RestApiId: !Ref API
  importDelete:
    Type: "AWS::ApiGateway::Method"
    Properties:
      AuthorizationType: AWS_IAM
      HttpMethod: DELETE
      Integration:
        Type: AWS
        IntegrationHttpMethod: DELETE
        Credentials: !GetAtt
          - S3AccessRole
          - Arn
        Uri: !Join
          - ""
          - - "arn:aws:apigateway:"
            - !Ref "AWS::Region"
            - ":s3:path/"
            - !Ref ImportBucket
            - "/status/{proxy}"
        RequestParameters:
          integration.request.path.proxy: method.request.path.proxy
        IntegrationResponses:
          - StatusCode: 200
            ResponseParameters:
              method.response.header.content-type: integration.response.header.Content-Type
          - StatusCode: 404
            ResponseTemplates:
              application/xml: '{"error":"Job not found"}'
            SelectionPattern: "403"
      RequestParameters:
        method.request.path.proxy: false
      ResourceId: !Ref import
      MethodResponses:
        - StatusCode: 200
          ResponseParameters:
            method.response.header.content-type: false
        - StatusCode: 400
        - StatusCode: 404
      RestApiId: !Ref API
  S3ListLambda:
    Type: "AWS::Lambda::Function"
    Properties:
      Code:
        ZipFile: |+
          var aws=require('aws-sdk')
          aws.config.region=process.env.AWS_REGION
          var s3=new aws.S3()

          exports.handler = (event, context, callback) => {
              console.log('Received event:', JSON.stringify(event, null, 2));
             
              return s3.listObjects({
                  Bucket:event.bucket,
                  Prefix:event.prefix,
                  MaxKeys:event.perpage || 100,
                  Marker:event.token || null
              }).promise()
                  .then(x => {
                      if (x.Contents && Array.isArray(x.Contents)) {
                          x.Contents.sort((a, b) => {
                              if (a.LastModified && b.LastModified) {
                                  return new Date(b.LastModified).getTime() - new Date(a.LastModified).getTime();
                              } else {
                                  return 0;
                              }
                          })
                      }
                      callback(null, {
                      token:x.NextMarker,
                      jobs:x.Contents.map(y=>{return {
                          id:y.Key.split('/').pop(),
                          href:`${event.root}/jobs/${event.type}/`+encodeURI(y.Key.split('/').pop())
                      }})
                  })
              })
              .catch(e=>callback(JSON.stringify({
                  type:"[InternalServiceError]",
                  data:e
              })))
          }


      Handler: index.handler
      MemorySize: "128"
      Role: !GetAtt
        - S3ListLambdaRole
        - Arn
      Runtime: nodejs16.x
      Timeout: 300
      VpcConfig: !If
        - VPCEnabled
        - SubnetIds:
            - !Ref "AWS::NoValue"
          SecurityGroupIds:
            - !Ref "AWS::NoValue"
        - !Ref "AWS::NoValue"
      TracingConfig: !If
        - XRAYEnabled
        - Mode: Active
        - !Ref "AWS::NoValue"
      Tags:
        - Key: Type
          Value: Api
  S3ListLambdaRole:
    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"
        - "arn:aws:iam::aws:policy/AWSXRayDaemonWriteAccess"
      Policies:
        - PolicyName: S3ListPolicy
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Effect: Allow
                Action:
                  - "S3:List*"
                Resource:
                  - "*"
  Examples:
    Type: "AWS::ApiGateway::Resource"
    Properties:
      ParentId: !GetAtt
        - API
        - RootResourceId
      PathPart: examples
      RestApiId: !Ref API
  ExamplesGet:
    Type: "AWS::ApiGateway::Method"
    Properties:
      AuthorizationType: AWS_IAM
      HttpMethod: GET
      Integration:
        Type: MOCK
        IntegrationResponses:
          - ResponseTemplates:
              application/json: !Sub >+
                #set (
                $root="https://${!context.apiId}.execute-api.${AWS::Region}.amazonaws.com/${!context.stage}")


                {
                    "_links":{
                        "documents":{
                            "href":"$root/examples/documents"
                        },
                        "photos":{
                            "href":"$root/examples/photos"
                        }
                    }
                }

            StatusCode: "200"
        RequestTemplates:
          application/json: '{"statusCode": 200}'
      ResourceId: !Ref Examples
      MethodResponses:
        - StatusCode: 200
      RestApiId: !Ref API
  photos:
    Type: "AWS::ApiGateway::Resource"
    Properties:
      ParentId: !Ref Examples
      PathPart: photos
      RestApiId: !Ref API
  photosList:
    Type: "AWS::ApiGateway::Method"
    Properties:
      AuthorizationType: AWS_IAM
      HttpMethod: GET
      Integration:
        Type: AWS
        IntegrationHttpMethod: POST
        Uri: !Join
          - ""
          - - "arn:aws:apigateway:"
            - !Ref "AWS::Region"
            - ":lambda:path/2015-03-31/functions/"
            - !GetAtt
              - ExampleS3ListPhotoLambda
              - Arn
            - /invocations
        IntegrationResponses:
          - StatusCode: 200
          - SelectionPattern: ".*[InternalServiceError].*"
            StatusCode: 500
            ResponseTemplates:
              application/json: >+
                #set ($errorMessageObj =
                $util.parseJson($input.path('$.errorMessage')))



                {
                    "type":"$errorMessageObj.type",
                    "message":"$errorMessageObj.message",
                    "data":"$errorMessageObj.data"
                }

          - SelectionPattern: ".*[BadRequest].*"
            StatusCode: 400
            ResponseTemplates:
              application/json: >+
                #set ($errorMessageObj =
                $util.parseJson($input.path('$.errorMessage')))



                {
                    "type":"$errorMessageObj.type",
                    "message":"$errorMessageObj.message",
                    "data":"$errorMessageObj.data"
                }

          - SelectionPattern: ".*[Conflict].*"
            StatusCode: 409
            ResponseTemplates:
              application/json: >+
                #set ($errorMessageObj =
                $util.parseJson($input.path('$.errorMessage')))



                {
                    "type":"$errorMessageObj.type",
                    "message":"$errorMessageObj.message",
                    "data":"$errorMessageObj.data"
                }

          - SelectionPattern: ".*[NotFound].*"
            StatusCode: 404
            ResponseTemplates:
              application/json: >+
                #set ($errorMessageObj =
                $util.parseJson($input.path('$.errorMessage')))



                {
                    "type":"$errorMessageObj.type",
                    "message":"$errorMessageObj.message",
                    "data":"$errorMessageObj.data"
                }

        RequestTemplates:
          application/json: !Sub >+
            #set (
            $root="https://${!context.apiId}.execute-api.${AWS::Region}.amazonaws.com/${!context.stage}")


            {
                "bucket":"${AssetBucket}",
                "prefix":"examples/photos/",
                "perpage":"$input.params('perpage')",
                "token":"$input.params('token')",
                "root":"$root"
            }

      RequestParameters:
        method.request.querystring.perpage: false
        method.request.querystring.token: false
      ResourceId: !Ref photos
      MethodResponses:
        - StatusCode: 200
          ResponseParameters:
            method.response.header.date: true
        - StatusCode: 404
        - StatusCode: 500
      RestApiId: !Ref API
  photo:
    Type: "AWS::ApiGateway::Resource"
    Properties:
      ParentId: !Ref photos
      PathPart: "{proxy+}"
      RestApiId: !Ref API
  photoGet:
    Type: "AWS::ApiGateway::Method"
    Properties:
      AuthorizationType: AWS_IAM
      HttpMethod: GET
      Integration:
        Type: AWS
        IntegrationHttpMethod: GET
        Credentials: !GetAtt
          - S3AccessRole
          - Arn
        Uri: !Join
          - ""
          - - "arn:aws:apigateway:"
            - !Ref "AWS::Region"
            - ":s3:path/"
            - !Ref AssetBucket
            - "/examples/photos/{proxy}"
        RequestParameters:
          integration.request.path.proxy: method.request.path.proxy
        IntegrationResponses:
          - StatusCode: 200
            ResponseParameters:
              method.response.header.content-type: integration.response.header.Content-Type
          - StatusCode: 404
            ResponseTemplates:
              application/xml: '{"error":"Not Found"}'
            SelectionPattern: "403"
      RequestParameters:
        method.request.path.proxy: false
      ResourceId: !Ref photo
      MethodResponses:
        - StatusCode: 200
          ResponseParameters:
            method.response.header.content-type: false
        - StatusCode: 400
        - StatusCode: 404
      RestApiId: !Ref API
  Documents:
    Type: "AWS::ApiGateway::Resource"
    Properties:
      ParentId: !Ref Examples
      PathPart: documents
      RestApiId: !Ref API
  DocumentsList:
    Type: "AWS::ApiGateway::Method"
    Properties:
      AuthorizationType: AWS_IAM
      HttpMethod: GET
      Integration:
        Type: AWS
        IntegrationHttpMethod: POST
        Uri: !Join
          - ""
          - - "arn:aws:apigateway:"
            - !Ref "AWS::Region"
            - ":lambda:path/2015-03-31/functions/"
            - !GetAtt
              - ExampleS3ListLambda
              - Arn
            - /invocations
        IntegrationResponses:
          - StatusCode: 200
          - SelectionPattern: ".*[InternalServiceError].*"
            StatusCode: 500
            ResponseTemplates:
              application/json: >+
                #set ($errorMessageObj =
                $util.parseJson($input.path('$.errorMessage')))



                {
                    "type":"$errorMessageObj.type",
                    "message":"$errorMessageObj.message",
                    "data":"$errorMessageObj.data"
                }

          - SelectionPattern: ".*[BadRequest].*"
            StatusCode: 400
            ResponseTemplates:
              application/json: >+
                #set ($errorMessageObj =
                $util.parseJson($input.path('$.errorMessage')))



                {
                    "type":"$errorMessageObj.type",
                    "message":"$errorMessageObj.message",
                    "data":"$errorMessageObj.data"
                }

          - SelectionPattern: ".*[Conflict].*"
            StatusCode: 409
            ResponseTemplates:
              application/json: >+
                #set ($errorMessageObj =
                $util.parseJson($input.path('$.errorMessage')))



                {
                    "type":"$errorMessageObj.type",
                    "message":"$errorMessageObj.message",
                    "data":"$errorMessageObj.data"
                }

          - SelectionPattern: ".*[NotFound].*"
            StatusCode: 404
            ResponseTemplates:
              application/json: >+
                #set ($errorMessageObj =
                $util.parseJson($input.path('$.errorMessage')))



                {
                    "type":"$errorMessageObj.type",
                    "message":"$errorMessageObj.message",
                    "data":"$errorMessageObj.data"
                }

        RequestTemplates:
          application/json: !Sub >+
            #set (
            $root="https://${!context.apiId}.execute-api.${AWS::Region}.amazonaws.com/${!context.stage}")


            {
                "bucket":"${AssetBucket}",
                "prefix":"examples/documents/",
                "perpage":"$input.params('perpage')",
                "token":"$input.params('token')",
                "root":"$root"
            }

      RequestParameters:
        method.request.querystring.perpage: false
        method.request.querystring.token: false
      ResourceId: !Ref Documents
      MethodResponses:
        - StatusCode: 200
          ResponseParameters:
            method.response.header.date: true
        - StatusCode: 404
        - StatusCode: 500
      RestApiId: !Ref API
  Example:
    Type: "AWS::ApiGateway::Resource"
    Properties:
      ParentId: !Ref Documents
      PathPart: "{proxy+}"
      RestApiId: !Ref API
  ExampleGet:
    Type: "AWS::ApiGateway::Method"
    Properties:
      AuthorizationType: AWS_IAM
      HttpMethod: GET
      Integration:
        Type: AWS
        IntegrationHttpMethod: GET
        Credentials: !GetAtt
          - S3AccessRole
          - Arn
        Uri: !Join
          - ""
          - - "arn:aws:apigateway:"
            - !Ref "AWS::Region"
            - ":s3:path/"
            - !Ref AssetBucket
            - "/examples/documents/{proxy}"
        RequestParameters:
          integration.request.path.proxy: method.request.path.proxy
        IntegrationResponses:
          - StatusCode: 200
            ResponseParameters:
              method.response.header.content-type: integration.response.header.Content-Type
          - StatusCode: 404
            ResponseTemplates:
              application/xml: '{"error":"Not Found"}'
            SelectionPattern: "403"
      RequestParameters:
        method.request.path.proxy: false
      ResourceId: !Ref Example
      MethodResponses:
        - StatusCode: 200
          ResponseParameters:
            method.response.header.content-type: false
        - StatusCode: 400
        - StatusCode: 404
      RestApiId: !Ref API
  ExampleHead:
    Type: "AWS::ApiGateway::Method"
    Properties:
      AuthorizationType: AWS_IAM
      HttpMethod: HEAD
      Integration:
        Type: AWS
        IntegrationHttpMethod: HEAD
        Credentials: !GetAtt
          - S3AccessRole
          - Arn
        Uri: !Join
          - ""
          - - "arn:aws:apigateway:"
            - !Ref "AWS::Region"
            - ":s3:path/"
            - !Ref AssetBucket
            - "/examples/documents/{proxy}"
        RequestParameters:
          integration.request.path.proxy: method.request.path.proxy
        IntegrationResponses:
          - StatusCode: 200
            ResponseParameters:
              method.response.header.content-type: integration.response.header.Content-Type
          - StatusCode: 404
            ResponseTemplates:
              application/xml: '{"error":"Not Found"}'
            SelectionPattern: "403"
      RequestParameters:
        method.request.path.proxy: false
      ResourceId: !Ref Example
      MethodResponses:
        - StatusCode: 200
          ResponseParameters:
            method.response.header.content-type: false
        - StatusCode: 400
        - StatusCode: 404
      RestApiId: !Ref API
  ExampleS3ListLambda:
    Type: "AWS::Lambda::Function"
    Properties:
      Code:
        ZipFile: |+
          var aws=require('aws-sdk')
          aws.config.region=process.env.AWS_REGION
          var s3=new aws.S3()

          exports.photos = (event, context, callback) => {
              console.log('Received event:', JSON.stringify(event, null, 2));
              
              return s3.listObjects({
                  Bucket:event.bucket,
                  Prefix:event.prefix,
                  MaxKeys:event.perpage || 100,
                  Marker:event.token || null
              }).promise()
              .then(x=>{
                  console.log("s3 response:",JSON.stringify(x,null,2))
                  var photos=x.Contents.map(function(value){
                      var key=value.Key.split('/').pop()
                      return `${event.root}/examples/photos/${key}`
                  },[])
                  callback(null,{
                      token:x.NextMarker,
                      photos
                  })
              })
              .catch(e=>callback(JSON.stringify({
                  type:"[InternalServiceError]",
                  data:e
              })))
          }
          exports.documents = (event, context, callback) => {
              console.log('Received event:', JSON.stringify(event, null, 2));
             
              return s3.listObjects({
                  Bucket:event.bucket,
                  Prefix:event.prefix,
                  MaxKeys:event.perpage || 100,
                  Marker:event.token || null
              }).promise()
              .then(x=>{
                  console.log("s3 response:",JSON.stringify(x,null,2))
                  var examples=x.Contents.reduce(function(accum,value){
                      var key=value.Key.split('/').pop().split('.')
                      var ext=key.length >1 ? key.pop() : 'txt'
                      key=key[0]
                      var href=`${event.root}/examples/documents/${key}.${ext}`
                      if(!accum[key]){
                          accum[key]={id:key}
                      }
                      if(ext==='json'){
                          accum[key].document={href}
                      }else{
                          accum[key].description={href}
                      }
                      return accum
                  },[])
                  
                  callback(null,{
                      token:x.NextMarker,
                      examples:Object.keys(examples).map(x=>examples[x])
                  })
              })
              .catch(e=>callback(JSON.stringify({
                  type:"[InternalServiceError]",
                  data:e
              })))
          }


      Handler: index.documents
      MemorySize: "128"
      Role: !GetAtt
        - S3ListLambdaRole
        - Arn
      Runtime: nodejs16.x
      Timeout: 300
      VpcConfig: !If
        - VPCEnabled
        - SubnetIds:
            - !Ref "AWS::NoValue"
          SecurityGroupIds:
            - !Ref "AWS::NoValue"
        - !Ref "AWS::NoValue"
      TracingConfig: !If
        - XRAYEnabled
        - Mode: Active
        - !Ref "AWS::NoValue"
      Tags:
        - Key: Type
          Value: Api
  ExampleS3ListPhotoLambda:
    Type: "AWS::Lambda::Function"
    Properties:
      Code:
        ZipFile: |+
          var aws=require('aws-sdk')
          aws.config.region=process.env.AWS_REGION
          var s3=new aws.S3()

          exports.photos = (event, context, callback) => {
              console.log('Received event:', JSON.stringify(event, null, 2));
              
              return s3.listObjects({
                  Bucket:event.bucket,
                  Prefix:event.prefix,
                  MaxKeys:event.perpage || 100,
                  Marker:event.token || null
              }).promise()
              .then(x=>{
                  console.log("s3 response:",JSON.stringify(x,null,2))
                  var photos=x.Contents.map(function(value){
                      var key=value.Key.split('/').pop()
                      return `${event.root}/examples/photos/${key}`
                  },[])
                  callback(null,{
                      token:x.NextMarker,
                      photos
                  })
              })
              .catch(e=>callback(JSON.stringify({
                  type:"[InternalServiceError]",
                  data:e
              })))
          }
          exports.documents = (event, context, callback) => {
              console.log('Received event:', JSON.stringify(event, null, 2));
             
              return s3.listObjects({
                  Bucket:event.bucket,
                  Prefix:event.prefix,
                  MaxKeys:event.perpage || 100,
                  Marker:event.token || null
              }).promise()
              .then(x=>{
                  console.log("s3 response:",JSON.stringify(x,null,2))
                  var examples=x.Contents.reduce(function(accum,value){
                      var key=value.Key.split('/').pop().split('.')
                      var ext=key.length >1 ? key.pop() : 'txt'
                      key=key[0]
                      var href=`${event.root}/examples/documents/${key}.${ext}`
                      if(!accum[key]){
                          accum[key]={id:key}
                      }
                      if(ext==='json'){
                          accum[key].document={href}
                      }else{
                          accum[key].description={href}
                      }
                      return accum
                  },[])
                  
                  callback(null,{
                      token:x.NextMarker,
                      examples:Object.keys(examples).map(x=>examples[x])
                  })
              })
              .catch(e=>callback(JSON.stringify({
                  type:"[InternalServiceError]",
                  data:e
              })))
          }


      Handler: index.photos
      MemorySize: "128"
      Role: !GetAtt
        - S3ListLambdaRole
        - Arn
      Runtime: nodejs16.x
      Timeout: 300
      VpcConfig: !If
        - VPCEnabled
        - SubnetIds:
            - !Ref "AWS::NoValue"
          SecurityGroupIds:
            - !Ref "AWS::NoValue"
        - !Ref "AWS::NoValue"
      TracingConfig: !If
        - XRAYEnabled
        - Mode: Active
        - !Ref "AWS::NoValue"
      Tags:
        - Key: Type
          Value: Api
  Services:
    Type: "AWS::ApiGateway::Resource"
    Properties:
      ParentId: !GetAtt
        - API
        - RootResourceId
      PathPart: services
      RestApiId: !Ref API
  ServicesGet:
    Type: "AWS::ApiGateway::Method"
    Properties:
      AuthorizationType: AWS_IAM
      HttpMethod: GET
      Integration:
        Type: MOCK
        IntegrationResponses:
          - ResponseTemplates:
              application/json: !Sub |+
                {
                    "elasticsearch":{
                        "qid":"${ESQidLambda.Arn}",
                        "proxy":"${ESProxyLambda.Arn}"
                    }
                }

            StatusCode: "200"
        RequestTemplates:
          application/json: '{"statusCode": 200}'
      ResourceId: !Ref Services
      MethodResponses:
        - StatusCode: 200
      RestApiId: !Ref API
  Images:
    Type: "AWS::ApiGateway::Resource"
    Properties:
      ParentId: !GetAtt
        - API
        - RootResourceId
      PathPart: images
      RestApiId: !Ref API
  ImagesProxy:
    Type: "AWS::ApiGateway::Resource"
    Properties:
      ParentId: !Ref Images
      PathPart: "{proxy+}"
      RestApiId: !Ref API
  ImagesProxyGet:
    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
            - "/assets/{proxy}"
        RequestParameters:
          integration.request.path.proxy: method.request.path.proxy
        IntegrationResponses:
          - StatusCode: 200
            ContentHandling: CONVERT_TO_BINARY
            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 ImagesProxy
      MethodResponses:
        - StatusCode: 200
          ResponseParameters:
            method.response.header.content-type: false
            method.response.header.api-stage: false
        - StatusCode: 400
        - StatusCode: 404
      RestApiId: !Ref API
  Bucket:
    Type: "AWS::S3::Bucket"
    DeletionPolicy: Delete
    Properties:
      WebsiteConfiguration:
        IndexDocument: index.html
      BucketEncryption: !If
        - Encrypted
        - ServerSideEncryptionConfiguration:
            - ServerSideEncryptionByDefault:
                SSEAlgorithm: AES256
        - !Ref "AWS::NoValue"
  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: !Join
        - ""
        - - qnabot-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:::${ImportBucket}/*"
                  - !Sub "arn:aws:s3:::${ExportBucket}/*"
                  - !Sub "arn:aws:s3:::${TestAllBucket}/*"
                  - !Sub "arn:aws:s3:::${Bucket}/*"
                  - !Sub "arn:aws:s3:::${AssetBucket}/*"
              - Effect: Allow
                Action:
                  - "s3:PutObject"
                Resource:
                  - !Sub "arn:aws:s3:::${ExportBucket}/*"
                  - !Sub "arn:aws:s3:::${TestAllBucket}/*"
              - Effect: Allow
                Action:
                  - "s3:DeleteObject"
                Resource:
                  - !Sub "arn:aws:s3:::${ImportBucket}/*"
                  - !Sub "arn:aws:s3:::${ExportBucket}/*"
                  - !Sub "arn:aws:s3:::${TestAllBucket}/*"
  SchemaLambdaCodeVersion:
    Type: "Custom::S3Version"
    Properties:
      ServiceToken: !GetAtt
        - CFNLambda
        - Arn
      Bucket: aws-bigdata-blog-replica-ap-northeast-1
      Key: !Sub artifacts/aws-ai-qna-bot/lambda/schema.zip
      BuildDate: "2021-06-06T14:08:00.271Z"
  SchemaLambda:
    Type: "AWS::Lambda::Function"
    Properties:
      Code:
        S3Bucket: aws-bigdata-blog-replica-ap-northeast-1
        S3Key: !Sub artifacts/aws-ai-qna-bot/lambda/schema.zip
        S3ObjectVersion: !Ref SchemaLambdaCodeVersion
      Handler: index.handler
      MemorySize: "128"
      Role: !GetAtt
        - SchemaLambdaRole
        - Arn
      Runtime: nodejs16.x
      Timeout: 300
      VpcConfig: !If
        - VPCEnabled
        - SubnetIds:
            - !Ref "AWS::NoValue"
          SecurityGroupIds:
            - !Ref "AWS::NoValue"
        - !Ref "AWS::NoValue"
      TracingConfig: !If
        - XRAYEnabled
        - Mode: Active
        - !Ref "AWS::NoValue"
      Tags:
        - Key: Type
          Value: Api
  SchemaLambdaRole:
    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"
        - "arn:aws:iam::aws:policy/AWSXRayDaemonWriteAccess"
        - !Ref QueryPolicy
  SignupPermision:
    Type: "AWS::Lambda::Permission"
    Properties:
      Action: "lambda:InvokeFunction"
      FunctionName: !GetAtt
        - SignupLambda
        - Arn
      Principal: cognito-idp.amazonaws.com
      SourceArn: !GetAtt
        - UserPool
        - Arn
  MessagePermision:
    Type: "AWS::Lambda::Permission"
    Properties:
      Action: "lambda:InvokeFunction"
      FunctionName: !GetAtt
        - MessageLambda
        - Arn
      Principal: cognito-idp.amazonaws.com
      SourceArn: !GetAtt
        - UserPool
        - Arn
  MessageLambda:
    Type: "AWS::Lambda::Function"
    Properties:
      Code:
        ZipFile: |+
          exports.handler = (event, context, callback) => {
              console.log('Received event:', JSON.stringify(event, null, 2));
              var approvedDomain = process.env.APPROVED_DOMAIN;

              if(approvedDomain){
                  var regex=new RegExp(`^[A-Za-z0-9._%+-]+@${approvedDomain}$`)
                  if (event.request.userAttributes.email.match(regex)) {
                      event.response.emailSubject = subject;
                      event.response.emailMessage = message(
                          event.request.codeParameter,
                          event.request.usernameParameter
                      );
                      context.done(null, event);
                  } else {
                      var error = new Error('EMAIL_DOMAIN_DENIED_ERR');
                      context.done(error, event);
                  }
              }else{
                  event.response.emailSubject = subject;
                  event.response.emailMessage = message(event.request.codeParameter);
                  context.done(null, event);
              }
          };

          var subject="QnABot Signup Verification Code";
          function message(code,name){
              return `Hello, Your QnABot verification code is: ${code}`
          }

      Handler: index.handler
      MemorySize: "128"
      Environment:
        Variables:
          APPROVED_DOMAIN: !If
            - Domain
            - EMPTY
            - !Ref "AWS::NoValue"
      Role: !GetAtt
        - SignupLambdaRole
        - Arn
      Runtime: nodejs16.x
      Timeout: 300
      VpcConfig: !If
        - VPCEnabled
        - SubnetIds:
            - !Ref "AWS::NoValue"
          SecurityGroupIds:
            - !Ref "AWS::NoValue"
        - !Ref "AWS::NoValue"
      TracingConfig: !If
        - XRAYEnabled
        - Mode: Active
        - !Ref "AWS::NoValue"
      Tags:
        - Key: Type
          Value: Cognito
  SignupLambda:
    Type: "AWS::Lambda::Function"
    Properties:
      Code:
        ZipFile: |+
          exports.handler = (event, context, callback) => {
              console.log('Received event:', JSON.stringify(event, null, 2));
              var approvedDomain = process.env.APPROVED_DOMAIN;
              
              if(approvedDomain){
                  var regex=new RegExp(`^[A-Za-z0-9._%+-]+@${approvedDomain}$`)
                  if (event.request.userAttributes.email.match(regex)) {
                      context.done(null, event);
                  }else{
                      var error = new Error('EMAIL_DOMAIN_DENIED_ERR');
                      context.done(error, event);
                  }
              }else{
                  context.done(null,event) 
              }
          };

      Handler: index.handler
      MemorySize: "128"
      Environment:
        Variables:
          APPROVED_DOMAIN: !If
            - Domain
            - EMPTY
            - !Ref "AWS::NoValue"
      Role: !GetAtt
        - SignupLambdaRole
        - Arn
      Runtime: nodejs16.x
      Timeout: 300
      VpcConfig: !If
        - VPCEnabled
        - SubnetIds:
            - !Ref "AWS::NoValue"
          SecurityGroupIds:
            - !Ref "AWS::NoValue"
        - !Ref "AWS::NoValue"
      TracingConfig: !If
        - XRAYEnabled
        - Mode: Active
        - !Ref "AWS::NoValue"
      Tags:
        - Key: Type
          Value: Cognito
  SignupLambdaRole:
    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"
        - "arn:aws:iam::aws:policy/AWSXRayDaemonWriteAccess"
  TestAllStack:
    Type: "AWS::CloudFormation::Stack"
    Properties:
      TemplateURL: !Sub >-
        https://lexhandson-202106.s3.ap-northeast-1.amazonaws.com/qnabot-app/testall.json
      Parameters:
        CFNLambda: !GetAtt
          - CFNLambda
          - Arn
        CFNInvokePolicy: !Ref CFNInvokePolicy
        LexV2BotId: !GetAtt
          - LexV2Bot
          - botId
        LexV2BotAliasId: !GetAtt
          - LexV2Bot
          - botAliasId
        BootstrapBucket: aws-bigdata-blog-replica-ap-northeast-1
        BootstrapPrefix: artifacts/aws-ai-qna-bot
        VarIndex: !GetAtt
          - Var
          - QnaIndex
        EsEndpoint: !GetAtt
          - ESVar
          - ESAddress
        EsProxyLambda: !GetAtt
          - ESProxyLambda
          - Arn
        TestAllBucket: !Ref TestAllBucket
        VPCSubnetIdList: !Join
          - ","
          - - !Ref "AWS::NoValue"
        VPCSecurityGroupIdList: !Join
          - ","
          - - !Ref "AWS::NoValue"
        XraySetting: !Ref XraySetting
  Var:
    Type: "Custom::Variable"
    Properties:
      ServiceToken: !GetAtt
        - CFNLambda
        - Arn
      index:
        value: !Ref "AWS::StackName"
        op: toLowerCase
      QnAType: qna
      QuizType: quiz
      QnaIndex:
        value: !Sub "${AWS::StackName}"
        op: toLowerCase
      MetricsIndex:
        value: !Sub "${AWS::StackName}-metrics"
        op: toLowerCase
      FeedbackIndex:
        value: !Sub "${AWS::StackName}-feedback"
        op: toLowerCase
  InfoVar:
    Type: "Custom::Variable"
    Properties:
      ServiceToken: !GetAtt
        - CFNLambda
        - Arn
      Version: 4.7.0
      BuildDateString: "Sun Jun 06 2021 14:08:00 GMT+0000 (Coordinated Universal Time)"
      BuildDate: "2021-06-06T14:08:00.272Z"
  ESVar:
    Type: "Custom::Variable"
    Properties:
      ServiceToken: !GetAtt
        - CFNLambda
        - Arn
      ESArn: !If
        - CreateDomain
        - !GetAtt
          - ElasticsearchDomain
          - DomainArn
        - !GetAtt
          - ESInfo
          - Arn
      ESAddress: !If
        - CreateDomain
        - !GetAtt
          - ElasticsearchDomain
          - DomainEndpoint
        - !GetAtt
          - ESInfo
          - Endpoint
      ESDomain: !If
        - CreateDomain
        - !Ref ElasticsearchDomain
        - EMPTY
  ApiUrl:
    Type: "Custom::Variable"
    Properties:
      ServiceToken: !GetAtt
        - CFNLambda
        - Arn
      Name: !Join
        - ""
        - - "https://"
          - !Ref API
          - .execute-api.
          - !Ref "AWS::Region"
          - .amazonaws.com/prod
  Urls:
    Type: "Custom::Variable"
    Properties:
      ServiceToken: !GetAtt
        - CFNLambda
        - Arn
      Designer: !Join
        - ""
        - - !GetAtt
            - ApiUrl
            - Name
          - /static/index.html
      Client: !Join
        - ""
        - - !GetAtt
            - ApiUrl
            - Name
          - /static/client.html
      Kibana: !Sub "${ESVar.ESAddress}/_plugin/kibana/"
  API:
    Type: "AWS::ApiGateway::RestApi"
    Properties:
      Name: !Ref "AWS::StackName"
      Description: An Api interface for the admin actions on the QNA bot
      BinaryMediaTypes:
        - image/png
  ApiCompression:
    Type: "Custom::ApiCompression"
    Properties:
      ServiceToken: !GetAtt
        - CFNLambda
        - Arn
      restApiId: !Ref API
      value: "500000"
  Deployment:
    Type: "Custom::ApiDeployment"
    Properties:
      ServiceToken: !GetAtt
        - CFNLambda
        - Arn
      restApiId: !Ref API
      buildDate: "2021-06-06T14:08:00.276Z"
      stage: prod
      Encryption: !Ref Encryption
    DependsOn:
      - AlexaSchema
      - BotPost
      - BotGet
      - HealthGet
      - rootGet
      - QuestionsGet
      - QuestionsDelete
      - QuestionHead
      - QuestionPut
      - QuestionsOptions
      - QuestionDelete
      - ProxyAnyGet
      - ProxyAnyHead
      - DesignerLoginResourceGet
      - ClientLoginResourceGet
      - JobsGet
      - testallsList
      - testallPut
      - testallGet
      - testallDelete
      - exportsList
      - exportPut
      - exportGet
      - exportDelete
      - importsList
      - importGet
      - importDelete
      - ExamplesGet
      - photosList
      - photoGet
      - DocumentsList
      - ExampleGet
      - ExampleHead
      - ServicesGet
      - ImagesProxyGet
      - InvokePermissionLexBuildLambdaStart
      - InvokePermissionLexv2BotLambda
      - InvokePermissionUtteranceLambda
      - InvokePermissionESQidLambda
      - InvokePermissionESCleaningLambda
      - InvokePermissionESProxyLambda
      - InvokePermissionLexProxyLambda
      - InvokePermissionS3ListLambda
      - InvokePermissionExampleS3ListLambda
      - InvokePermissionExampleS3ListPhotoLambda
      - InvokePermissionSchemaLambda
  Stage:
    Type: "AWS::ApiGateway::Stage"
    Properties:
      DeploymentId: !Ref Deployment
      RestApiId: !Ref API
      StageName: prod
      MethodSettings:
        - DataTraceEnabled: true
          HttpMethod: "*"
          LoggingLevel: INFO
          ResourcePath: /*
      Variables:
        Id: QnABot
        Region: !Ref "AWS::Region"
        CognitoEndpoint: !GetAtt
          - DesignerLogin
          - Domain
        DesignerLoginUrl: !Join
          - ""
          - - !GetAtt
              - ApiUrl
              - Name
            - /pages/designer
        ClientLoginUrl: !If
          - Public
          - !GetAtt
            - Urls
            - Client
          - !Join
            - ""
            - - !GetAtt
                - ApiUrl
                - Name
              - /pages/client
  ApiGatewayAccount:
    Type: "AWS::ApiGateway::Account"
    Properties:
      CloudWatchRoleArn: !GetAtt
        - ApiGatewayCloudWatchLogsRole
        - Arn
  DocumentationVersion:
    Type: "AWS::ApiGateway::DocumentationVersion"
    DependsOn:
      - BotDoc
    Properties:
      Description: ""
      DocumentationVersion: "1.0"
      RestApiId: !Ref API
  InvokePermissionLexBuildLambda:
    Type: "AWS::Lambda::Permission"
    Properties:
      Action: "lambda:InvokeFunction"
      FunctionName: !GetAtt
        - LexBuildLambda
        - Arn
      Principal: apigateway.amazonaws.com
  InvokePermissionLexBuildLambdaStart:
    Type: "AWS::Lambda::Permission"
    Properties:
      Action: "lambda:InvokeFunction"
      FunctionName: !GetAtt
        - LexBuildLambdaStart
        - Arn
      Principal: apigateway.amazonaws.com
  InvokePermissionLexBuildLambdaPoll:
    Type: "AWS::Lambda::Permission"
    Properties:
      Action: "lambda:InvokeFunction"
      FunctionName: !GetAtt
        - LexBuildLambdaPoll
        - Arn
      Principal: apigateway.amazonaws.com
  InvokePermissionLexv2BotLambda:
    Type: "AWS::Lambda::Permission"
    Properties:
      Action: "lambda:InvokeFunction"
      FunctionName: !GetAtt
        - Lexv2BotLambda
        - Arn
      Principal: apigateway.amazonaws.com
  InvokePermissionUtteranceLambda:
    Type: "AWS::Lambda::Permission"
    Properties:
      Action: "lambda:InvokeFunction"
      FunctionName: !GetAtt
        - UtteranceLambda
        - Arn
      Principal: apigateway.amazonaws.com
  InvokePermissionESQidLambda:
    Type: "AWS::Lambda::Permission"
    Properties:
      Action: "lambda:InvokeFunction"
      FunctionName: !GetAtt
        - ESQidLambda
        - Arn
      Principal: apigateway.amazonaws.com
  InvokePermissionESCleaningLambda:
    Type: "AWS::Lambda::Permission"
    Properties:
      Action: "lambda:InvokeFunction"
      FunctionName: !GetAtt
        - ESCleaningLambda
        - Arn
      Principal: apigateway.amazonaws.com
  InvokePermissionESProxyLambda:
    Type: "AWS::Lambda::Permission"
    Properties:
      Action: "lambda:InvokeFunction"
      FunctionName: !GetAtt
        - ESProxyLambda
        - Arn
      Principal: apigateway.amazonaws.com
  InvokePermissionLexProxyLambda:
    Type: "AWS::Lambda::Permission"
    Properties:
      Action: "lambda:InvokeFunction"
      FunctionName: !GetAtt
        - LexProxyLambda
        - Arn
      Principal: apigateway.amazonaws.com
  InvokePermissionLexStatusLambda:
    Type: "AWS::Lambda::Permission"
    Properties:
      Action: "lambda:InvokeFunction"
      FunctionName: !GetAtt
        - LexStatusLambda
        - Arn
      Principal: apigateway.amazonaws.com
  InvokePermissionS3ListLambda:
    Type: "AWS::Lambda::Permission"
    Properties:
      Action: "lambda:InvokeFunction"
      FunctionName: !GetAtt
        - S3ListLambda
        - Arn
      Principal: apigateway.amazonaws.com
  InvokePermissionExampleS3ListLambda:
    Type: "AWS::Lambda::Permission"
    Properties:
      Action: "lambda:InvokeFunction"
      FunctionName: !GetAtt
        - ExampleS3ListLambda
        - Arn
      Principal: apigateway.amazonaws.com
  InvokePermissionExampleS3ListPhotoLambda:
    Type: "AWS::Lambda::Permission"
    Properties:
      Action: "lambda:InvokeFunction"
      FunctionName: !GetAtt
        - ExampleS3ListPhotoLambda
        - Arn
      Principal: apigateway.amazonaws.com
  InvokePermissionSchemaLambda:
    Type: "AWS::Lambda::Permission"
    Properties:
      Action: "lambda:InvokeFunction"
      FunctionName: !GetAtt
        - SchemaLambda
        - Arn
      Principal: apigateway.amazonaws.com
  LambdaAccessRole:
    Type: "AWS::IAM::Role"
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service: apigateway.amazonaws.com
            Action: "sts:AssumeRole"
      Path: /
      ManagedPolicyArns:
        - "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
        - "arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole"
        - "arn:aws:iam::aws:policy/AWSXRayDaemonWriteAccess"
      Policies:
        - PolicyName: LambdaPolicy
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Effect: Allow
                Action:
                  - "lambda:*"
                Resource:
                  - "*"
  dashboard:
    Type: "AWS::CloudWatch::Dashboard"
    Properties:
      DashboardName: !Sub "${AWS::Region}-${AWS::StackName}"
      DashboardBody: !Sub >-
        {"widgets":[{"type":"text","width":24,"height":2,"x":0,"y":0,"properties":{"markdown":"#
        QnABot:${AWS::StackName}
        Dashboard"}},{"type":"text","width":24,"height":2,"x":0,"y":3,"properties":{"markdown":"##
        ElasticSearch"}},{"type":"metric","width":6,"height":6,"properties":{"view":"timeSeries","stacked":false,"metrics":[["AWS/ES","ReadLatency","DomainName","${ESVar.ESDomain}","ClientId","${AWS::AccountId}"]],"region":"${AWS::Region}"},"x":0,"y":5},{"type":"metric","width":6,"height":6,"properties":{"view":"timeSeries","stacked":false,"metrics":[["AWS/ES","ReadIOPS","DomainName","${ESVar.ESDomain}","ClientId","${AWS::AccountId}"],[".","ReadThroughput",".",".",".",".",{"yAxis":"right"}]],"region":"${AWS::Region}"},"x":6,"y":5},{"type":"metric","width":6,"height":6,"properties":{"view":"timeSeries","stacked":false,"metrics":[["AWS/ES","CPUUtilization","DomainName","${ESVar.ESDomain}","ClientId","${AWS::AccountId}"]],"region":"${AWS::Region}"},"x":12,"y":5},{"type":"metric","x":18,"y":5,"properties":{"view":"timeSeries","stacked":false,"metrics":[["AWS/ES","ClusterUsedSpace","DomainName","${ESVar.ESDomain}","ClientId","${AWS::AccountId}"],[".","SearchableDocuments",".",".",".",".",{"yAxis":"right"}]],"region":"${AWS::Region}"},"height":6,"width":6},{"type":"metric","width":6,"height":6,"properties":{"view":"timeSeries","stacked":false,"metrics":[["AWS/ES","ClusterStatus.green","DomainName","${ESVar.ESDomain}","ClientId","${AWS::AccountId}",{"color":"#2ca02c"}],[".","ClusterStatus.red",".",".",".",".",{"color":"#d62728"}],[".","ClusterStatus.yellow",".",".",".",".",{"color":"#bcbd22"}]],"region":"${AWS::Region}"},"x":0,"y":11},{"type":"text","width":24,"height":2,"x":0,"y":24,"properties":{"markdown":"##
        Lambda
        Function"}},{"type":"text","width":24,"height":2,"x":0,"y":26,"properties":{"markdown":"###
        CustomResource"}},{"type":"metric","properties":{"view":"timeSeries","stacked":false,"metrics":[["AWS/Lambda","Errors","FunctionName","${VersionLambda}",{"stat":"Sum"}],[".","Invocations",".",".",{"stat":"Sum"}],[".","Duration",".",".",{"yAxis":"right"}],[".","Throttles",".",".",{"stat":"Sum"}]],"region":"${AWS::Region}","title":"VersionLambda","period":300},"height":6,"width":6,"x":0,"y":28},{"type":"metric","properties":{"view":"timeSeries","stacked":false,"metrics":[["AWS/Lambda","Errors","FunctionName","${CFNLambda}",{"stat":"Sum"}],[".","Invocations",".",".",{"stat":"Sum"}],[".","Duration",".",".",{"yAxis":"right"}],[".","Throttles",".",".",{"stat":"Sum"}]],"region":"${AWS::Region}","title":"CFNLambda","period":300},"height":6,"width":6,"x":6,"y":28},{"type":"metric","properties":{"view":"timeSeries","stacked":false,"metrics":[["AWS/Lambda","Errors","FunctionName","${ESCFNProxyLambda}",{"stat":"Sum"}],[".","Invocations",".",".",{"stat":"Sum"}],[".","Duration",".",".",{"yAxis":"right"}],[".","Throttles",".",".",{"stat":"Sum"}]],"region":"${AWS::Region}","title":"ESCFNProxyLambda","period":300},"height":6,"width":6,"x":12,"y":28},{"type":"text","width":24,"height":2,"x":0,"y":34,"properties":{"markdown":"###
        Fulfillment"}},{"type":"metric","properties":{"view":"timeSeries","stacked":false,"metrics":[["AWS/Lambda","Errors","FunctionName","${FulfillmentLambda}",{"stat":"Sum"}],[".","Invocations",".",".",{"stat":"Sum"}],[".","Duration",".",".",{"yAxis":"right"}],[".","Throttles",".",".",{"stat":"Sum"}]],"region":"${AWS::Region}","title":"FulfillmentLambda","period":300},"height":6,"width":6,"x":0,"y":36},{"type":"text","width":24,"height":2,"x":0,"y":42,"properties":{"markdown":"###
        Warmer"}},{"type":"metric","properties":{"view":"timeSeries","stacked":false,"metrics":[["AWS/Lambda","Errors","FunctionName","${ESWarmerLambda}",{"stat":"Sum"}],[".","Invocations",".",".",{"stat":"Sum"}],[".","Duration",".",".",{"yAxis":"right"}],[".","Throttles",".",".",{"stat":"Sum"}]],"region":"${AWS::Region}","title":"ESWarmerLambda","period":300},"height":6,"width":6,"x":0,"y":44},{"type":"text","width":24,"height":2,"x":0,"y":50,"properties":{"markdown":"###
        Api"}},{"type":"metric","properties":{"view":"timeSeries","stacked":false,"metrics":[["AWS/Lambda","Errors","FunctionName","${LexBuildLambda}",{"stat":"Sum"}],[".","Invocations",".",".",{"stat":"Sum"}],[".","Duration",".",".",{"yAxis":"right"}],[".","Throttles",".",".",{"stat":"Sum"}]],"region":"${AWS::Region}","title":"LexBuildLambda","period":300},"height":6,"width":6,"x":0,"y":52},{"type":"metric","properties":{"view":"timeSeries","stacked":false,"metrics":[["AWS/Lambda","Errors","FunctionName","${LexBuildLambdaStart}",{"stat":"Sum"}],[".","Invocations",".",".",{"stat":"Sum"}],[".","Duration",".",".",{"yAxis":"right"}],[".","Throttles",".",".",{"stat":"Sum"}]],"region":"${AWS::Region}","title":"LexBuildLambdaStart","period":300},"height":6,"width":6,"x":6,"y":52},{"type":"metric","properties":{"view":"timeSeries","stacked":false,"metrics":[["AWS/Lambda","Errors","FunctionName","${LexBuildLambdaPoll}",{"stat":"Sum"}],[".","Invocations",".",".",{"stat":"Sum"}],[".","Duration",".",".",{"yAxis":"right"}],[".","Throttles",".",".",{"stat":"Sum"}]],"region":"${AWS::Region}","title":"LexBuildLambdaPoll","period":300},"height":6,"width":6,"x":12,"y":52},{"type":"metric","properties":{"view":"timeSeries","stacked":false,"metrics":[["AWS/Lambda","Errors","FunctionName","${Lexv2BotLambda}",{"stat":"Sum"}],[".","Invocations",".",".",{"stat":"Sum"}],[".","Duration",".",".",{"yAxis":"right"}],[".","Throttles",".",".",{"stat":"Sum"}]],"region":"${AWS::Region}","title":"Lexv2BotLambda","period":300},"height":6,"width":6,"x":18,"y":52},{"type":"metric","properties":{"view":"timeSeries","stacked":false,"metrics":[["AWS/Lambda","Errors","FunctionName","${LexProxyLambda}",{"stat":"Sum"}],[".","Invocations",".",".",{"stat":"Sum"}],[".","Duration",".",".",{"yAxis":"right"}],[".","Throttles",".",".",{"stat":"Sum"}]],"region":"${AWS::Region}","title":"LexProxyLambda","period":300},"height":6,"width":6,"x":0,"y":58},{"type":"metric","properties":{"view":"timeSeries","stacked":false,"metrics":[["AWS/Lambda","Errors","FunctionName","${LexStatusLambda}",{"stat":"Sum"}],[".","Invocations",".",".",{"stat":"Sum"}],[".","Duration",".",".",{"yAxis":"right"}],[".","Throttles",".",".",{"stat":"Sum"}]],"region":"${AWS::Region}","title":"LexStatusLambda","period":300},"height":6,"width":6,"x":6,"y":58},{"type":"metric","properties":{"view":"timeSeries","stacked":false,"metrics":[["AWS/Lambda","Errors","FunctionName","${S3ListLambda}",{"stat":"Sum"}],[".","Invocations",".",".",{"stat":"Sum"}],[".","Duration",".",".",{"yAxis":"right"}],[".","Throttles",".",".",{"stat":"Sum"}]],"region":"${AWS::Region}","title":"S3ListLambda","period":300},"height":6,"width":6,"x":12,"y":58},{"type":"metric","properties":{"view":"timeSeries","stacked":false,"metrics":[["AWS/Lambda","Errors","FunctionName","${ExampleS3ListLambda}",{"stat":"Sum"}],[".","Invocations",".",".",{"stat":"Sum"}],[".","Duration",".",".",{"yAxis":"right"}],[".","Throttles",".",".",{"stat":"Sum"}]],"region":"${AWS::Region}","title":"ExampleS3ListLambda","period":300},"height":6,"width":6,"x":18,"y":58},{"type":"metric","properties":{"view":"timeSeries","stacked":false,"metrics":[["AWS/Lambda","Errors","FunctionName","${ExampleS3ListPhotoLambda}",{"stat":"Sum"}],[".","Invocations",".",".",{"stat":"Sum"}],[".","Duration",".",".",{"yAxis":"right"}],[".","Throttles",".",".",{"stat":"Sum"}]],"region":"${AWS::Region}","title":"ExampleS3ListPhotoLambda","period":300},"height":6,"width":6,"x":0,"y":64},{"type":"metric","properties":{"view":"timeSeries","stacked":false,"metrics":[["AWS/Lambda","Errors","FunctionName","${SchemaLambda}",{"stat":"Sum"}],[".","Invocations",".",".",{"stat":"Sum"}],[".","Duration",".",".",{"yAxis":"right"}],[".","Throttles",".",".",{"stat":"Sum"}]],"region":"${AWS::Region}","title":"SchemaLambda","period":300},"height":6,"width":6,"x":6,"y":64},{"type":"text","width":24,"height":2,"x":0,"y":70,"properties":{"markdown":"###
        Service"}},{"type":"metric","properties":{"view":"timeSeries","stacked":false,"metrics":[["AWS/Lambda","Errors","FunctionName","${UtteranceLambda}",{"stat":"Sum"}],[".","Invocations",".",".",{"stat":"Sum"}],[".","Duration",".",".",{"yAxis":"right"}],[".","Throttles",".",".",{"stat":"Sum"}]],"region":"${AWS::Region}","title":"UtteranceLambda","period":300},"height":6,"width":6,"x":0,"y":72},{"type":"metric","properties":{"view":"timeSeries","stacked":false,"metrics":[["AWS/Lambda","Errors","FunctionName","${ESQidLambda}",{"stat":"Sum"}],[".","Invocations",".",".",{"stat":"Sum"}],[".","Duration",".",".",{"yAxis":"right"}],[".","Throttles",".",".",{"stat":"Sum"}]],"region":"${AWS::Region}","title":"ESQidLambda","period":300},"height":6,"width":6,"x":6,"y":72},{"type":"metric","properties":{"view":"timeSeries","stacked":false,"metrics":[["AWS/Lambda","Errors","FunctionName","${ESCleaningLambda}",{"stat":"Sum"}],[".","Invocations",".",".",{"stat":"Sum"}],[".","Duration",".",".",{"yAxis":"right"}],[".","Throttles",".",".",{"stat":"Sum"}]],"region":"${AWS::Region}","title":"ESCleaningLambda","period":300},"height":6,"width":6,"x":12,"y":72},{"type":"metric","properties":{"view":"timeSeries","stacked":false,"metrics":[["AWS/Lambda","Errors","FunctionName","${ESProxyLambda}",{"stat":"Sum"}],[".","Invocations",".",".",{"stat":"Sum"}],[".","Duration",".",".",{"yAxis":"right"}],[".","Throttles",".",".",{"stat":"Sum"}]],"region":"${AWS::Region}","title":"ESProxyLambda","period":300},"height":6,"width":6,"x":18,"y":72},{"type":"text","width":24,"height":2,"x":0,"y":78,"properties":{"markdown":"###
        Logging"}},{"type":"metric","properties":{"view":"timeSeries","stacked":false,"metrics":[["AWS/Lambda","Errors","FunctionName","${ESLoggingLambda}",{"stat":"Sum"}],[".","Invocations",".",".",{"stat":"Sum"}],[".","Duration",".",".",{"yAxis":"right"}],[".","Throttles",".",".",{"stat":"Sum"}]],"region":"${AWS::Region}","title":"ESLoggingLambda","period":300},"height":6,"width":6,"x":0,"y":80},{"type":"text","width":24,"height":2,"x":0,"y":86,"properties":{"markdown":"###
        Query"}},{"type":"metric","properties":{"view":"timeSeries","stacked":false,"metrics":[["AWS/Lambda","Errors","FunctionName","${ESQueryLambda}",{"stat":"Sum"}],[".","Invocations",".",".",{"stat":"Sum"}],[".","Duration",".",".",{"yAxis":"right"}],[".","Throttles",".",".",{"stat":"Sum"}]],"region":"${AWS::Region}","title":"ESQueryLambda","period":300},"height":6,"width":6,"x":0,"y":88},{"type":"text","width":24,"height":2,"x":0,"y":94,"properties":{"markdown":"###
        Cognito"}},{"type":"metric","properties":{"view":"timeSeries","stacked":false,"metrics":[["AWS/Lambda","Errors","FunctionName","${MessageLambda}",{"stat":"Sum"}],[".","Invocations",".",".",{"stat":"Sum"}],[".","Duration",".",".",{"yAxis":"right"}],[".","Throttles",".",".",{"stat":"Sum"}]],"region":"${AWS::Region}","title":"MessageLambda","period":300},"height":6,"width":6,"x":0,"y":96},{"type":"metric","properties":{"view":"timeSeries","stacked":false,"metrics":[["AWS/Lambda","Errors","FunctionName","${SignupLambda}",{"stat":"Sum"}],[".","Invocations",".",".",{"stat":"Sum"}],[".","Duration",".",".",{"yAxis":"right"}],[".","Throttles",".",".",{"stat":"Sum"}]],"region":"${AWS::Region}","title":"SignupLambda","period":300},"height":6,"width":6,"x":6,"y":96}]}
Conditions:
  Public: !Equals
    - !Ref PublicOrPrivate
    - PUBLIC
  Encrypted: !Equals
    - !Ref Encryption
    - ENCRYPTED
  AdminSignUp: !Equals
    - true
    - true
  XRAYEnabled: !Equals
    - !Ref XraySetting
    - "TRUE"
  Domain: !Equals
    - true
    - false
  BuildExamples: !Equals
    - true
    - true
  CreateDomain: !Equals
    - true
    - true
  DontCreateDomain: !Equals
    - true
    - false
  CreateLexV1Bots: !Equals
    - !Ref LexBotVersion
    - LexV1 and LexV2
  VPCEnabled: !Equals
    - true
    - false
AWSTemplateFormatVersion: 2010-09-09
Description: QnABot with admin and client websites - (Master v4.7.0)
Mappings: {}
Outputs:
  ContentDesignerURL:
    Value: !Join
      - ""
      - - !GetAtt
          - ApiUrl
          - Name
        - /pages/designer
  ClientURL:
    Value: !If
      - Public
      - !GetAtt
        - Urls
        - Client
      - !Join
        - ""
        - - !GetAtt
            - ApiUrl
            - Name
          - /pages/client
  DashboardURL:
    Value: !Join
      - ""
      - - "https://console.aws.amazon.com/cloudwatch/home?"
        - region=
        - !Ref "AWS::Region"
        - "#dashboards:name="
        - !Ref dashboard
  UserPoolURL:
    Value: !Join
      - ""
      - - "https://console.aws.amazon.com/cognito/users/"
        - "?region="
        - !Ref "AWS::Region"
        - "#/pool/"
        - !Ref UserPool
        - /details
  LexV1BotName:
    Condition: CreateLexV1Bots
    Value: !Ref LexBot
  LexV1BotAlias:
    Condition: CreateLexV1Bots
    Value: !Ref VersionAlias
  LexV1Intent:
    Condition: CreateLexV1Bots
    Value: !Ref Intent
  LexV1IntentFallback:
    Condition: CreateLexV1Bots
    Value: !Ref IntentFallback
  LexV2BotName:
    Value: !GetAtt
      - LexV2Bot
      - botName
  LexV2BotId:
    Value: !GetAtt
      - LexV2Bot
      - botId
  LexV2BotAlias:
    Value: !GetAtt
      - LexV2Bot
      - botAlias
  LexV2BotAliasId:
    Value: !GetAtt
      - LexV2Bot
      - botAliasId
  LexV2Intent:
    Value: !GetAtt
      - LexV2Bot
      - botIntent
  LexV2IntentFallback:
    Value: !GetAtt
      - LexV2Bot
      - botIntentFallback
  LexV2BotLocaleIds:
    Value: !GetAtt
      - LexV2Bot
      - botLocaleIds
  FeedbackSNSTopic:
    Value: !GetAtt
      - ExamplesStack
      - Outputs.FeedbackSNSTopic
Parameters:
  Email:
    Type: String
    Description: >-
      Required: Email address for the admin user. Will be used for logging in
      and for setting the admin password. This email will receive the temporary
      password for the admin user.
    AllowedPattern: .+\@.+\..+
    ConstraintDescription: Must be valid email address eg. johndoe@example.com
  Username:
    Type: String
    Description: Administrator username
    Default: Admin
  DefaultKendraIndexId:
    Type: String
    Description: >-
      Optional: Index ID of an existing Kendra index, used as the default index
      for QnABot's Kendra integration. You can use the QnABot Content Designer
      to reconfigure Kendra Index ID settings at any time.
    Default: ""
  Encryption:
    Type: String
    Description: >-
      Enables encryption at rest for S3 and ElasticSearch, and provisions
      c5.large.elasticsearch instances - recommended for production deployments.
      Selecting the unencrypted configuration provisions lower cost
      t3.small.elasticsearch instances. See
      https://aws.amazon.com/elasticsearch-service/pricing/.
    AllowedValues:
      - ENCRYPTED
      - UNENCRYPTED
    Default: ENCRYPTED
    ConstraintDescription: Allowed Values are UNENCRYPTED or ENCRYPTED
  ElasticSearchNodeCount:
    Type: String
    Description: >-
      Number of nodes in ElasticSearch domain - '4' is recommended for fault
      tolerant production deployments.
    AllowedValues:
      - "2"
      - "4"
    Default: "4"
  KibanaDashboardRetentionMinutes:
    Type: Number
    Description: >-
      To conserve storage in Amazon ElasticSearch, metrics and feedback data
      used to populate the Kibana dashboard are automatically deleted after this
      period (default 43200 minutes = 30 days). Monitor 'Free storage space' for
      your ElasticSearch domain to ensure that you have sufficient space
      available to store data for the desired retention period.
    Default: 43200
  PublicOrPrivate:
    Type: String
    Description: >-
      Choose whether access to the QnABot client should be publicly available or
      restricted to users in QnABot UserPool.
    AllowedValues:
      - PUBLIC
      - PRIVATE
    Default: PUBLIC
  LexV2BotLocaleIds:
    Description: >-
      Languages for QnABot voice interaction using LexV2. Specify as a comma
      separated list of valid Locale IDs - see
      https://docs.aws.amazon.com/lexv2/latest/dg/how-languages.html
    Type: String
    Default: "ja_JP"
  LexBotVersion:
    Description: >-
      Lex versions to use for QnABot. Select 'LexV2 Only' to install QnABot in
      AWS reqions where LexV1 is not supported.
    Type: String
    AllowedValues:
      - LexV1 and LexV2
      - LexV2 Only
    Default: LexV2 Only
  XraySetting:
    Type: String
    Description: Configure Lambdas with X-Ray enabled
    AllowedValues:
      - "FALSE"
      - "TRUE"
    Default: "FALSE"
    ConstraintDescription: Allowed Values are FALSE or TRUE
Metadata:
  "AWS::CloudFormation::Interface":
    ParameterGroups:
      - Label:
          default: Authentication
        Parameters:
          - Email
          - Username
          - PublicOrPrivate
      - Label:
          default: Amazon Kendra Integration
        Parameters:
          - DefaultKendraIndexId
      - Label:
          default: Amazon ElasticSearch
        Parameters:
          - ElasticSearchNodeCount
          - Encryption
          - KibanaDashboardRetentionMinutes
      - Label:
          default: Amazon LexV2
        Parameters:
          - LexV2BotLocaleIds
