From 9e679364022c6b8be398f48940207727cfc9ef98 Mon Sep 17 00:00:00 2001 From: Benjamin Cane Date: Sun, 29 Oct 2023 18:33:54 -0700 Subject: [PATCH] Add request handler and kv cache --- Makefile | 6 ++- config/tarmac.json | 9 ++++ docker-compose.yml | 8 ++++ functions/src/handler/main.go | 82 ++++++++++++++++++++++++++++++++--- functions/src/init/main.go | 2 +- 5 files changed, 100 insertions(+), 7 deletions(-) diff --git a/Makefile b/Makefile index 0aa6296..013f614 100644 --- a/Makefile +++ b/Makefile @@ -3,10 +3,14 @@ build: ## Create build directory mkdir -p functions/build - ## Run TinyGo build via Docker because its easier + ## Build Init Function docker run --rm -v `pwd`:/build -w /build/functions/build/init tinygo/tinygo:0.25.0 tinygo build -o /build/functions/build/init.wasm -target wasi /build/functions/src/init/main.go + ## Build CSV Fetch Function docker run --rm -v `pwd`:/build -w /build/functions/build/data/fetch tinygo/tinygo:0.25.0 tinygo build -o /build/functions/build/fetch.wasm -target wasi /build/functions/src/data/fetch/main.go + ## Build CSV Load Function docker run --rm -v `pwd`:/build -w /build/functions/build/data/load tinygo/tinygo:0.25.0 tinygo build -o /build/functions/build/load.wasm -target wasi /build/functions/src/data/load/main.go + ## Build HTTP Request Handler Function + docker run --rm -v `pwd`:/build -w /build/functions/build/handler tinygo/tinygo:0.25.0 tinygo build -o /build/functions/build/handler.wasm -target wasi /build/functions/src/handler/main.go .PHONY: tests tests: diff --git a/config/tarmac.json b/config/tarmac.json index 4d1d45c..199a4b0 100644 --- a/config/tarmac.json +++ b/config/tarmac.json @@ -11,9 +11,18 @@ }, "fetch": { "filepath": "/functions/fetch.wasm" + }, + "handler": { + "filepath": "/functions/handler.wasm" } }, "routes": [ + { + "type": "http", + "path": "/", + "methods": ["POST"], + "function": "handler" + }, { "type": "init", "function": "init" diff --git a/docker-compose.yml b/docker-compose.yml index a484a0d..0da4b28 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -13,11 +13,15 @@ services: - "APP_ENABLE_SQL=true" - "APP_SQL_TYPE=mysql" - "APP_SQL_DSN=root:example@tcp(mysql:3306)/example" + - "APP_ENABLE_KVSTORE=true" + - "APP_KVSTORE_TYPE=redis" + - "APP_REDIS_SERVER=redis:6379" volumes: - "./config:/config" - "./functions/build:/functions" depends_on: - mysql + - redis mysql: image: bitnami/mysql:latest restart: always @@ -26,3 +30,7 @@ services: MYSQL_DATABASE: example ports: - 3306:3306 + redis: + image: bitnami/redis:latest + environment: + - ALLOW_EMPTY_PASSWORD=yes diff --git a/functions/src/handler/main.go b/functions/src/handler/main.go index 0d81bdc..75230be 100644 --- a/functions/src/handler/main.go +++ b/functions/src/handler/main.go @@ -1,8 +1,11 @@ package main import ( + "encoding/base64" "fmt" "github.com/tarmac-project/tarmac/pkg/sdk" + "github.com/valyala/fastjson" + "html" ) type Function struct { @@ -10,19 +13,88 @@ type Function struct { } func (f *Function) Handler(payload []byte) ([]byte, error) { - return []byte("Hello World"), fmt.Errorf("not implemented http handler") // Parse the incoming request + lc := fastjson.GetString(payload, "local_code") + if lc == "" { + return []byte(`{"error": "local_code is required"}`), fmt.Errorf("local_code is required") + } - // Lookup the airport from cache + // Lookup the airport from cache and return if found + cache, err := f.tarmac.KV.Get(lc) + if err == nil && len(cache) > 0 { + return cache, nil + } // If not in cache, lookup the airport from the database + query := fmt.Sprintf(`SELECT * FROM airports WHERE local_code = "%s"`, html.EscapeString(lc)) + data, err := f.tarmac.SQL.Query(query) + if err != nil { + return nil, fmt.Errorf("error querying database: %w", err) + } - // If not previously in cache, add to cache + // Verify we got a result + if len(data) == 0 { + return []byte(`{"error": "airport not found"}`), fmt.Errorf("airport not found") + } + + // Build a response + rsp := make(map[string]string) + + // Grab fields from the database and base64 decode them (this would be much easier with encoding/json) + v, err := base64.StdEncoding.DecodeString(fastjson.GetString(data, "0", "local_code")) + if err != nil { + return nil, fmt.Errorf("error decoding local_code: %w", err) + } + rsp["local_code"] = string(v) + + v, err = base64.StdEncoding.DecodeString(fastjson.GetString(data, "0", "name")) + if err != nil { + return nil, fmt.Errorf("error decoding name: %w", err) + } + rsp["name"] = string(v) + + v, err = base64.StdEncoding.DecodeString(fastjson.GetString(data, "0", "iso_country")) + if err != nil { + return nil, fmt.Errorf("error decoding country: %w", err) + } + rsp["country"] = string(v) - // If nothing found, return an error + v, err = base64.StdEncoding.DecodeString(fastjson.GetString(data, "0", "emoji")) + if err != nil { + return nil, fmt.Errorf("error decoding emoji: %w", err) + } + rsp["emoji"] = string(v) + + v, err = base64.StdEncoding.DecodeString(fastjson.GetString(data, "0", "type")) + if err != nil { + return nil, fmt.Errorf("error decoding type: %w", err) + } + rsp["type"] = string(v) + + v, err = base64.StdEncoding.DecodeString(fastjson.GetString(data, "0", "type_emoji")) + if err != nil { + return nil, fmt.Errorf("error decoding type_emoji: %w", err) + } + rsp["type_emoji"] = string(v) + + v, err = base64.StdEncoding.DecodeString(fastjson.GetString(data, "0", "status")) + if err != nil { + return nil, fmt.Errorf("error decoding status: %w", err) + } + rsp["status"] = string(v) + + // Create JSON response + j := fmt.Sprintf(`{"local_code": "%s", "name": "%s", "country": "%s", "emoji": "%s", "type": "%s", "type_emoji": "%s", "status": "%s"}`, + rsp["local_code"], rsp["name"], rsp["country"], rsp["emoji"], rsp["type"], rsp["type_emoji"], rsp["status"]) + + // If not previously in cache, add to cache + err = f.tarmac.KV.Set(lc, []byte(j)) + if err != nil { + f.tarmac.Logger.Error(fmt.Sprintf("error setting cache value: %w", err)) + } // Return the airport - return nil, nil + return []byte(j), nil } func main() { diff --git a/functions/src/init/main.go b/functions/src/init/main.go index cb101a7..72918c9 100644 --- a/functions/src/init/main.go +++ b/functions/src/init/main.go @@ -14,7 +14,7 @@ func (f *Function) Handler(_ []byte) ([]byte, error) { // Create MySQL Database structure query := `CREATE TABLE IF NOT EXISTS airports ( - local_code VARCHAR(4) NOT NULL UNIQUE, + local_code VARCHAR(25) NOT NULL UNIQUE, name VARCHAR(255) NOT NULL, type VARCHAR(255) NOT NULL, type_emoji VARCHAR(255),