DEV Community

Discussion on: The Guide to implement Geo Search in your React Native App with AWS Amplify

Collapse
 
rosswilliams profile image
rosswilliams

The best way to add the ES mapping is to use a cloudformation custom resource lambda, you can write an inline lambda function that makes the right API calls when the template is first deployed, if you make the ES domain a dependency of the custom resource Lambda then it will always run after the ES domain has been setup.

Collapse
 
rpostulart profile image
rpostulart

Ok nice, I was not aware of that. Do you have some more info / referencence documentation about this?

Collapse
 
rosswilliams profile image
rosswilliams

there are some gists floating around to adapt, most look like this block of JSON for the cloudformation custom resources file. You likely want to lock down the permission a bit more.

{
  "ConfigureESCustom": {
    "Type": "Custom::ConfigureES",
    "Properties": {
      "ServiceToken": {
        "Fn::GetAtt": [
          "ConfigureES",
          "Arn"
        ]
      }
    }
  },
  "ConfigureES": {
    "Type": "AWS::Lambda::Function",
    "Properties": {
      "Environment": {
        "Variables": {
          "ES_ENDPOINT": {
            "Fn::ImportValue": {
              "Fn::Join": [
                ":",
                [
                  {
                    "Ref": "AppSyncApiId"
                  },
                  "GetAtt",
                  "Elasticsearch",
                  "DomainEndpoint"
                ]
              ]
            }
          },
          "ES_REGION": {
            "Ref": "AWS::Region"
          }
        }
      },
      "Code": {
            "ZipFile": "import base64
    import json
    import logging
    import string
    import boto3
    import os
    import time
    import datetime
    import traceback
    from urllib.parse import urlparse, quote
    from botocore.vendored import requests
    from botocore.auth import SigV4Auth
    from botocore.awsrequest import AWSRequest
    from botocore.credentials import get_credentials
    from botocore.endpoint import BotocoreHTTPSession
    from botocore.session import Session
    import cfnresponse

    logger = logging.getLogger()
    logger.setLevel(logging.INFO)

    # The following parameters are required to configure the ES cluster
    ES_ENDPOINT = os.environ['ES_ENDPOINT']
    ES_REGION = os.environ['ES_REGION']
    DEBUG = True if os.environ['DEBUG'] is not None else False

    def es_put(payload, region, creds, host, path, method='PUT', proto='https://'):
    '''Put index data to ES endpoint with SigV4 signed http headers'''
    req = AWSRequest(method=method, url=proto + host +
        quote(path), data=payload, headers={'Host': host, 'Content-Type': 'application/json'})
    SigV4Auth(creds, 'es', region).add_auth(req)
    http_session = BotocoreHTTPSession()
    res = http_session.send(req.prepare())
    return res._content

    def lambda_handler(event, context):
    logger.info('got event {}'.format(event))

    if event['RequestType'] == 'Create':
        # Get aws_region and credentials to post signed URL to ES
        es_region = ES_REGION or os.environ['AWS_REGION']
        session = Session({'region': es_region})
        creds = get_credentials(session)
        es_url = urlparse(ES_ENDPOINT)
        # Extract the domain name in ES_ENDPOINT
        es_endpoint = es_url.netloc or es_url.path
        es_put('', es_region, creds,
        es_endpoint, '/s12doctor')
        es_actions = []
        es_actions.append('')  # Add one empty line to force final \

        es_payload = '\
    '.join(es_actions)
        action = {\"properties\": {\"location\": {\"type\": \"geo_point\"}, \"coordinates\": {\"type\": \"geo_point\"}}}
        es_actions.append(json.dumps(action))
    es_actions.append('')  # Add one empty line to force final \

    es_payload = '\
'.join(es_actions)
    es_put(es_payload, es_region, creds,
      es_endpoint, '/INDEX_NAME_HERE/_mapping/doc')

  cfnresponse.send(event, context, cfnresponse.SUCCESS, {})
"
      },
      "FunctionName": {
        "Fn::Join": [
          "-",
          [
            "ConfigureES",
            {
              "Ref": "env"
            }
          ]
        ]
      },
      "Handler": "index.lambda_handler",
      "Timeout": 30,
      "Role": {
        "Fn::GetAtt": [
          "LambdaRole",
          "Arn"
        ]
      },
      "Runtime": "python3.6",
      "Layers": [
        "arn:aws:lambda:eu-west-2:142628438157:layer:AWSLambda-Python-AWS-SDK:1"
      ]
    }
  },
  "LambdaRole": {
    "Type": "AWS::IAM::Role",
    "Properties": {
      "AssumeRolePolicyDocument": {
        "Version": "2012-10-17",
        "Statement": [
          {
            "Effect": "Allow",
            "Principal": {
              "Service": [
                "lambda.amazonaws.com"
              ]
            },
            "Action": [
              "sts:AssumeRole"
            ]
          }
        ]
      },
      "Path": "/",
      "Policies": [
        {
          "PolicyName": "lambda-logs",
          "PolicyDocument": {
            "Version": "2012-10-17",
            "Statement": [
              {
                "Effect": "Allow",
                "Action": [
                  "logs:CreateLogGroup",
                  "logs:CreateLogStream",
                  "logs:PutLogEvents"
                ],
                "Resource": [
                  "arn:aws:logs:*:*:*"
                ]
              }
            ]
          }
        },
        {
          "PolicyName": "ES",
          "PolicyDocument": {
            "Version": "2012-10-17",
            "Statement": [
              {
                "Effect": "Allow",
                "Action": [
                  "es:*"
                ],
                "Resource": {
                  "Fn::Join": [
                    "",
                    [
                      {
                        "Fn::ImportValue": {
                          "Fn::Join": [
                            ":",
                            [
                              {
                                "Ref": "AppSyncApiId"
                              },
                              "GetAtt",
                              "Elasticsearch",
                              "DomainArn"
                            ]
                          ]
                        }
                      },
                      "/*"
                    ]
                  ]
                }
              }
            ]
          }
        }
      ]
    }
  }
}
Thread Thread
 
rpostulart profile image
rpostulart

Thx, will look into it and update this post accordingly