Skip to content

Commit 3cd22af

Browse files
committed
feat(wasip2/kv): add support for wasip2 kv
Signed-off-by: Rajat Jindal <[email protected]>
1 parent 6c3e074 commit 3cd22af

File tree

11 files changed

+295
-2
lines changed

11 files changed

+295
-2
lines changed

.github/workflows/pr.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
name: Run Integration tests
22
on:
3-
push:
3+
pull_request:
44
branches:
5-
- wasip2-http
5+
- wasip2
66

77
workflow_dispatch: {}
88

v2/examples/kv/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/v2/examples/kv
2+
3+
go 1.24.1
4+
5+
require github.com/spinframework/spin-go-sdk/v2 v2.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/v2 => ../../

v2/examples/kv/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=

v2/examples/kv/main.go

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package main
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
"net/http"
7+
8+
spinhttp "github.com/spinframework/spin-go-sdk/v2/http"
9+
"github.com/spinframework/spin-go-sdk/v2/kv"
10+
)
11+
12+
func init() {
13+
spinhttp.Handle(func(w http.ResponseWriter, r *http.Request) {
14+
store, err := kv.OpenDefault()
15+
if err != nil {
16+
http.Error(w, err.Error(), http.StatusInternalServerError)
17+
return
18+
}
19+
20+
err = store.Set("foo", []byte("bar"))
21+
if err != nil {
22+
http.Error(w, err.Error(), http.StatusInternalServerError)
23+
return
24+
}
25+
26+
value, err := store.Get("foo")
27+
if err != nil {
28+
http.Error(w, err.Error(), http.StatusInternalServerError)
29+
return
30+
}
31+
32+
if string(value) != "bar" {
33+
http.Error(w, fmt.Sprintf("expected: %q, got: %q", "bar", value), http.StatusInternalServerError)
34+
return
35+
}
36+
37+
keys, err := store.GetKeys()
38+
if err != nil {
39+
http.Error(w, err.Error(), http.StatusInternalServerError)
40+
return
41+
}
42+
43+
w.Header().Set("Content-Type", "application/json")
44+
45+
w.WriteHeader(http.StatusOK)
46+
_ = json.NewEncoder(w).Encode(keys)
47+
})
48+
}
49+
50+
func main() {}

v2/examples/kv/spin.toml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
spin_manifest_version = 2
2+
3+
[application]
4+
authors = ["Rajat Jindal <[email protected]>"]
5+
description = "A simple Spin application written in (Tiny)Go."
6+
name = "hello-kv"
7+
version = "1.0.0"
8+
9+
[[trigger.http]]
10+
route = "/hello"
11+
component = "hello"
12+
13+
[component.hello]
14+
source = "main.wasm"
15+
key_value_stores = ["default"]
16+
[component.hello.build]
17+
command = "tinygo build -target=wasip2 --wit-package $(go list -mod=readonly -m -f '{{.Dir}}' github.com/spinframework/spin-go-sdk/v2)/wit --wit-world http-trigger -gc=leaking -no-debug -o main.wasm main.go"

v2/integration_test.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,30 @@ func TestHTTPTriger(t *testing.T) {
120120
}
121121
}
122122

123+
func TestKeyValue(t *testing.T) {
124+
spin := startSpin(t, "kv/testdata/key-value")
125+
defer spin.cancel()
126+
127+
resp := retryGet(t, spin.url+"/hello")
128+
spin.cancel()
129+
if resp.Body == nil {
130+
t.Fatal("body is nil")
131+
}
132+
t.Log(resp.Status)
133+
b, err := io.ReadAll(resp.Body)
134+
resp.Body.Close()
135+
if err != nil {
136+
t.Fatal(err)
137+
}
138+
139+
// assert response body
140+
want := "[\"foo\"]\n"
141+
got := string(b)
142+
if want != got {
143+
t.Fatalf("body is not equal: want = %q got = %q", want, got)
144+
}
145+
}
146+
123147
// TestBuildExamples ensures that the tinygo examples will build successfully.
124148
func TestBuildExamples(t *testing.T) {
125149
examples, err := os.ReadDir("examples")

v2/kv/kv.go

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
package kv
2+
3+
import (
4+
"fmt"
5+
6+
keyvalue "github.com/spinframework/spin-go-sdk/v2/internal/fermyon/spin/v2.0.0/key-value"
7+
"go.bytecodealliance.org/cm"
8+
)
9+
10+
type Store struct {
11+
store *keyvalue.Store
12+
}
13+
14+
// Open the store with the label.
15+
func Open(label string) (*Store, error) {
16+
result := keyvalue.StoreOpen(label)
17+
if result.IsErr() {
18+
return nil, errorVariantToError(*result.Err())
19+
}
20+
21+
return &Store{
22+
store: result.OK(),
23+
}, nil
24+
}
25+
26+
// Open the default store.
27+
//
28+
// This is equivalent to `kv.Open("default")`.
29+
func OpenDefault() (*Store, error) {
30+
return Open("default")
31+
}
32+
33+
// Set the key/value pair in store
34+
func (s *Store) Set(key string, value []byte) error {
35+
result := s.store.Set(key, cm.ToList(value))
36+
if result.IsErr() {
37+
return errorVariantToError(*result.Err())
38+
}
39+
40+
return nil
41+
}
42+
43+
// Get the value of provided key from the store
44+
func (s *Store) Get(key string) ([]byte, error) {
45+
result := s.store.Get(key)
46+
if result.IsErr() {
47+
return nil, errorVariantToError(*result.Err())
48+
}
49+
50+
value := result.OK()
51+
if value.None() {
52+
return []byte(""), nil
53+
}
54+
55+
return value.Some().Slice(), nil
56+
}
57+
58+
// Delete the given key/value from the store
59+
func (s *Store) Delete(key string) error {
60+
result := s.store.Delete(key)
61+
if result.IsErr() {
62+
return errorVariantToError(*result.Err())
63+
}
64+
65+
return nil
66+
}
67+
68+
// Exists check if a given key exist in the store
69+
func (s *Store) Exists(key string) (bool, error) {
70+
result := s.store.Exists(key)
71+
if result.IsErr() {
72+
return false, errorVariantToError(*result.Err())
73+
}
74+
75+
return *result.OK(), nil
76+
}
77+
78+
// GetKets returns all the keys from the store
79+
func (s *Store) GetKeys() ([]string, error) {
80+
result := s.store.GetKeys()
81+
if result.IsErr() {
82+
return nil, errorVariantToError(*result.Err())
83+
}
84+
85+
return result.OK().Slice(), nil
86+
}
87+
88+
func errorVariantToError(code keyvalue.Error) error {
89+
switch code {
90+
case keyvalue.ErrorAccessDenied():
91+
return fmt.Errorf("access denied")
92+
case keyvalue.ErrorNoSuchStore():
93+
return fmt.Errorf("no such store")
94+
case keyvalue.ErrorStoreTableFull():
95+
return fmt.Errorf("store table full")
96+
default:
97+
if code.Other() != nil {
98+
return fmt.Errorf(*code.Other())
99+
}
100+
101+
return fmt.Errorf("no error provided by host implementation")
102+
}
103+
}

v2/kv/testdata/key-value/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/v2/http/testdata/kv
2+
3+
go 1.24.1
4+
5+
require github.com/spinframework/spin-go-sdk/v2 v2.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/v2 => ../../../

v2/kv/testdata/key-value/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=

v2/kv/testdata/key-value/main.go

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package main
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
"net/http"
7+
8+
spinhttp "github.com/spinframework/spin-go-sdk/v2/http"
9+
"github.com/spinframework/spin-go-sdk/v2/kv"
10+
)
11+
12+
func init() {
13+
spinhttp.Handle(func(w http.ResponseWriter, r *http.Request) {
14+
store, err := kv.OpenDefault()
15+
if err != nil {
16+
http.Error(w, err.Error(), http.StatusInternalServerError)
17+
return
18+
}
19+
20+
err = store.Set("foo", []byte("bar"))
21+
if err != nil {
22+
http.Error(w, err.Error(), http.StatusInternalServerError)
23+
return
24+
}
25+
26+
value, err := store.Get("foo")
27+
if err != nil {
28+
http.Error(w, err.Error(), http.StatusInternalServerError)
29+
return
30+
}
31+
32+
if string(value) != "bar" {
33+
http.Error(w, fmt.Sprintf("expected: %q, got: %q", "bar", value), http.StatusInternalServerError)
34+
return
35+
}
36+
37+
keys, err := store.GetKeys()
38+
if err != nil {
39+
http.Error(w, err.Error(), http.StatusInternalServerError)
40+
return
41+
}
42+
43+
w.Header().Set("Content-Type", "application/json")
44+
45+
w.WriteHeader(http.StatusOK)
46+
_ = json.NewEncoder(w).Encode(keys)
47+
})
48+
}
49+
50+
func main() {}

0 commit comments

Comments
 (0)