github.com/moov-io/imagecashletter@v0.10.1/internal/responder/responder.go (about)

     1  package responder
     2  
     3  import (
     4  	"encoding/json"
     5  	"net/http"
     6  
     7  	"github.com/moov-io/base/log"
     8  	"github.com/moov-io/imagecashletter"
     9  	client "github.com/moov-io/imagecashletter/client"
    10  )
    11  
    12  // Responder is a helper for writing responses to an http.ResponseWriter.
    13  type Responder struct {
    14  	logger log.Logger
    15  	w      http.ResponseWriter
    16  	r      *http.Request
    17  	optionalHeaders
    18  }
    19  
    20  type optionalHeaders struct {
    21  	location string
    22  }
    23  
    24  func (h optionalHeaders) apply(w http.ResponseWriter) {
    25  	if h.location != "" {
    26  		w.Header().Set("Location", h.location)
    27  	}
    28  }
    29  
    30  func NewResponder(logger log.Logger, w http.ResponseWriter, r *http.Request) *Responder {
    31  	return &Responder{
    32  		logger: logger,
    33  		w:      w,
    34  		r:      r,
    35  	}
    36  }
    37  
    38  // WithLocation sets the Location header on the response. The Location header should be used for
    39  // HTTP 201 responses to indicate the location of the newly created resource. While both absolute
    40  // and relative URIs are allowed, the absolute URI is preferred.
    41  func (r *Responder) WithLocation(location string) *Responder {
    42  	r.optionalHeaders.location = location
    43  	return r
    44  }
    45  
    46  // File writes the file to the http.ResponseWriter as an attachment. It should
    47  // only be called when the request's Accept header was "application/octet-stream" or "text/plain".
    48  func (r *Responder) File(status int, file imagecashletter.File, name string) {
    49  	opts := []imagecashletter.WriterOption{
    50  		imagecashletter.WriteVariableLineLengthOption(),
    51  	}
    52  
    53  	// determine which encoding the caller expects to set up the writer and response headers
    54  	mimeType := r.r.Header.Get("Accept")
    55  	switch mimeType {
    56  	case "application/octet-stream":
    57  		r.w.Header().Set("Content-Type", "application/octet-stream")
    58  		opts = append(opts, imagecashletter.WriteEbcdicEncodingOption())
    59  	case "text/plain":
    60  		r.w.Header().Set("Content-Type", "text/plain")
    61  	default:
    62  		r.logger.LogErrorf("renderer: file method called for unsupported mime-type: %s", mimeType)
    63  		r.w.WriteHeader(http.StatusInternalServerError)
    64  		return
    65  	}
    66  
    67  	r.optionalHeaders.apply(r.w)
    68  	r.w.Header().Set("Content-Disposition", "attachment; filename="+name)
    69  	r.w.WriteHeader(status)
    70  
    71  	if err := imagecashletter.NewWriter(r.w, opts...).Write(&file); err != nil {
    72  		r.logger.LogErrorf("rendering file: %v", err)
    73  		r.w.WriteHeader(http.StatusInternalServerError)
    74  		return
    75  	}
    76  }
    77  
    78  // JSON writes the resource to the http.ResponseWriter as JSON.
    79  func (r *Responder) JSON(status int, resource any) {
    80  	r.optionalHeaders.apply(r.w)
    81  	r.w.Header().Set("Content-Type", "application/json; charset=UTF-8")
    82  	r.w.WriteHeader(status)
    83  	if err := json.NewEncoder(r.w).Encode(resource); err != nil {
    84  		r.logger.LogErrorf("problem encoding response: %v", err)
    85  		r.w.WriteHeader(http.StatusInternalServerError)
    86  		return
    87  	}
    88  }
    89  
    90  // Error sets the status code and writes an error to the http.ResponseWriter. Response body is
    91  // omitted for 5xx errors.
    92  func (r *Responder) Error(status int, err error) {
    93  	if status >= 500 { // don't return body for internal errors
    94  		r.w.WriteHeader(status)
    95  		return
    96  	}
    97  
    98  	r.w.Header().Set("Content-Type", "application/json; charset=UTF-8")
    99  	r.w.WriteHeader(status)
   100  	if err := json.NewEncoder(r.w).Encode(client.Error{Error: err.Error()}); err != nil {
   101  		r.logger.LogErrorf("problem encoding response: %v", err)
   102  		r.w.WriteHeader(http.StatusInternalServerError)
   103  		return
   104  	}
   105  }