github.com/wawandco/ox@v0.13.6-0.20230809142027-913b3d837f2a/pkg/buffalotools/database_middleware.go (about)

     1  package buffalotools
     2  
     3  import (
     4  	"errors"
     5  	"net/http"
     6  	"time"
     7  
     8  	"github.com/gobuffalo/buffalo"
     9  	"github.com/gobuffalo/pop/v6"
    10  )
    11  
    12  var errNonSuccess = errors.New("non success status code")
    13  
    14  // DatabaseMiddleware returns a database connection provider middleware
    15  // that will add the db into the `tx` context variable, in case its a
    16  // PUT/POST/DELETE/PATCH method, it will wrap the
    17  // handler in a transaction, otherwise it will just
    18  // add the connection in the context.
    19  //
    20  // Provided middleware can also consider a seccond read only replica
    21  // database connection (rodb) that will be used on idempotent operations if present.
    22  func DatabaseMiddleware(db, rodb *pop.Connection) buffalo.MiddlewareFunc {
    23  	return func(h buffalo.Handler) buffalo.Handler {
    24  		return func(c buffalo.Context) error {
    25  
    26  			// Setting the context on the DB.
    27  			dbc := db.WithContext(c)
    28  
    29  			switch c.Request().Method {
    30  			default:
    31  				// If the passed read only db (rodb) is different than nil it should use it.
    32  				// As this block is for read only operations.
    33  				if rodb != nil {
    34  					dbc = rodb.WithContext(c)
    35  				}
    36  
    37  				c.Set("tx", dbc)
    38  
    39  				return h(c)
    40  			case http.MethodPost, http.MethodPut, http.MethodDelete, http.MethodPatch:
    41  				couldBeDBorYourErr := dbc.Transaction(func(tx *pop.Connection) error {
    42  					// setup logging
    43  					start := tx.Elapsed
    44  					defer func() {
    45  						finished := tx.Elapsed
    46  						elapsed := time.Duration(finished - start)
    47  						c.LogField("db", elapsed)
    48  					}()
    49  
    50  					// add the transaction to the context
    51  					c.Set("tx", tx)
    52  
    53  					// call the next handler; if it errors stop and return the error
    54  					if yourError := h(c); yourError != nil {
    55  						return yourError
    56  					}
    57  
    58  					// check the response status code. if the code is NOT 200..399
    59  					// then it is considered "NOT SUCCESSFUL" and an error will be returned
    60  					if res, ok := c.Response().(*buffalo.Response); ok {
    61  						if res.Status < 200 || res.Status >= 400 {
    62  							return errNonSuccess
    63  						}
    64  					}
    65  
    66  					// as far was we can tell everything went well
    67  					return nil
    68  				})
    69  
    70  				// couldBeDBorYourErr could be one of possible values:
    71  				// * nil - everything went well, if so, return
    72  				// * yourError - an error returned from your application, middleware, etc...
    73  				// * a database error - this is returned if there were problems committing the transaction
    74  				// * a errNonSuccess - this is returned if the response status code is not between 200..399
    75  				if couldBeDBorYourErr != nil && !errors.Is(couldBeDBorYourErr, errNonSuccess) {
    76  					return couldBeDBorYourErr
    77  				}
    78  
    79  				return nil
    80  			}
    81  		}
    82  	}
    83  }