package stdlib import ( "context" "fmt" "io" "maps" "net/http" "strings" "reichard.io/poiesis/internal/functions" ) func init() { functions.RegisterAsyncFunction("fetch", Fetch) } type FetchArgs struct { Input string `json:"input"` Init *RequestInit `json:"init,omitempty"` } func (f FetchArgs) Validate() error { return nil } type RequestInit struct { Method string `json:"method,omitempty"` Headers map[string]string `json:"headers,omitempty"` Body *string `json:"body,omitempty"` } func (o *RequestInit) Validate() error { return nil } type Response struct { OK bool `json:"ok"` Status int `json:"status"` Body string `json:"body"` Headers map[string]string `json:"headers"` } func Fetch(ctx context.Context, args FetchArgs) (Response, error) { // Set Default Method and Headers method := "GET" headers := make(map[string]string) // Apply Init Options if args.Init != nil { if args.Init.Method != "" { method = args.Init.Method } if args.Init.Headers != nil { maps.Copy(headers, args.Init.Headers) } } // Create Request req, err := http.NewRequestWithContext(ctx, method, args.Input, nil) if err != nil { return Response{}, fmt.Errorf("failed to create request: %w", err) } // Set Request Headers for k, v := range headers { req.Header.Set(k, v) } // Execute Request resp, err := http.DefaultClient.Do(req) if err != nil { return Response{}, fmt.Errorf("failed to fetch: %w", err) } defer func() { _ = resp.Body.Close() }() // Read Response Body body, err := io.ReadAll(resp.Body) if err != nil { return Response{}, fmt.Errorf("failed to read body: %w", err) } // Collect Response Headers resultHeaders := make(map[string]string) for key, values := range resp.Header { if len(values) > 0 { val := values[0] resultHeaders[key] = val resultHeaders[strings.ToLower(key)] = val } } // Return Response return Response{ OK: resp.StatusCode >= 200 && resp.StatusCode < 300, Status: resp.StatusCode, Body: string(body), Headers: resultHeaders, }, nil }