diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index 8c71c95..afad31c 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -1,7 +1,7 @@ name: Integration Tests on: [push] env: - VICEROY_VERSION: 0.9.3 + VICEROY_VERSION: 0.9.6 jobs: integration-tests-tinygo: strategy: diff --git a/CHANGELOG.md b/CHANGELOG.md index fd874dd..eb93beb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,13 @@ +## 1.3.1 (2024-04-25) + +### Added + +- `kvstore`: add Store.Delete method + +### Changed + +- Update Viceroy requirement to 0.9.6 + ## 1.3.0 (2024-02-21) ### Added diff --git a/integration_tests/kvstore/main_test.go b/integration_tests/kvstore/main_test.go index 416c51a..39d09aa 100644 --- a/integration_tests/kvstore/main_test.go +++ b/integration_tests/kvstore/main_test.go @@ -39,4 +39,20 @@ func TestKVStore(t *testing.T) { if got, want := animal.String(), "cat"; got != want { t.Errorf("Insert: got %q, want %q", got, want) } + + if err = store.Delete("animal"); err != nil { + t.Fatal(err) + } + + _, err = store.Lookup("animal") + if err == nil { + t.Error("expected Lookup failure") + } + + /* + // TODO(athomason) address inconsistent behavior in viceroy and production + if err = store.Delete("nonexistent"); err != nil { + t.Fatal(err) + } + */ } diff --git a/internal/abi/fastly/hostcalls_guest.go b/internal/abi/fastly/hostcalls_guest.go index 6e397d6..ff89679 100644 --- a/internal/abi/fastly/hostcalls_guest.go +++ b/internal/abi/fastly/hostcalls_guest.go @@ -2606,6 +2606,52 @@ func (o *KVStore) Insert(key string, value io.Reader) error { return nil } +// witx: +// +// (@interface func (export "delete_async") +// (param $store $object_store_handle) +// (param $key string) +// (param $pending_handle_out (@witx pointer $pending_kv_delete_handle)) +// (result $err (expected (error $fastly_status))) +// ) + +//go:wasmimport fastly_object_store delete_async +//go:noescape +func fastlyObjectStoreDeleteAsync( + h objectStoreHandle, + keyData prim.Pointer[prim.U8], keyLen prim.Usize, + pendingReq prim.Pointer[pendingRequestHandle], +) FastlyStatus + +// witx: +// +// (@interface func (export "pending_delete_wait") +// (param $pending_handle $pending_kv_delete_handle) +// (result $err (expected (error $fastly_status))) +// ) + +//go:wasmimport fastly_object_store pending_delete_wait +//go:noescape +func fastlyObjectStorePendingDeleteWait( + pendingReq pendingRequestHandle, +) FastlyStatus + +// Delete removes a key from the kv store. +func (o *KVStore) Delete(key string) error { + keyBuffer := prim.NewReadBufferFromString(key).Wstring() + + var handle pendingRequestHandle + if err := fastlyObjectStoreDeleteAsync( + o.h, + keyBuffer.Data, keyBuffer.Len, + prim.ToPointer(&handle), + ).toError(); err != nil { + return err + } + + return fastlyObjectStorePendingDeleteWait(handle).toError() +} + // SecretStore represents a Fastly secret store, a collection of // key/value pairs for storing sensitive data. type SecretStore struct { diff --git a/internal/abi/fastly/hostcalls_noguest.go b/internal/abi/fastly/hostcalls_noguest.go index 112618b..b22034e 100644 --- a/internal/abi/fastly/hostcalls_noguest.go +++ b/internal/abi/fastly/hostcalls_noguest.go @@ -319,6 +319,10 @@ func (o *KVStore) Insert(key string, value io.Reader) error { return fmt.Errorf("not implemented") } +func (o *KVStore) Delete(key string) error { + return fmt.Errorf("not implemented") +} + type ( SecretStore struct{} Secret struct{} diff --git a/kvstore/kvstore.go b/kvstore/kvstore.go index a1c6f98..b875a8b 100644 --- a/kvstore/kvstore.go +++ b/kvstore/kvstore.go @@ -129,3 +129,22 @@ func (s *Store) Insert(key string, value io.Reader) error { } return nil } + +// Delete removes a key from the associated KV store. +func (s *Store) Delete(key string) error { + err := s.kvstore.Delete(key) + if err != nil { + status, ok := fastly.IsFastlyError(err) + switch { + case ok && status == fastly.FastlyStatusNone: + return ErrKeyNotFound + case ok && status == fastly.FastlyStatusInval: + return ErrInvalidKey + case ok: + return fmt.Errorf("%w (%s)", ErrUnexpected, status) + default: + return err + } + } + return nil +}