Skip to content

Commit

Permalink
Tidying up the code
Browse files Browse the repository at this point in the history
  • Loading branch information
isichei committed Sep 8, 2023
1 parent 53434c3 commit ec7b5da
Show file tree
Hide file tree
Showing 7 changed files with 99 additions and 273 deletions.
107 changes: 15 additions & 92 deletions api/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,107 +2,30 @@ package api

import (
"context"
"encoding/base64"
"fmt"
"log"
"strings"

"github.com/aws/aws-lambda-go/events"
)

type SimpleResponse struct {
Body string
StatusCode int
}

// Testing handler for debugging
func TestRequestHandler(ctx context.Context, request events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {

default_body := "Unknown request: " + request.HTTPMethod + "with path: " + request.Path
default_status := 404
sr := SimpleResponse{Body: default_body, StatusCode: default_status}

if request.HTTPMethod == "GET" {
switch request.Path {
case "/":
sr.Body = "Root hit"
sr.StatusCode = 200
case "/search-recipes":
text := request.QueryStringParameters["text"]
sr.Body = "Search response hit with query: " + text
sr.StatusCode = 200
default:
sr.Body = "Unknown path: " + request.Path
sr.StatusCode = 404
}
func errorResponse(status int, message string) events.APIGatewayProxyResponse {
return events.APIGatewayProxyResponse{
Body: message,
StatusCode: status,
Headers: map[string]string{"Content-Type": "text/plain"},
}

return events.APIGatewayProxyResponse{Body: sr.Body, StatusCode: sr.StatusCode}, nil
}

// Real handler for recipe App
func RecipeRequestHandler(ctx context.Context, request events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {

var requester Requester

log.Printf("Path: %s", request.Path)
log.Printf("Method: %s", request.HTTPMethod)

if request.HTTPMethod == "GET" {
switch request_path := request.Path; {
case request_path == "/":
requester = NewHtmlRequester(true, "")
case strings.HasPrefix(request_path, "/search-recipes"):
requester = NewHtmlRequester(false, request.QueryStringParameters["text"])
case strings.HasPrefix(request_path, "/static"):
requester = TextRequester{}
case strings.HasPrefix(request_path, "/thumbnails"):
image_name, _ := strings.CutPrefix(request_path, "/")
requester = NewImageRequester(image_name)
default:
err_body := fmt.Sprintf("Unknown Path: %s", request.Path)
log.Print(err_body)
return events.APIGatewayProxyResponse{
Body: err_body,
StatusCode: 404,
Headers: request.Headers,
}, nil
}
} else {
err_body := "Only expect GET requests"
log.Print(err_body)
return events.APIGatewayProxyResponse{
Body: err_body,
StatusCode: 405,
Headers: request.Headers,
}, nil
}

body, e := requester.RetrieveData()
if e != nil {
body_err := e.Error()
log.Print(body_err)
return events.APIGatewayProxyResponse{
Body: body_err,
StatusCode: 405,
Headers: request.Headers,
}, nil
// This creates handles the actual lambda request and will deal with errors in this handler, any remaining uncaught errors
// should be caught by the lambda itself
func RequestHandler(ctx context.Context, request events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {
requester, err := RequesterFactory(request)
if err != nil {
return errorResponse(405, err.Error()), nil
}
headers := map[string]string{"Content-Type": requester.ContentType()}
if requester.ContentType() == "image/jpeg" {
headers["Content-Length"] = fmt.Sprintf("%d", len(body))
return events.APIGatewayProxyResponse{
Body: base64.StdEncoding.EncodeToString(body),
StatusCode: 200,
Headers: headers,
IsBase64Encoded: true,
}, nil

resp, err := requester.Do(ctx, request)
if err != nil {
return errorResponse(500, err.Error()), nil
} else {
return events.APIGatewayProxyResponse{
Body: string(body),
StatusCode: 200,
Headers: headers,
}, nil
return resp, nil
}
}
121 changes: 82 additions & 39 deletions api/requesters.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,17 @@ package api

import (
"bytes"
"context"
"embed"
"encoding/base64"
"errors"
"fmt"
"html/template"
"io/fs"
"log"
"strings"

"github.com/aws/aws-lambda-go/events"
"github.com/isichei/recipe-book/types"
)

Expand All @@ -15,67 +21,104 @@ import (
var StaticResources embed.FS

type Requester interface {
RetrieveData() ([]byte, error)
ContentType() string
Do(ctx context.Context, request events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error)
}

type HtmlRequester struct {
tmpl *template.Template
searchText string
}
func RequesterFactory(request events.APIGatewayProxyRequest) (Requester, error) {

func NewHtmlRequester(is_root bool, text string) HtmlRequester {
var h HtmlRequester
log.Printf("Path: %s", request.Path)
log.Printf("Method: %s", request.HTTPMethod)

if is_root {
h = HtmlRequester{
template.Must(template.ParseFS(StaticResources, "templates/home.html", "templates/search_results.html")),
"",
}
} else {
h = HtmlRequester{
template.Must(template.ParseFS(StaticResources, "templates/search_results.html")),
text,
}
if request.HTTPMethod != "GET" {
return nil, errors.New(fmt.Sprintf("API only accepts GET requests but got a %s request", request.HTTPMethod))
}
var requester Requester
switch request_path := request.Path; {
case request_path == "/":
requester = homeRequester{}
case strings.HasPrefix(request_path, "/search-recipes"):
requester = searchRecipesRequester{}
case strings.HasPrefix(request_path, "/static"):
requester = staticRequester{}
case strings.HasPrefix(request_path, "/thumbnails"):
requester = imageRequester{}
default:
return nullRequester{}, errors.New(fmt.Sprintf("Recieved unexpected Path %s", request_path))
}
return h
return requester, nil
}

func (h HtmlRequester) RetrieveData() ([]byte, error) {
var html bytes.Buffer
type nullRequester struct{}

e := h.tmpl.Execute(&html, searchRecipes(h.searchText))
return html.Bytes(), e
func (nullRequester) Do(ctx context.Context, request events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {
return events.APIGatewayProxyResponse{}, nil
}

func (h HtmlRequester) ContentType() string {
return "text/html"
}
type homeRequester struct{}

type ImageRequester struct {
imagePath string
func (homeRequester) Do(ctx context.Context, request events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {
h := template.Must(template.ParseFS(StaticResources, "templates/home.html", "templates/search_results.html"))
return htmlTemplateToResponse(h, searchRecipes(""))
}

func NewImageRequester(imagePath string) ImageRequester {
return ImageRequester{imagePath}
type searchRecipesRequester struct{}

func (searchRecipesRequester) Do(ctx context.Context, request events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {
tmpl := template.Must(template.ParseFS(StaticResources, "templates/search_results.html"))
return htmlTemplateToResponse(tmpl, searchRecipes(request.QueryStringParameters["text"]))
}

func (ir ImageRequester) RetrieveData() ([]byte, error) {
return fs.ReadFile(StaticResources, ir.imagePath)
type staticRequester struct{}

func (staticRequester) Do(ctx context.Context, request events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {
b, err := fs.ReadFile(StaticResources, "static/styles.css")
if err != nil {
return events.APIGatewayProxyResponse{}, err
}
headers := map[string]string{"Content-Type": "text/css"}
return events.APIGatewayProxyResponse{
Body: string(b),
StatusCode: 200,
Headers: headers,
}, nil
}

func (ir ImageRequester) ContentType() string {
return "image/jpeg"
type imageRequester struct{}

func (imageRequester) Do(ctx context.Context, request events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {

image_name, _ := strings.CutPrefix(request.Path, "/")
b, err := fs.ReadFile(StaticResources, image_name)
if err != nil {
return events.APIGatewayProxyResponse{}, err
}
return imageToResponse(b), nil
}

type TextRequester struct{}
func htmlTemplateToResponse(tmpl *template.Template, data any) (events.APIGatewayProxyResponse, error) {
var html bytes.Buffer

e := tmpl.Execute(&html, data)

func (tr TextRequester) RetrieveData() ([]byte, error) {
return fs.ReadFile(StaticResources, "static/styles.css")
if e != nil {
return events.APIGatewayProxyResponse{}, e
}
headers := map[string]string{"Content-Type": "text/html"}
return events.APIGatewayProxyResponse{
Body: html.String(),
StatusCode: 200,
Headers: headers,
}, nil
}

func (tr TextRequester) ContentType() string {
return "text/css"
func imageToResponse(body []byte) events.APIGatewayProxyResponse {
headers := map[string]string{"Content-Type": "image/jpeg", "Content-Length": fmt.Sprintf("%d", len(body))}
return events.APIGatewayProxyResponse{
Body: base64.StdEncoding.EncodeToString(body),
StatusCode: 200,
Headers: headers,
IsBase64Encoded: true,
}
}

// Todo move stuff around once lambdas are working as this duplicates storage package
Expand Down
75 changes: 0 additions & 75 deletions api/server.go

This file was deleted.

2 changes: 1 addition & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,5 @@ var staticResources embed.FS
func main() {

api.StaticResources = staticResources
lambda.Start(api.RecipeRequestHandler)
lambda.Start(api.RequestHandler)
}
2 changes: 1 addition & 1 deletion main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ resource "aws_lambda_function" "recipe_app_lambda" {
filename = data.archive_file.recipe_app_zip.output_path
role = aws_iam_role.lambda_role.arn
handler = "bootstrap"
runtime = "go1.x"
runtime = "provided.al2"
timeout = 10
source_code_hash = filebase64sha256(data.archive_file.recipe_app_zip.output_path)
}
Expand Down
Loading

0 comments on commit ec7b5da

Please sign in to comment.