github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/api/base/caller.go (about)

     1  // Copyright 2012, 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package base
     5  
     6  import (
     7  	"io"
     8  	"net/http"
     9  	"net/url"
    10  
    11  	"github.com/juju/httprequest"
    12  	"gopkg.in/juju/names.v2"
    13  	"gopkg.in/macaroon-bakery.v2-unstable/httpbakery"
    14  )
    15  
    16  //go:generate mockgen -package mocks -destination mocks/caller_mock.go github.com/juju/juju/api/base APICaller,FacadeCaller
    17  
    18  // APICaller is implemented by the client-facing State object.
    19  // It defines the lowest level of API calls and is used by
    20  // the various API implementations to actually make
    21  // the calls to the API. It should not be used outside
    22  // of tests or the api/* hierarchy.
    23  type APICaller interface {
    24  	// APICall makes a call to the API server with the given object type,
    25  	// id, request and parameters. The response is filled in with the
    26  	// call's result if the call is successful.
    27  	APICall(objType string, version int, id, request string, params, response interface{}) error
    28  
    29  	// BestFacadeVersion returns the newest version of 'objType' that this
    30  	// client can use with the current API server.
    31  	BestFacadeVersion(facade string) int
    32  
    33  	// ModelTag returns the tag of the model the client is connected
    34  	// to if there is one. It returns false for a controller-only connection.
    35  	ModelTag() (names.ModelTag, bool)
    36  
    37  	// HTTPClient returns an httprequest.Client that can be used
    38  	// to make HTTP requests to the API. URLs passed to the client
    39  	// will be made relative to the API host and the current model.
    40  	//
    41  	// Note that the URLs in HTTP requests passed to the Client.Do
    42  	// method should not include a host part.
    43  	HTTPClient() (*httprequest.Client, error)
    44  
    45  	// BakeryClient returns the bakery client for this connection.
    46  	BakeryClient() *httpbakery.Client
    47  
    48  	StreamConnector
    49  	ControllerStreamConnector
    50  }
    51  
    52  // StreamConnector is implemented by the client-facing State object.
    53  type StreamConnector interface {
    54  	// ConnectStream connects to the given HTTP websocket
    55  	// endpoint path (interpreted relative to the receiver's
    56  	// model) and returns the resulting connection.
    57  	// The given parameters are used as URL query values
    58  	// when making the initial HTTP request.
    59  	//
    60  	// The path must start with a "/".
    61  	ConnectStream(path string, attrs url.Values) (Stream, error)
    62  }
    63  
    64  // ControllerStreamConnector is implemented by the client-facing State object.
    65  type ControllerStreamConnector interface {
    66  	// ConnectControllerStream connects to the given HTTP websocket
    67  	// endpoint path and returns the resulting connection. The given
    68  	// values are used as URL query values when making the initial
    69  	// HTTP request. Headers passed in will be added to the HTTP
    70  	// request.
    71  	//
    72  	// The path must be absolute and can't start with "/model".
    73  	ConnectControllerStream(path string, attrs url.Values, headers http.Header) (Stream, error)
    74  }
    75  
    76  // Stream represents a streaming connection to the API.
    77  type Stream interface {
    78  	io.Closer
    79  
    80  	// NextReader is used to get direct access to the underlying Read methods
    81  	// on the websocket. Mostly just to read the initial error resonse.
    82  	NextReader() (messageType int, r io.Reader, err error)
    83  
    84  	// WriteJSON encodes the given value as JSON
    85  	// and writes it to the connection.
    86  	WriteJSON(v interface{}) error
    87  
    88  	// ReadJSON reads a JSON value from the stream
    89  	// and decodes it into the element pointed to by
    90  	// the given value, which should be a pointer.
    91  	ReadJSON(v interface{}) error
    92  }
    93  
    94  // FacadeCaller is a wrapper for the common paradigm that a given client just
    95  // wants to make calls on a facade using the best known version of the API. And
    96  // without dealing with an id parameter.
    97  type FacadeCaller interface {
    98  	// FacadeCall will place a request against the API using the requested
    99  	// Facade and the best version that the API server supports that is
   100  	// also known to the client.
   101  	FacadeCall(request string, params, response interface{}) error
   102  
   103  	// Name returns the facade name.
   104  	Name() string
   105  
   106  	// BestAPIVersion returns the API version that we were able to
   107  	// determine is supported by both the client and the API Server
   108  	BestAPIVersion() int
   109  
   110  	// RawAPICaller returns the wrapped APICaller. This can be used if you need
   111  	// to switch what Facade you are calling (such as Facades that return
   112  	// Watchers and then need to use the Watcher facade)
   113  	RawAPICaller() APICaller
   114  }
   115  
   116  type facadeCaller struct {
   117  	facadeName  string
   118  	bestVersion int
   119  	caller      APICaller
   120  }
   121  
   122  var _ FacadeCaller = facadeCaller{}
   123  
   124  // FacadeCall will place a request against the API using the requested
   125  // Facade and the best version that the API server supports that is
   126  // also known to the client. (id is always passed as the empty string.)
   127  func (fc facadeCaller) FacadeCall(request string, params, response interface{}) error {
   128  	return fc.caller.APICall(
   129  		fc.facadeName, fc.bestVersion, "",
   130  		request, params, response)
   131  }
   132  
   133  // Name returns the facade name.
   134  func (fc facadeCaller) Name() string {
   135  	return fc.facadeName
   136  }
   137  
   138  // BestAPIVersion returns the version of the Facade that is going to be used
   139  // for calls. It is determined using the algorithm defined in api
   140  // BestFacadeVersion. Callers can use this to determine what methods must be
   141  // used for compatibility.
   142  func (fc facadeCaller) BestAPIVersion() int {
   143  	return fc.bestVersion
   144  }
   145  
   146  // RawAPICaller returns the wrapped APICaller. This can be used if you need to
   147  // switch what Facade you are calling (such as Facades that return Watchers and
   148  // then need to use the Watcher facade)
   149  func (fc facadeCaller) RawAPICaller() APICaller {
   150  	return fc.caller
   151  }
   152  
   153  // NewFacadeCaller wraps an APICaller for a given facade name and the
   154  // best available version.
   155  func NewFacadeCaller(caller APICaller, facadeName string) FacadeCaller {
   156  	return NewFacadeCallerForVersion(caller, facadeName, caller.BestFacadeVersion(facadeName))
   157  }
   158  
   159  // NewFacadeCallerForVersion wraps an APICaller for a given facade
   160  // name and version.
   161  func NewFacadeCallerForVersion(caller APICaller, facadeName string, version int) FacadeCaller {
   162  	return facadeCaller{
   163  		facadeName:  facadeName,
   164  		bestVersion: version,
   165  		caller:      caller,
   166  	}
   167  }