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) }