github.com/tonto/cli@v0.0.0-20180104210444-aec958fa47db/client/call_fn.go (about)

     1  package client
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"io"
     7  	"net/http"
     8  	"os"
     9  	"strings"
    10  )
    11  
    12  const FN_CALL_ID = "Fn_call_id"
    13  
    14  func EnvAsHeader(req *http.Request, selectedEnv []string) {
    15  	detectedEnv := os.Environ()
    16  	if len(selectedEnv) > 0 {
    17  		detectedEnv = selectedEnv
    18  	}
    19  
    20  	for _, e := range detectedEnv {
    21  		kv := strings.Split(e, "=")
    22  		name := kv[0]
    23  		req.Header.Set(name, os.Getenv(name))
    24  	}
    25  }
    26  
    27  type apiErr struct {
    28  	Message string `json:"message"`
    29  }
    30  
    31  type callID struct {
    32  	CallID string `json:"call_id"`
    33  	Error  apiErr `json:"error"`
    34  }
    35  
    36  func CallFN(u string, content io.Reader, output io.Writer, method string, env []string, includeCallID bool) error {
    37  	if method == "" {
    38  		if content == nil {
    39  			method = "GET"
    40  		} else {
    41  			method = "POST"
    42  		}
    43  	}
    44  
    45  	req, err := http.NewRequest(method, u, content)
    46  	if err != nil {
    47  		return fmt.Errorf("error running route: %s", err)
    48  	}
    49  
    50  	req.Header.Set("Content-Type", "application/json")
    51  
    52  	if len(env) > 0 {
    53  		EnvAsHeader(req, env)
    54  	}
    55  
    56  	resp, err := http.DefaultClient.Do(req)
    57  	if err != nil {
    58  		return fmt.Errorf("error running route: %s", err)
    59  	}
    60  	// for sync calls
    61  	if call_id, found := resp.Header[FN_CALL_ID]; found {
    62  		if includeCallID {
    63  			fmt.Fprint(os.Stderr, fmt.Sprintf("Call ID: %v\n", call_id[0]))
    64  		}
    65  		io.Copy(output, resp.Body)
    66  	} else {
    67  		// for async calls and error discovering
    68  		c := &callID{}
    69  		err = json.NewDecoder(resp.Body).Decode(c)
    70  		if err == nil {
    71  			// decode would not fail in both cases:
    72  			// - call id in body
    73  			// - error in body
    74  			// that's why we need to check values of attributes
    75  			if c.CallID != "" {
    76  				fmt.Fprint(os.Stderr, fmt.Sprintf("Call ID: %v\n", c.CallID))
    77  			} else {
    78  				fmt.Fprint(output, fmt.Sprintf("Error: %v\n", c.Error.Message))
    79  			}
    80  		} else {
    81  			return err
    82  		}
    83  	}
    84  
    85  	if resp.StatusCode >= 400 {
    86  		// TODO: parse out error message
    87  		return fmt.Errorf("error calling function: status %v", resp.StatusCode)
    88  	}
    89  
    90  	return nil
    91  }