Skip to content

Commit b058ceb

Browse files
committed
feat(redis): implement redis in wasip2
Signed-off-by: Andrew Steurer <[email protected]>
1 parent 1a3e34e commit b058ceb

File tree

7 files changed

+440
-1
lines changed

7 files changed

+440
-1
lines changed

v3/examples/mqtt-outbound/go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
module github.com/http_go
1+
module github.com/spinframework/spin-go-sdk/v3/examples/mqtt-outbound
22

33
go 1.24
44

v3/examples/redis-outbound/README.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Requirements
2+
- Latest version of [TinyGo](https://tinygo.org/getting-started/)
3+
- Latest version of [Docker](https://docs.docker.com/get-started/get-docker/)
4+
5+
# Usage
6+
7+
In one terminal window, you'll run a Redis container:
8+
```sh
9+
docker run -p 6379:6379 redis:8.2
10+
```
11+
12+
In another terminal, you'll run your Spin app:
13+
```sh
14+
spin up --build
15+
```
16+
17+
In yet another terminal, you'll interact with the Spin app:
18+
```sh
19+
curl localhost:3000
20+
```

v3/examples/redis-outbound/go.mod

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
module github.com/spinframework/spin-go-sdk/v3/examples/redis-outbound
2+
3+
go 1.24
4+
5+
require github.com/spinframework/spin-go-sdk/v3 v3.0.0
6+
7+
require (
8+
github.com/julienschmidt/httprouter v1.3.0 // indirect
9+
go.bytecodealliance.org/cm v0.2.2 // indirect
10+
)
11+
12+
replace github.com/spinframework/spin-go-sdk/v3 => ../../

v3/examples/redis-outbound/go.sum

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U=
2+
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
3+
go.bytecodealliance.org/cm v0.2.2 h1:M9iHS6qs884mbQbIjtLX1OifgyPG9DuMs2iwz8G4WQA=
4+
go.bytecodealliance.org/cm v0.2.2/go.mod h1:JD5vtVNZv7sBoQQkvBvAAVKJPhR/bqBH7yYXTItMfZI=

v3/examples/redis-outbound/main.go

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
"net/http"
6+
"os"
7+
"reflect"
8+
"sort"
9+
"strconv"
10+
11+
spin_http "github.com/spinframework/spin-go-sdk/v3/http"
12+
"github.com/spinframework/spin-go-sdk/v3/redis"
13+
)
14+
15+
func init() {
16+
17+
// handler for the http trigger
18+
spin_http.Handle(func(w http.ResponseWriter, _ *http.Request) {
19+
20+
// addr is the environment variable set in `spin.toml` that points to the
21+
// address of the Redis server.
22+
addr := os.Getenv("REDIS_ADDRESS")
23+
24+
// channel is the environment variable set in `spin.toml` that specifies
25+
// the Redis channel that the component will publish to.
26+
channel := os.Getenv("REDIS_CHANNEL")
27+
28+
// payload is the data publish to the redis channel.
29+
payload := []byte(`Hello redis from tinygo!`)
30+
31+
rdb, err := redis.NewClient(addr)
32+
if err != nil {
33+
http.Error(w, err.Error(), http.StatusInternalServerError)
34+
return
35+
}
36+
37+
if err := rdb.Publish(channel, payload); err != nil {
38+
http.Error(w, err.Error(), http.StatusInternalServerError)
39+
return
40+
}
41+
42+
// set redis `mykey` = `myvalue`
43+
if err := rdb.Set("mykey", []byte("myvalue")); err != nil {
44+
http.Error(w, err.Error(), http.StatusInternalServerError)
45+
return
46+
}
47+
48+
// get redis payload for `mykey`
49+
if payload, err := rdb.Get("mykey"); err != nil {
50+
http.Error(w, err.Error(), http.StatusInternalServerError)
51+
return
52+
} else {
53+
w.Write([]byte("mykey value was: "))
54+
w.Write(payload)
55+
w.Write([]byte("\n"))
56+
}
57+
58+
// incr `spin-go-incr` by 1
59+
if payload, err := rdb.Incr("spin-go-incr"); err != nil {
60+
http.Error(w, err.Error(), http.StatusInternalServerError)
61+
return
62+
} else {
63+
w.Write([]byte("spin-go-incr value: "))
64+
w.Write([]byte(strconv.FormatInt(payload, 10)))
65+
w.Write([]byte("\n"))
66+
}
67+
68+
// delete `spin-go-incr` and `mykey`
69+
if payload, err := rdb.Del("spin-go-incr", "mykey", "non-existing-key"); err != nil {
70+
http.Error(w, err.Error(), http.StatusInternalServerError)
71+
} else {
72+
w.Write([]byte("deleted keys num: "))
73+
w.Write([]byte(strconv.FormatInt(int64(payload), 10)))
74+
w.Write([]byte("\n"))
75+
}
76+
77+
if _, err := rdb.Sadd("myset", "foo", "bar"); err != nil {
78+
http.Error(w, err.Error(), http.StatusInternalServerError)
79+
return
80+
}
81+
82+
{
83+
expected := []string{"bar", "foo"}
84+
payload, err := rdb.Smembers("myset")
85+
if err != nil {
86+
http.Error(w, err.Error(), http.StatusInternalServerError)
87+
return
88+
}
89+
sort.Strings(payload)
90+
if !reflect.DeepEqual(payload, expected) {
91+
http.Error(
92+
w,
93+
fmt.Sprintf(
94+
"unexpected SMEMBERS result: expected %v, got %v",
95+
expected,
96+
payload,
97+
),
98+
http.StatusInternalServerError,
99+
)
100+
return
101+
}
102+
}
103+
104+
if _, err := rdb.Srem("myset", "bar"); err != nil {
105+
http.Error(w, err.Error(), http.StatusInternalServerError)
106+
return
107+
}
108+
109+
{
110+
expected := []string{"foo"}
111+
if payload, err := rdb.Smembers("myset"); err != nil {
112+
http.Error(w, err.Error(), http.StatusInternalServerError)
113+
return
114+
} else if !reflect.DeepEqual(payload, expected) {
115+
http.Error(
116+
w,
117+
fmt.Sprintf(
118+
"unexpected SMEMBERS result: expected %v, got %v",
119+
expected,
120+
payload,
121+
),
122+
http.StatusInternalServerError,
123+
)
124+
return
125+
}
126+
}
127+
128+
if _, err := rdb.Execute("set", "message", "hello"); err != nil {
129+
http.Error(w, err.Error(), http.StatusInternalServerError)
130+
return
131+
}
132+
133+
if _, err := rdb.Execute("append", "message", " world"); err != nil {
134+
http.Error(w, err.Error(), http.StatusInternalServerError)
135+
return
136+
}
137+
138+
if payload, err := rdb.Execute("get", "message"); err != nil {
139+
http.Error(w, err.Error(), http.StatusInternalServerError)
140+
return
141+
} else if !reflect.DeepEqual(
142+
payload,
143+
[]*redis.Result{{
144+
Kind: redis.ResultKindBinary,
145+
Val: []byte("hello world"),
146+
}}) {
147+
148+
http.Error(w, "unexpected GET result", http.StatusInternalServerError)
149+
fmt.Println()
150+
return
151+
}
152+
})
153+
}
154+
155+
func main() {}

v3/examples/redis-outbound/spin.toml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
spin_manifest_version = 2
2+
3+
[application]
4+
name = "go-redis-outbound-example"
5+
version = "0.1.0"
6+
authors = ["Andrew Steurer <[email protected]>"]
7+
description = "Using Spin with Redis"
8+
9+
[[trigger.http]]
10+
route = "/"
11+
component = "redis-outbound"
12+
13+
[component.redis-outbound]
14+
source = "main.wasm"
15+
environment = { REDIS_ADDRESS = "redis://localhost:6379", REDIS_CHANNEL = "messages" }
16+
allowed_outbound_hosts = ["redis://localhost:6379"]
17+
18+
[component.redis-outbound.build]
19+
command = "tinygo build -target=wasip2 --wit-package $(go list -mod=readonly -m -f '{{.Dir}}' github.com/spinframework/spin-go-sdk/v3)/wit --wit-world http-trigger -gc=leaking -o main.wasm main.go"
20+
watch = ["**/*.go", "go.mod"]

0 commit comments

Comments
 (0)