func WithSuccess(ctx context.Context, msg MessageWithFields) {
	i := atomic.AddUint32(counter, uint32(time.Now().Nanosecond()%100+1))
	md := metadata.Pairs(fmt.Sprintf("success-%d", i), fmt.Sprintf("message:%s", msg.Error()))
	if f := msg.GetFields(); f != nil {
		b, _ := json.Marshal(msg.GetFields())
		md.Append(fmt.Sprintf("success-%d", i), fmt.Sprintf("fields:%q", b))
	}
	grpc.SetTrailer(ctx, md)
}
WithSuccess will save a MessageWithFields into the grpc trailer metadata.
This success message will then be inserted into the return JSON if the
ResponseForwarder is used
package gateway
import (
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"math"
	"net/http"
	"strconv"
	"strings"
	"sync/atomic"
	"time"
	"google.golang.org/grpc"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/grpclog"
	"google.golang.org/grpc/metadata"
	"google.golang.org/grpc/status"
	"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
	"github.com/infobloxopen/atlas-app-toolkit/rpc/errdetails"
	"github.com/infobloxopen/atlas-app-toolkit/rpc/errfields"
)
type ProtoStreamErrorHandlerFunc func(context.Context, bool, *runtime.ServeMux, runtime.Marshaler, http.ResponseWriter, *http.Request, error)
type RestErrs struct {
	Error []map[string]interface{} `json:"error,omitempty"`
}
var (
	ProtoMessageErrorHandler = NewProtoMessageErrorHandler(PrefixOutgoingHeaderMatcher)
	ProtoStreamErrorHandler = NewProtoStreamErrorHandler(PrefixOutgoingHeaderMatcher)
)
func NewProtoMessageErrorHandler(out runtime.HeaderMatcherFunc) runtime.ErrorHandlerFunc {
	h := &ProtoErrorHandler{out}
	return h.MessageHandler
}
func NewProtoStreamErrorHandler(out runtime.HeaderMatcherFunc) ProtoStreamErrorHandlerFunc {
	h := &ProtoErrorHandler{out}
	return h.StreamHandler
}
type ProtoErrorHandler struct {
	OutgoingHeaderMatcher runtime.HeaderMatcherFunc
}
func (h *ProtoErrorHandler) MessageHandler(ctx context.Context, mux *runtime.ServeMux, marshaler runtime.Marshaler, rw http.ResponseWriter, req *http.Request, err error) {
	md, ok := runtime.ServerMetadataFromContext(ctx)
	if !ok {
		grpclog.Infof("error handler: failed to extract ServerMetadata from context")
	}
	handleForwardResponseServerMetadata(h.OutgoingHeaderMatcher, rw, md)
	handleForwardResponseTrailerHeader(rw, md)
	h.writeError(ctx, false, marshaler, rw, err)
	handleForwardResponseTrailer(rw, md)
}
func (h *ProtoErrorHandler) StreamHandler(ctx context.Context, headerWritten bool, mux *runtime.ServeMux, marshaler runtime.Marshaler, rw http.ResponseWriter, req *http.Request, err error) {
	h.writeError(ctx, headerWritten, marshaler, rw, err)
}
func (h *ProtoErrorHandler) writeError(ctx context.Context, headerWritten bool, marshaler runtime.Marshaler, rw http.ResponseWriter, err error) {
	var fallback = `{"error":[{"message":"%s"}]}`
	if setStatusDetails {
		fallback = `{"error":[{"message":"%s", "code":500, "status": "INTERNAL"}]}`
	}
	st, ok := status.FromError(err)
	if !ok {
		st = status.New(codes.Unknown, err.Error())
	}
	statusCode, statusStr := HTTPStatus(ctx, st)
	details := []interface{}{}
	var fields interface{}
	for _, d := range st.Details() {
		switch d.(type) {
		case *errdetails.TargetInfo:
			details = append(details, d)
		case *errfields.FieldInfo:
			fields = d
		default:
			grpclog.Infof("error handler: failed to recognize error message")
			rw.WriteHeader(http.StatusInternalServerError)
			return
		}
	}
	restErr := map[string]interface{}{
		"message": st.Message(),
	}
	if len(details) > 0 {
		restErr["details"] = details
	}
	if fields != nil {
		restErr["fields"] = fields
	}
	if setStatusDetails {
		restErr["code"] = statusCode
		restErr["status"] = statusStr
	}
	errs, _, overrideErr := errorsAndSuccessFromContext(ctx)
	restResp := &RestErrs{
		Error: errs,
	}
	if !overrideErr {
		restResp.Error = append(restResp.Error, restErr)
	} else if setStatusDetails && len(restResp.Error) > 0 {
		restResp.Error[0]["code"] = statusCode
		restResp.Error[0]["status"] = statusStr
	}
	if !headerWritten {
		rw.Header().Del("Trailer")
		rw.Header().Set("Content-Type", marshaler.ContentType(nil))
		rw.WriteHeader(statusCode)
	}
	buf, merr := marshaler.Marshal(restResp)
	if merr != nil {
		grpclog.Infof("error handler: failed to marshal error message %q: %v", restErr, merr)
		rw.WriteHeader(http.StatusInternalServerError)
		if _, err := io.WriteString(rw, fmt.Sprintf(fallback, merr)); err != nil {
			grpclog.Infof("error handler: failed to write response: %v", err)
		}
		return
	}
	if _, err := rw.Write(buf); err != nil {
		grpclog.Infof("error handler: failed to write response: %v", err)
	}
}
type MessageWithFields interface {
	error
	GetFields() map[string]interface{}
	GetMessage() string
}
type messageWithFields struct {
	message string
	fields  map[string]interface{}
}
func (m *messageWithFields) Error() string {
	return m.message
}
func (m *messageWithFields) GetFields() map[string]interface{} {
	return m.fields
}
func (m *messageWithFields) GetMessage() string {
	return m.message
}
func NewWithFields(message string, kvpairs ...interface{}) MessageWithFields {
	mwf := &messageWithFields{message: message, fields: make(map[string]interface{})}
	for i := 0; i+1 < len(kvpairs); i += 2 {
		k, ok := kvpairs[i].(string)
		if !ok {
			return mwf
		}
		mwf.fields[k] = kvpairs[i+1]
	}
	return mwf
}
var counter *uint32
func init() {
	counter = new(uint32)
	*counter = uint32(time.Now().Nanosecond() % math.MaxUint32)
}
func WithError(ctx context.Context, err error) {
	i := atomic.AddUint32(counter, uint32(time.Now().Nanosecond()%100+1))
	md := metadata.Pairs(fmt.Sprintf("error-%d", i), fmt.Sprintf("message:%s", err.Error()))
	if mwf, ok := err.(MessageWithFields); ok {
		if f := mwf.GetFields(); f != nil {
			b, _ := json.Marshal(mwf.GetFields())
			md.Append(fmt.Sprintf("error-%d", i), fmt.Sprintf("fields:%q", b))
		}
	}
	grpc.SetTrailer(ctx, md)
}
func NewResponseError(ctx context.Context, msg string, kvpairs ...interface{}) error {
	md := metadata.Pairs("error", fmt.Sprintf("message:%s", msg))
	if len(kvpairs) > 0 {
		fields := make(map[string]interface{})
		for i := 0; i+1 < len(kvpairs); i += 2 {
			k, ok := kvpairs[i].(string)
			if !ok {
				grpclog.Infof("Key value for error details must be a string")
				continue
			}
			fields[k] = kvpairs[i+1]
		}
		b, _ := json.Marshal(fields)
		md.Append("error", fmt.Sprintf("fields:%q", b))
	}
	grpc.SetTrailer(ctx, md)
	return errors.New(msg)  
}
func NewResponseErrorWithCode(ctx context.Context, c codes.Code, msg string, kvpairs ...interface{}) error {
	SetStatus(ctx, status.New(c, msg))
	NewResponseError(ctx, msg, kvpairs...)
	return status.Error(c, msg)
}