Creating the Lambda function

Now we have the IAM role with two IAM policies attached, create the Lambda function itself. Here, we are creating a function from scratch, as we want to walk through the full details to deepen your understanding of what is involved in creating a serverless stack. The following diagram shows data API architecture involving CloudWatch, DynamoDB, IAM, and Lambda:

Perform the following steps:

  1. Sign in to the AWS Management Console and open the AWS Lambda console at https://console.aws.amazon.com/lambda/.
  2. Choose Create function or, in the AWS Lambda navigation pane, choose Functions and choose Create function.
  1. On the Create function page, choose Author from scratch taking the following steps:
    1. For Name, type lambda-dynamo-data-api
    2. In Runtime, choose Python 3.7
    3. In Role, leave Choose an existing role
    4. In Existing Role, choose lambda-dynamo-data-api
  1. Under the Function Code, a new file called lambda_function.py has been created. Copy and paste the following code under the lambda_function tab, overwriting the existing code:
      import json
import decimal

from boto3 import resource
from boto3.dynamodb.conditions import Key

class HttpUtils:
def init(self):
pass

@staticmethod def parse_parameters(event): try: return_parameters =
event['queryStringParameters'].copy() except Exception: return_parameters = {} try: resource_id = event.get('path', '').split('/')[-1] if resource_id.isdigit(): return_parameters['resource_id'] = resource_id else: return {"parsedParams": None, "err": Exception("resource_id not a number")} except Exception as e: return {"parsedParams": None, "err": e}
# Generally bad idea to expose exceptions return {"parsedParams": return_parameters, "err": None} @staticmethod def respond(err=None, err_code=400, res=None): return { 'statusCode': str(err_code) if err else '200', 'body': '{"message":%s}' % json.dumps(str(err))
if err else json.dumps(res, cls=DecimalEncoder), 'headers': { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' }, } @staticmethod def parse_body(event): try: return {"body": json.loads(event['body']),
"err": None} except Exception as e: return {"body": None, "err": e}

class DecimalEncoder(json.JSONEncoder): def default(self, o):
if isinstance(o, decimal.Decimal): if o % 1 > 0:
return float(o)
else: return int(o) return super(DecimalEncoder,
self).default(o)

class DynamoRepository: def init(self, table_name):
self.dynamo_client = resource(service_name='dynamodb',
region_name='eu-west-1') self.table_name =
table_name self.db_table =
self.dynamo_client.Table(table_name)

def query_by_partition_and_sort_key(self,
partition_key, partition_value, sort_key, sort_value): response = self.db_table.query(KeyConditionExpression=
Key(partition_key).eq(partition_value) & Key(sort_key).gte(sort_value)) return response.get('Items') def query_by_partition_key(self, partition_key,
partition_value): response = self.db_table.query(KeyConditionExpression=
Key(partition_key).eq(partition_value)) return response.get('Items')

def print_exception(e): try: print('Exception %s type' %
str(type(e))) print('Exception message: %s '
% str(e)) except Exception: pass

class Controller(): def init(self): pass

@staticmethod def get_dynamodb_records(event): try: validation_result = HttpUtils.parse_parameters(event) if validation_result.get(
'parsedParams', None) is None: return HttpUtils.respond(
err=validation_result['err'], err_code=404) resource_id = str(validation_result['parsedParams']
["resource_id"]) if validation_result['parsedParams']
.get("startDate") is None: result = repo.query_by_partition_key(
partition_key="EventId",
partition_value=resource_id) else: start_date = int(validation_result['parsedParams']
["startDate"]) result = repo.query_by_partition_and_sort_key(
partition_key="EventId",
partition_value=resource_id, sort_key="EventDay",
sort_value=start_date) return HttpUtils.respond(res=result) except Exception as e: print_exception(e) return HttpUtils.respond(err=Exception('Not found'),
err_code=404)

table_name = 'user-visits' repo =
DynamoRepository(table_name=table_name)

def lambda_handler(event, context): response =
Controller.get_dynamodb_records(event) return response
  1. Choose Save.

A Lambda function always has a handler (https://docs.aws.amazon.com/lambda/latest/dg/python-programming-model-handler-types.html) and the main idea event is passed in, which contains the event source data. Here, this will be an API Gateway GET request. The other parameter is the context (https://docs.aws.amazon.com/lambda/latest/dg/python-context-object.html), which gives you details such as memory or the time-to-live for the Lambda.

You will recognize class DynamoRepository() from the earlier example, which deals with connection and queries. The new HttpUtils class is a set of utility methods for parsing the query string and body, and returning a response. The other new Controller() class controls the main flow. Here, it assumes the API Gateway request is a GET method, so it call functions to parse the request, query DynamoDB, and return the response to API Gateway.

Exception flows are built defensively, so all exceptions are caught (generally, best practice is to only catch specific named exceptions and raise the rest) rather than raised. This is because we want the API to be able to return a 4XX or 5XX in the event of exceptions. For debugging purposes, we are returning the exception too. In a production environment, you would not return the exception, only log it, as it could expose vulnerabilities in the code. As an analogy, you might remember seeing those SQL Server errors with a source error and full stack trace in yellow on a white background, from a poorly secured web application in your browser.

I recommend that you develop your Lambda code locally, but AWS has recently acquired an online IDE editor called Cloud9, which is where you have pasted the Python code, as shown in the following screenshot:

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset