gitee.com/liuxuezhan/go-micro-v1.18.0@v1.0.0/api/handler/event/event.go (about)

     1  // Package event provides a handler which publishes an event
     2  package event
     3  
     4  import (
     5  	"encoding/json"
     6  	"fmt"
     7  	"io/ioutil"
     8  	"net/http"
     9  	"path"
    10  	"regexp"
    11  	"strings"
    12  	"time"
    13  
    14  	"github.com/google/uuid"
    15  	"gitee.com/liuxuezhan/go-micro-v1.18.0/api/handler"
    16  	proto "gitee.com/liuxuezhan/go-micro-v1.18.0/api/proto"
    17  	"gitee.com/liuxuezhan/go-micro-v1.18.0/util/ctx"
    18  )
    19  
    20  type event struct {
    21  	options handler.Options
    22  }
    23  
    24  var (
    25  	Handler   = "event"
    26  	versionRe = regexp.MustCompilePOSIX("^v[0-9]+$")
    27  )
    28  
    29  func eventName(parts []string) string {
    30  	return strings.Join(parts, ".")
    31  }
    32  
    33  func evRoute(ns, p string) (string, string) {
    34  	p = path.Clean(p)
    35  	p = strings.TrimPrefix(p, "/")
    36  
    37  	if len(p) == 0 {
    38  		return ns, "event"
    39  	}
    40  
    41  	parts := strings.Split(p, "/")
    42  
    43  	// no path
    44  	if len(parts) == 0 {
    45  		// topic: namespace
    46  		// action: event
    47  		return strings.Trim(ns, "."), "event"
    48  	}
    49  
    50  	// Treat /v[0-9]+ as versioning
    51  	// /v1/foo/bar => topic: v1.foo action: bar
    52  	if len(parts) >= 2 && versionRe.Match([]byte(parts[0])) {
    53  		topic := ns + "." + strings.Join(parts[:2], ".")
    54  		action := eventName(parts[1:])
    55  		return topic, action
    56  	}
    57  
    58  	// /foo => topic: ns.foo action: foo
    59  	// /foo/bar => topic: ns.foo action: bar
    60  	topic := ns + "." + strings.Join(parts[:1], ".")
    61  	action := eventName(parts[1:])
    62  
    63  	return topic, action
    64  }
    65  
    66  func (e *event) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    67  	// request to topic:event
    68  	// create event
    69  	// publish to topic
    70  
    71  	topic, action := evRoute(e.options.Namespace, r.URL.Path)
    72  
    73  	// create event
    74  	ev := &proto.Event{
    75  		Name: action,
    76  		// TODO: dedupe event
    77  		Id:        fmt.Sprintf("%s-%s-%s", topic, action, uuid.New().String()),
    78  		Header:    make(map[string]*proto.Pair),
    79  		Timestamp: time.Now().Unix(),
    80  	}
    81  
    82  	// set headers
    83  	for key, vals := range r.Header {
    84  		header, ok := ev.Header[key]
    85  		if !ok {
    86  			header = &proto.Pair{
    87  				Key: key,
    88  			}
    89  			ev.Header[key] = header
    90  		}
    91  		header.Values = vals
    92  	}
    93  
    94  	// set body
    95  	if r.Method == "GET" {
    96  		bytes, _ := json.Marshal(r.URL.Query())
    97  		ev.Data = string(bytes)
    98  	} else {
    99  		b, err := ioutil.ReadAll(r.Body)
   100  		if err != nil {
   101  			http.Error(w, err.Error(), 500)
   102  			return
   103  		}
   104  		ev.Data = string(b)
   105  	}
   106  
   107  	// get client
   108  	c := e.options.Service.Client()
   109  
   110  	// create publication
   111  	p := c.NewMessage(topic, ev)
   112  
   113  	// publish event
   114  	if err := c.Publish(ctx.FromRequest(r), p); err != nil {
   115  		http.Error(w, err.Error(), 500)
   116  		return
   117  	}
   118  }
   119  
   120  func (e *event) String() string {
   121  	return "event"
   122  }
   123  
   124  func NewHandler(opts ...handler.Option) handler.Handler {
   125  	return &event{
   126  		options: handler.NewOptions(opts...),
   127  	}
   128  }