penguin/AstroJS

Javascript framework for my blog

resolver-go/internal/closure/client.go

raw ยท 2355 bytes

package closure

import (
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"os"
	"path/filepath"
	"strconv"
	"time"
)

type Client struct {
	endpoint string
	http     *http.Client
}

func NewClientFromEnv() *Client {
	endpoint := os.Getenv("CLOSURE_ENDPOINT")
	if endpoint == "" {
		endpoint = "http://closure-svc:8080/compile"
	}

	return &Client{
		endpoint: endpoint,
		http: &http.Client{
			Timeout: 70 * time.Second,
		},
	}
}

func (c *Client) Compile(ctx context.Context, reqBody CompileRequest) ([]byte, error) {
	body, err := json.Marshal(reqBody)
	if err != nil {
		return nil, err
	}

	req, err := http.NewRequestWithContext(
		ctx,
		http.MethodPost,
		c.endpoint,
		bytes.NewReader(body),
	)
	if err != nil {
		return nil, err
	}

	req.Header.Set("Content-Type", "application/json")

	resp, err := c.http.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	respBody, err := io.ReadAll(io.LimitReader(resp.Body, 64<<20))
	if err != nil {
		return nil, err
	}

	if resp.StatusCode/100 != 2 {
		return nil, fmt.Errorf("closure failed: %s: %s", resp.Status, respBody)
	}

	var cr CompileResponse
	if err := json.Unmarshal(respBody, &cr); err != nil {
		return nil, fmt.Errorf("closure returned invalid json: %w: %s", err, respBody)
	}

	if !cr.OK {
		return nil, fmt.Errorf("closure compile failed: errors=%q warnings=%q", len(cr.Errors), len(cr.Warnings))
	}

	return []byte(cr.JS), nil
}

func (c *Client) DebugPrintCurl(ctx context.Context, reqBody CompileRequest) {
	if os.Getenv("CLOSURE_DEBUG_CURL") == "" {
		return
	}

	body, err := json.MarshalIndent(reqBody, "", "  ")
	if err != nil {
		fmt.Fprintf(os.Stderr, "closure debug: marshal payload failed: %v\n", err)
		return
	}

	path := filepath.Join(
		os.TempDir(),
		fmt.Sprintf("closure-request-%d.json", time.Now().UnixNano()),
	)

	if err := os.WriteFile(path, body, 0o600); err != nil {
		fmt.Fprintf(os.Stderr, "closure debug: write payload failed: %v\n", err)
		return
	}

	fmt.Fprintf(
		os.Stderr,
		"closure debug curl:\n  curl %s -H 'Content-Type: application/json' --data-binary @%s\n",
		shellQuote(c.endpoint),
		shellQuote(path),
	)

	if deadline, ok := ctx.Deadline(); ok {
		fmt.Fprintf(os.Stderr, "closure debug deadline: %s\n", deadline.Format(time.RFC3339Nano))
	}
}

func shellQuote(s string) string {
	return strconv.Quote(s)
}