github.com/percona-lab/go-swagger@v0.19.0/generator/templates/contrib/stratoscale/README.md (about)

     1  # swagger
     2  
     3  In Stratoscale, we really like the idea of API-first services, and we also really like Go.
     4  We saw the go-swagger library, and thought that most of it can really help us. Generating code from
     5  swagger files is a big problem with a lot of corner cases, and go-swagger is doing great job.
     6  
     7  The one thing that we felt missing, is customization of the server to run with our design principles:
     8  
     9  * Custom `main` function
    10  * Dependency injection
    11  * Limited scopes with unit testing.
    12  
    13  Also:
    14  
    15  * Adding you functions to the generated `configure_swagger_*.go` seems to be a burden.
    16  * Lack of Interface that the service implement.
    17  * Complicated and custom http clients and runtime.
    18  
    19  Those are the changes that this contributor templates are providing:
    20  
    21  ## Server
    22  
    23  ### The new `restapi` package exposes interfaces
    24  
    25  * Those interfaces can implemented by the developer and are the business logic of the service.
    26  * The implementation of those is extensible.
    27  * The implementation is separated from the generated code.
    28  
    29  ### The `restapi` returns an `http.Handler`
    30  
    31  The `restapi.Handler` (see [example](./example/restapi/configure_swagger_petstore.go)) function returns
    32  a standard `http.Handler`
    33  
    34  * Given objects that implements the business logic, we can create a simple http handler.
    35  * This handler is standard go http.Handler, so we can now use any other middleware, library, or framework
    36    that support it.
    37  * This handler is standard, so we understand it better.
    38  
    39  ## Client
    40  
    41  * The new client package exposes interfaces, so functions in our code can receive those
    42    interfaces which can be mocked for testing.
    43  * The new client has a config that gets an `*url.URL` to customize the endpoint.
    44  * The new client has a config that gets an `http.RoundTripper` to customize client with libraries, middleware or
    45    frameworks that support the standard library's objects.
    46  
    47  # Example Walk-Through
    48  
    49  In the [example package](https://github.com/Stratoscale/swagger/tree/master/example) you'll find generated code and usage of the pet-store
    50  [swagger file](./example/swagger.yaml).
    51  
    52  * The `restapi`, `models` and `client` are auto-generated by the stratoscale/swagger docker file.
    53  * The `internal` package was manually added and contains the server's business logic.
    54  * The `main.go` file is the entrypoint and contains initializations and dependency injections of the project.
    55  
    56  ## Server
    57  
    58  ### [restapi](https://github.com/Stratoscale/swagger/tree/master/example/restapi)
    59  
    60  This package is autogenerated and contains the server routing and parameters parsing.
    61  
    62  The modified version contains `restapi.PetAPI` and `restapi.StoreAPI` which were auto generated.
    63  
    64  ```go
    65  // PetAPI
    66  type PetAPI interface {
    67  	PetCreate(ctx context.Context, params pet.PetCreateParams) middleware.Responder
    68  	PetDelete(ctx context.Context, params pet.PetDeleteParams) middleware.Responder
    69  	PetGet(ctx context.Context, params pet.PetGetParams) middleware.Responder
    70  	PetList(ctx context.Context, params pet.PetListParams) middleware.Responder
    71  	PetUpdate(ctx context.Context, params pet.PetUpdateParams) middleware.Responder
    72  }
    73  
    74  //go:generate mockery -name StoreAPI -inpkg
    75  
    76  // StoreAPI
    77  type StoreAPI interface {
    78  	InventoryGet(ctx context.Context, params store.InventoryGetParams) middleware.Responder
    79  	OrderCreate(ctx context.Context, params store.OrderCreateParams) middleware.Responder
    80  	// OrderDelete is For valid response try integer IDs with positive integer value. Negative or non-integer values will generate API errors
    81  	OrderDelete(ctx context.Context, params store.OrderDeleteParams) middleware.Responder
    82  	// OrderGet is For valid response try integer IDs with value >= 1 and <= 10. Other values will generated exceptions
    83  	OrderGet(ctx context.Context, params store.OrderGetParams) middleware.Responder
    84  }
    85  ```
    86  
    87  Each function matches an `operationId` in the swagger file and they are grouped according to
    88  the operation `tags`.
    89  
    90  There is also a `restapi.Config`:
    91  
    92  ```go
    93  // Config is configuration for Handler
    94  type Config struct {
    95  	PetAPI
    96  	StoreAPI
    97  	Logger func(string, ...interface{})
    98  	// InnerMiddleware is for the handler executors. These do not apply to the swagger.json document.
    99  	// The middleware executes after routing but before authentication, binding and validation
   100  	InnerMiddleware func(http.Handler) http.Handler
   101  }
   102  ```
   103  
   104  This config is auto generated and contains all the declared interfaces above.
   105  It is used to initiate an http.Handler with the `Handler` function:
   106  
   107  ```go
   108  // Handler returns an http.Handler given the handler configuration
   109  // It mounts all the business logic implementers in the right routing.
   110  func Handler(c Config) (http.Handler, error) {
   111      ...
   112  ```
   113  
   114  Let's look how we use this generated code to build our server.
   115  
   116  ### [internal](https://github.com/Stratoscale/swagger/tree/master/example/internal)
   117  
   118  The `internal` package is **not** auto generated and contains the business logic of our server.
   119  We can see two structs that implements the `restapi.PetAPI` and `restapi.StoreAPI` interfaces,
   120  needed to make our server work.
   121  
   122  When adding or removing functions from our REST API, we can just add or remove functions to those
   123  business logic units. We can also create new logical units when they are added to our REST API.
   124  
   125  ### [main.go](./example/main.go)
   126  
   127  The main function is pretty straight forward. We initiate our business logic units.
   128  Then create a config for our rest API. We then create a standard `http.Handler` which we can
   129  update with middleware, test with `httptest`, or to use with other standard tools.
   130  The last piece is to run the handler with `http.ListenAndServe` or to use it with an `http.Server` -
   131  it is all very customizable.
   132  
   133  ```go
   134  func main() {
   135  	// Initiate business logic implementers.
   136  	// This is the main function, so here the implementers' dependencies can be
   137  	// injected, such as database, parameters from environment variables, or different
   138  	// clients for different APIs.
   139  	p := internal.Pet{}
   140  	s := internal.Store{}
   141  
   142  	// Initiate the http handler, with the objects that are implementing the business logic.
   143  	h, err := restapi.Handler(restapi.Config{
   144  		PetAPI:   &p,
   145  		StoreAPI: &s,
   146  		Logger:   log.Printf,
   147  	})
   148  	if err != nil {
   149  		log.Fatal(err)
   150  	}
   151  
   152  	// Run the standard http server
   153  	log.Fatal(http.ListenAndServe(":8080", h))
   154  }
   155  ```
   156  
   157  ## Client
   158  
   159  The client code is in the [client package](https://github.com/Stratoscale/swagger/tree/master/example/client) and is autogenerated.
   160  
   161  To create a new client we use the `client.Config` struct:
   162  
   163  ```go
   164  type Config struct {
   165  	// URL is the base URL of the upstream server
   166  	URL *url.URL
   167  	// Transport is an inner transport for the client
   168  	Transport http.RoundTripper
   169  }
   170  ```
   171  
   172  This enables us to use custom server endpoint or custom client middleware. Easily, with the
   173  standard components, and with any library that accepts them.
   174  
   175  The client is then generated with the New method:
   176  
   177  ```go
   178  // New creates a new swagger petstore HTTP client.
   179  func New(c Config) *SwaggerPetstore { ... }
   180  ```
   181  
   182  This method returns an object that has two important fields:
   183  
   184  ```go
   185  type SwaggerPetstore {
   186  	...
   187  	Pet       *pet.Client
   188  	Store     *store.Client
   189  }
   190  ```
   191  
   192  Thos fields are objects, which implements interfaces declared in the [pet](./example/client/pet) and
   193  [store](./example/client/store) packages:
   194  
   195  For example:
   196  
   197  ```go
   198  // API is the interface of the pet client
   199  type API interface {
   200  	// PetCreate adds a new pet to the store
   201  	PetCreate(ctx context.Context, params *PetCreateParams) (*PetCreateCreated, error)
   202  	// PetDelete deletes a pet
   203  	PetDelete(ctx context.Context, params *PetDeleteParams) (*PetDeleteNoContent, error)
   204  	// PetGet gets pet by it s ID
   205  	PetGet(ctx context.Context, params *PetGetParams) (*PetGetOK, error)
   206  	// PetList lists pets
   207  	PetList(ctx context.Context, params *PetListParams) (*PetListOK, error)
   208  	// PetUpdate updates an existing pet
   209  	PetUpdate(ctx context.Context, params *PetUpdateParams) (*PetUpdateCreated, error)
   210  }
   211  ```
   212  
   213  They are very similar to the server interfaces, and can be used by consumers of those APIs
   214  (instead of using the actual client or the `*Pet` struct)
   215  
   216  # Authentication
   217  
   218  Authenticating and policy enforcement of the application is done in several stages, described below.
   219  
   220  ## Define security in swagger.yaml
   221  
   222  Add to the root of the swagger.yaml the security and security definitions sections.
   223  
   224  ```yaml
   225  securityDefinitions:
   226    token:
   227      type: apiKey
   228      in: header
   229      name: Cookie
   230  
   231  security:
   232    - token: []
   233  ```
   234  
   235  The securityDefinitions section defines different security types that your application can handle.
   236  The supported types by go-swagger are:
   237  * `apiKey` - token that should be able to processed.
   238  * `oauth2` - token and scopes that should be processed. 
   239  * and `basic` - user/password that should be processed.
   240  
   241  Here we defined an apiKey, that is passed through the Cookie header.
   242  
   243  The `security` section defines the default security enforcement for the application. You can select
   244  different securityDefinitions, as the keys, and apply "scopes" as the values. Those default definitions
   245  can be overriden in each route by a section with the same name:
   246  
   247  ```yaml
   248  paths:
   249    /pets:
   250      post:
   251        [...]
   252        security:
   253          - token: [admin]
   254  ```
   255  
   256  Here we overriden the scope of token in the POST /pets URL so that only admin can use this API.
   257  
   258  Let's see how we can use this functionality.
   259  
   260  ## Writing Security Handlers
   261  
   262  Once we created a security definition named "token", a function called "AuthToken" was added to the `restapi.Config`:
   263  
   264  ```go
   265  type Config struct {
   266      ...
   267  	// AuthToken Applies when the "Cookie" header is set
   268  	AuthToken func(token string) (interface{}, error)
   269  }
   270  ```
   271  
   272  This function gets the content of the Cookie header, and should return an `interface{}` and `error`.
   273  The `interface{}` is the object that should represent the user that performed the request, it should
   274  be nil to return an unauthorized 401 HTTP response. If the returned `error` is not nil, an HTTP 500,
   275  internal server error will be returned.
   276  
   277  The returned object, will be stored in the request context under the `restapi.AuthKey` key.
   278  
   279  There is another function that we should know about, in the `restapi.Config` struct:
   280  
   281  ```go
   282  type Config struct {
   283  	...
   284  	// Authorizer is used to authorize a request after the Auth function was called using the "Auth*" functions
   285  	// and the principal was stored in the context in the "AuthKey" context value.
   286  	Authorizer func(*http.Request) error
   287  }
   288  ```
   289  
   290  This one is a custom defined function that gets the request and can return an error.
   291  If the returned error is not nil, and 403 HTTP error will be returned to the client - here the policy
   292  enforcement comes to place.
   293  There are two things that this function should be aware of:
   294  
   295  1. The user - it can retrieve the user information from the context: `ctx.Value(restapi.AuthKey).(MyUserType)`.
   296     Usually, a server will have a function for extracting this user information and returns a concrete
   297     type which could be used by all the routes.
   298  2. The route - it can retrieve the route using the go-swagger function: `middleware.MatchedRouteFrom(*http.Request)`.
   299     So no need to parse URL and test the request method.
   300     This route struct contains the route information. If for example, we want to check the scopes that were
   301     defined for the current route in the swagger.yaml we can use the code below:
   302     
   303  ```go
   304  for _, auth := range route.Authenticators {
   305      for scopeName, scopeValues := range auth.Scopes {
   306          for _, scopeValue := range scopeValues {
   307              ...
   308          }
   309      }
   310  }
   311  ```