golang.org/x/build@v0.0.0-20240506185731-218518f32b70/internal/macservice/client.go (about)

     1  // Copyright 2023 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // Package macservice defines the client API for MacService.
     6  package macservice
     7  
     8  import (
     9  	"bytes"
    10  	"encoding/json"
    11  	"fmt"
    12  	"io"
    13  	"net/http"
    14  )
    15  
    16  const baseURL = "https://macservice-pa.googleapis.com/v1alpha1/"
    17  
    18  // Client is a MacService client.
    19  type Client struct {
    20  	apiKey string
    21  
    22  	client *http.Client
    23  }
    24  
    25  // NewClient creates a MacService client, authenticated with the provided API
    26  // key.
    27  func NewClient(apiKey string) *Client {
    28  	return &Client{
    29  		apiKey: apiKey,
    30  		client: http.DefaultClient,
    31  	}
    32  }
    33  
    34  func (c *Client) do(method, endpoint string, input, output any) error {
    35  	var buf bytes.Buffer
    36  	enc := json.NewEncoder(&buf)
    37  	if err := enc.Encode(input); err != nil {
    38  		return fmt.Errorf("error encoding request: %w", err)
    39  	}
    40  
    41  	req, err := http.NewRequest(method, baseURL+endpoint, &buf)
    42  	if err != nil {
    43  		return fmt.Errorf("error building request: %w", err)
    44  	}
    45  	req.Header.Add("Content-Type", "application/json")
    46  	req.Header.Add("x-goog-api-key", c.apiKey)
    47  
    48  	resp, err := c.client.Do(req)
    49  	if err != nil {
    50  		return fmt.Errorf("error sending request: %w", err)
    51  	}
    52  	defer resp.Body.Close()
    53  
    54  	body, err := io.ReadAll(resp.Body)
    55  	if err != nil {
    56  		return fmt.Errorf("error reading response body: %w", err)
    57  	}
    58  
    59  	if resp.StatusCode != http.StatusOK {
    60  		return fmt.Errorf("response error %s: %s", resp.Status, body)
    61  	}
    62  
    63  	if err := json.Unmarshal(body, output); err != nil {
    64  		return fmt.Errorf("error decoding response: %w; body: %s", err, body)
    65  	}
    66  
    67  	return nil
    68  }
    69  
    70  // Lease creates a new lease.
    71  func (c *Client) Lease(req LeaseRequest) (LeaseResponse, error) {
    72  	var resp LeaseResponse
    73  	if err := c.do("POST", "leases:create", req, &resp); err != nil {
    74  		return LeaseResponse{}, fmt.Errorf("error sending request: %w", err)
    75  	}
    76  	return resp, nil
    77  }
    78  
    79  // Renew updates the expiration time of a lease. Note that
    80  // RenewRequest.Duration is the lease duration from now, not from the current
    81  // lease expiration time.
    82  func (c *Client) Renew(req RenewRequest) (RenewResponse, error) {
    83  	var resp RenewResponse
    84  	if err := c.do("POST", "leases:renew", req, &resp); err != nil {
    85  		return RenewResponse{}, fmt.Errorf("error sending request: %w", err)
    86  	}
    87  	return resp, nil
    88  }
    89  
    90  // Vacate vacates a lease.
    91  func (c *Client) Vacate(req VacateRequest) error {
    92  	var resp struct{} // no response body
    93  	if err := c.do("POST", "leases:vacate", req, &resp); err != nil {
    94  		return fmt.Errorf("error sending request: %w", err)
    95  	}
    96  	return nil
    97  }
    98  
    99  // Find searches for leases.
   100  func (c *Client) Find(req FindRequest) (FindResponse, error) {
   101  	var resp FindResponse
   102  	if err := c.do("POST", "leases:find", req, &resp); err != nil {
   103  		return FindResponse{}, fmt.Errorf("error sending request: %w", err)
   104  	}
   105  	return resp, nil
   106  }