github.com/jasonkeene/cli@v6.14.1-0.20160816203908-ca5715166dfb+incompatible/cf/commands/curl.go (about)

     1  package commands
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"errors"
     7  	"fmt"
     8  	"io/ioutil"
     9  	"os"
    10  	"path/filepath"
    11  	"strings"
    12  
    13  	"github.com/cloudfoundry/cli/cf/flags"
    14  	. "github.com/cloudfoundry/cli/cf/i18n"
    15  	"github.com/cloudfoundry/cli/cf/util"
    16  
    17  	"github.com/cloudfoundry/cli/cf/api"
    18  	"github.com/cloudfoundry/cli/cf/commandregistry"
    19  	"github.com/cloudfoundry/cli/cf/configuration/coreconfig"
    20  	"github.com/cloudfoundry/cli/cf/requirements"
    21  	"github.com/cloudfoundry/cli/cf/terminal"
    22  	"github.com/cloudfoundry/cli/cf/trace"
    23  )
    24  
    25  type Curl struct {
    26  	ui         terminal.UI
    27  	config     coreconfig.Reader
    28  	curlRepo   api.CurlRepository
    29  	pluginCall bool
    30  }
    31  
    32  func init() {
    33  	commandregistry.Register(&Curl{})
    34  }
    35  
    36  func (cmd *Curl) MetaData() commandregistry.CommandMetadata {
    37  	fs := make(map[string]flags.FlagSet)
    38  	fs["i"] = &flags.BoolFlag{ShortName: "i", Usage: T("Include response headers in the output")}
    39  	fs["X"] = &flags.StringFlag{ShortName: "X", Usage: T("HTTP method (GET,POST,PUT,DELETE,etc)")}
    40  	fs["H"] = &flags.StringSliceFlag{ShortName: "H", Usage: T("Custom headers to include in the request, flag can be specified multiple times")}
    41  	fs["d"] = &flags.StringFlag{ShortName: "d", Usage: T("HTTP data to include in the request body, or '@' followed by a file name to read the data from")}
    42  	fs["output"] = &flags.StringFlag{Name: "output", Usage: T("Write curl body to FILE instead of stdout")}
    43  
    44  	return commandregistry.CommandMetadata{
    45  		Name:        "curl",
    46  		Description: T("Executes a request to the targeted API endpoint"),
    47  		Usage: []string{
    48  			T(`CF_NAME curl PATH [-iv] [-X METHOD] [-H HEADER] [-d DATA] [--output FILE]
    49  
    50     By default 'CF_NAME curl' will perform a GET to the specified PATH. If data
    51     is provided via -d, a POST will be performed instead, and the Content-Type
    52     will be set to application/json. You may override headers with -H and the
    53     request method with -X.
    54  
    55     For API documentation, please visit http://apidocs.cloudfoundry.org.`),
    56  		},
    57  		Examples: []string{
    58  			`CF_NAME curl "/v2/apps" -X GET -H "Content-Type: application/x-www-form-urlencoded" -d 'q=name:myapp'`,
    59  			`CF_NAME curl "/v2/apps" -d @/path/to/file`,
    60  		},
    61  		Flags: fs,
    62  	}
    63  }
    64  
    65  func (cmd *Curl) Requirements(requirementsFactory requirements.Factory, fc flags.FlagContext) ([]requirements.Requirement, error) {
    66  	if len(fc.Args()) != 1 {
    67  		cmd.ui.Failed(T("Incorrect Usage. An argument is missing or not correctly enclosed.\n\n") + commandregistry.Commands.CommandUsage("curl"))
    68  		return nil, fmt.Errorf("Incorrect usage: %d arguments of %d required", len(fc.Args()), 1)
    69  	}
    70  
    71  	reqs := []requirements.Requirement{
    72  		requirementsFactory.NewAPIEndpointRequirement(),
    73  	}
    74  
    75  	return reqs, nil
    76  }
    77  
    78  func (cmd *Curl) SetDependency(deps commandregistry.Dependency, pluginCall bool) commandregistry.Command {
    79  	cmd.ui = deps.UI
    80  	cmd.config = deps.Config
    81  	cmd.curlRepo = deps.RepoLocator.GetCurlRepository()
    82  	cmd.pluginCall = pluginCall
    83  	return cmd
    84  }
    85  
    86  func (cmd *Curl) Execute(c flags.FlagContext) error {
    87  	path := c.Args()[0]
    88  	headers := c.StringSlice("H")
    89  
    90  	var method string
    91  	var body string
    92  
    93  	if c.IsSet("d") {
    94  		method = "POST"
    95  
    96  		jsonBytes, err := util.GetContentsFromOptionalFlagValue(c.String("d"))
    97  		if err != nil {
    98  			return err
    99  		}
   100  		body = string(jsonBytes)
   101  	}
   102  
   103  	if c.IsSet("X") {
   104  		method = c.String("X")
   105  	}
   106  
   107  	reqHeader := strings.Join(headers, "\n")
   108  
   109  	responseHeader, responseBody, apiErr := cmd.curlRepo.Request(method, path, reqHeader, body)
   110  	if apiErr != nil {
   111  		return errors.New(T("Error creating request:\n{{.Err}}", map[string]interface{}{"Err": apiErr.Error()}))
   112  	}
   113  
   114  	if trace.LoggingToStdout && !cmd.pluginCall {
   115  		return nil
   116  	}
   117  
   118  	if c.Bool("i") {
   119  		cmd.ui.Say(responseHeader)
   120  	}
   121  
   122  	if c.String("output") != "" {
   123  		err := cmd.writeToFile(responseBody, c.String("output"))
   124  		if err != nil {
   125  			return errors.New(T("Error creating request:\n{{.Err}}", map[string]interface{}{"Err": err}))
   126  		}
   127  	} else {
   128  		if strings.Contains(responseHeader, "application/json") {
   129  			buffer := bytes.Buffer{}
   130  			err := json.Indent(&buffer, []byte(responseBody), "", "   ")
   131  			if err == nil {
   132  				responseBody = buffer.String()
   133  			}
   134  		}
   135  
   136  		cmd.ui.Say(responseBody)
   137  	}
   138  	return nil
   139  }
   140  
   141  func (cmd Curl) writeToFile(responseBody, filePath string) (err error) {
   142  	if _, err = os.Stat(filePath); os.IsNotExist(err) {
   143  		err = os.MkdirAll(filepath.Dir(filePath), 0755)
   144  	}
   145  
   146  	if err != nil {
   147  		return
   148  	}
   149  
   150  	return ioutil.WriteFile(filePath, []byte(responseBody), 0644)
   151  }