using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Threading.Tasks; using Amazon.Lambda.Core; using Amazon.Lambda.APIGatewayEvents; using Amazon; using Amazon.DynamoDBv2; using Amazon.DynamoDBv2.DataModel; using Newtonsoft.Json; // Assembly attribute to enable the Lambda function's JSON input to be converted into a .NET class. [assembly: LambdaSerializer(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))] namespace BlueprintBaseName._1 { public class Functions { // This const is the name of the environment variable that the serverless.template will use to set // the name of the DynamoDB table used to store blog posts. const string TABLENAME_ENVIRONMENT_VARIABLE_LOOKUP = "BlogTable"; public const string ID_QUERY_STRING_NAME = "Id"; IDynamoDBContext DDBContext { get; set; } /// /// Default constructor that Lambda will invoke. /// public Functions() { // Check to see if a table name was passed in through environment variables and if so // add the table mapping. var tableName = System.Environment.GetEnvironmentVariable(TABLENAME_ENVIRONMENT_VARIABLE_LOOKUP); if(!string.IsNullOrEmpty(tableName)) { AWSConfigsDynamoDB.Context.TypeMappings[typeof(Blog)] = new Amazon.Util.TypeMapping(typeof(Blog), tableName); } var config = new DynamoDBContextConfig { Conversion = DynamoDBEntryConversion.V2 }; this.DDBContext = new DynamoDBContext(new AmazonDynamoDBClient(), config); } /// /// Constructor used for testing passing in a preconfigured DynamoDB client. /// /// /// public Functions(IAmazonDynamoDB ddbClient, string tableName) { if (!string.IsNullOrEmpty(tableName)) { AWSConfigsDynamoDB.Context.TypeMappings[typeof(Blog)] = new Amazon.Util.TypeMapping(typeof(Blog), tableName); } var config = new DynamoDBContextConfig { Conversion = DynamoDBEntryConversion.V2 }; this.DDBContext = new DynamoDBContext(ddbClient, config); } /// /// A Lambda function that returns back a page worth of blog posts. /// /// /// The list of blogs public async Task GetBlogsAsync(APIGatewayProxyRequest request, ILambdaContext context) { context.Logger.LogLine("Getting blogs"); var search = this.DDBContext.ScanAsync(null); var page = await search.GetNextSetAsync(); context.Logger.LogLine($"Found {page.Count} blogs"); var response = new APIGatewayProxyResponse { StatusCode = (int)HttpStatusCode.OK, Body = JsonConvert.SerializeObject(page), Headers = new Dictionary { { "Content-Type", "application/json" } } }; return response; } /// /// A Lambda function that returns the blog identified by blogId /// /// /// public async Task GetBlogAsync(APIGatewayProxyRequest request, ILambdaContext context) { string blogId = null; if (request.PathParameters != null && request.PathParameters.ContainsKey(ID_QUERY_STRING_NAME)) blogId = request.PathParameters[ID_QUERY_STRING_NAME]; else if (request.QueryStringParameters != null && request.QueryStringParameters.ContainsKey(ID_QUERY_STRING_NAME)) blogId = request.QueryStringParameters[ID_QUERY_STRING_NAME]; if (string.IsNullOrEmpty(blogId)) { return new APIGatewayProxyResponse { StatusCode = (int)HttpStatusCode.BadRequest, Body = $"Missing required parameter {ID_QUERY_STRING_NAME}" }; } context.Logger.LogLine($"Getting blog {blogId}"); var blog = await DDBContext.LoadAsync(blogId); context.Logger.LogLine($"Found blog: {blog != null}"); if (blog == null) { return new APIGatewayProxyResponse { StatusCode = (int)HttpStatusCode.NotFound }; } var response = new APIGatewayProxyResponse { StatusCode = (int)HttpStatusCode.OK, Body = JsonConvert.SerializeObject(blog), Headers = new Dictionary { { "Content-Type", "application/json" } } }; return response; } /// /// A Lambda function that adds a blog post. /// /// /// public async Task AddBlogAsync(APIGatewayProxyRequest request, ILambdaContext context) { var blog = JsonConvert.DeserializeObject(request?.Body); blog.Id = Guid.NewGuid().ToString(); blog.CreatedTimestamp = DateTime.Now; context.Logger.LogLine($"Saving blog with id {blog.Id}"); await DDBContext.SaveAsync(blog); var response = new APIGatewayProxyResponse { StatusCode = (int)HttpStatusCode.OK, Body = blog.Id.ToString(), Headers = new Dictionary { { "Content-Type", "text/plain" } } }; return response; } /// /// A Lambda function that removes a blog post from the DynamoDB table. /// /// public async Task RemoveBlogAsync(APIGatewayProxyRequest request, ILambdaContext context) { string blogId = null; if (request.PathParameters != null && request.PathParameters.ContainsKey(ID_QUERY_STRING_NAME)) blogId = request.PathParameters[ID_QUERY_STRING_NAME]; else if (request.QueryStringParameters != null && request.QueryStringParameters.ContainsKey(ID_QUERY_STRING_NAME)) blogId = request.QueryStringParameters[ID_QUERY_STRING_NAME]; if (string.IsNullOrEmpty(blogId)) { return new APIGatewayProxyResponse { StatusCode = (int)HttpStatusCode.BadRequest, Body = $"Missing required parameter {ID_QUERY_STRING_NAME}" }; } context.Logger.LogLine($"Deleting blog with id {blogId}"); await this.DDBContext.DeleteAsync(blogId); return new APIGatewayProxyResponse { StatusCode = (int)HttpStatusCode.OK }; } } }