wip3
This commit is contained in:
92
internal/runtime/standard/fetch.go
Normal file
92
internal/runtime/standard/fetch.go
Normal file
@@ -0,0 +1,92 @@
|
||||
package standard
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/dop251/goja"
|
||||
"reichard.io/poiesis/internal/runtime/pkg/builtin"
|
||||
)
|
||||
|
||||
type FetchResult struct {
|
||||
OK bool
|
||||
Status int
|
||||
Body string
|
||||
Headers map[string]string
|
||||
}
|
||||
|
||||
func Fetch(url string, options map[string]any) (*FetchResult, error) {
|
||||
req, err := http.NewRequest("GET", url, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create request: %w", err)
|
||||
}
|
||||
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to fetch: %w", err)
|
||||
}
|
||||
defer func() {
|
||||
_ = resp.Body.Close()
|
||||
}()
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read body: %w", err)
|
||||
}
|
||||
|
||||
headers := make(map[string]string)
|
||||
for key, values := range resp.Header {
|
||||
if len(values) > 0 {
|
||||
val := values[0]
|
||||
headers[key] = val
|
||||
headers[strings.ToLower(key)] = val
|
||||
}
|
||||
}
|
||||
|
||||
return &FetchResult{
|
||||
OK: resp.StatusCode >= 200 && resp.StatusCode < 300,
|
||||
Status: resp.StatusCode,
|
||||
Body: string(body),
|
||||
Headers: headers,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func convertFetchResult(vm *goja.Runtime, result *FetchResult) goja.Value {
|
||||
if result == nil {
|
||||
return goja.Null()
|
||||
}
|
||||
|
||||
obj := vm.NewObject()
|
||||
_ = obj.Set("ok", result.OK)
|
||||
_ = obj.Set("status", result.Status)
|
||||
_ = obj.Set("text", func() string {
|
||||
return result.Body
|
||||
})
|
||||
|
||||
headersObj := vm.NewObject()
|
||||
headers := result.Headers
|
||||
_ = headersObj.Set("get", func(c goja.FunctionCall) goja.Value {
|
||||
if len(c.Arguments) < 1 {
|
||||
return goja.Undefined()
|
||||
}
|
||||
key := c.Arguments[0].String()
|
||||
return vm.ToValue(headers[key])
|
||||
})
|
||||
_ = obj.Set("headers", headersObj)
|
||||
|
||||
return obj
|
||||
}
|
||||
|
||||
func init() {
|
||||
builtin.RegisterCustomConverter(convertFetchResult)
|
||||
|
||||
builtin.RegisterBuiltin("fetch", Fetch)
|
||||
builtin.RegisterBuiltin("add", func(a, b int) int {
|
||||
return a + b
|
||||
})
|
||||
builtin.RegisterBuiltin("greet", func(name string) string {
|
||||
return fmt.Sprintf("Hello, %s!", name)
|
||||
})
|
||||
}
|
||||
56
internal/runtime/standard/fetch_test.go
Normal file
56
internal/runtime/standard/fetch_test.go
Normal file
@@ -0,0 +1,56 @@
|
||||
package standard
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestFetch(t *testing.T) {
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.Header().Set("X-Custom-Header", "test-value")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
_, _ = w.Write([]byte(`{"status":"ok","message":"Hello from httptest"}`))
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
result, err := Fetch(server.URL, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.True(t, result.OK)
|
||||
assert.Equal(t, http.StatusOK, result.Status)
|
||||
assert.Contains(t, result.Body, "Hello from httptest")
|
||||
assert.Contains(t, result.Body, `"status":"ok"`)
|
||||
assert.Equal(t, "application/json", result.Headers["Content-Type"])
|
||||
assert.Equal(t, "test-value", result.Headers["X-Custom-Header"])
|
||||
}
|
||||
|
||||
func TestFetchHTTPBin(t *testing.T) {
|
||||
t.Skip("httpbin.org test is flaky")
|
||||
|
||||
result, err := Fetch("https://httpbin.org/get", nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.True(t, result.OK)
|
||||
assert.Equal(t, http.StatusOK, result.Status)
|
||||
assert.Contains(t, result.Body, `"args"`)
|
||||
assert.Equal(t, "application/json", result.Headers["Content-Type"])
|
||||
}
|
||||
|
||||
func TestFetchWith404(t *testing.T) {
|
||||
result, err := Fetch("https://httpbin.org/status/404", nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.False(t, result.OK)
|
||||
assert.Equal(t, http.StatusNotFound, result.Status)
|
||||
}
|
||||
|
||||
func TestFetchWithInvalidURL(t *testing.T) {
|
||||
_, err := Fetch("http://this-domain-does-not-exist-12345.com", nil)
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "failed to fetch")
|
||||
}
|
||||
Reference in New Issue
Block a user