mirror of https://github.com/tidwall/tile38.git
Metadata for Webhooks
Added the `META name value` keyword to the SETHOOK command. Allows for adding metadata to a webhook. For example: SETHOOK myhook http://endpoint/ META m1 12 META m2 13 NEARBY ... Would result in notification that contain the "meta" element, which is represented like: "meta":{"m1":"12","m2":"13"} Thanks for the suggestion @amorskoy closed #105
This commit is contained in:
parent
73fd3cf7de
commit
bafb1823b3
|
@ -32,7 +32,7 @@ var (
|
|||
quiet bool
|
||||
)
|
||||
|
||||
// Fire up a webhook test server by using the --webhook-consumer-http-port
|
||||
// Fire up a webhook test server by using the --webhook-http-consumer-port
|
||||
// for example
|
||||
// $ ./tile38-server --webhook-http-consumer-port 9999
|
||||
//
|
||||
|
|
|
@ -229,7 +229,7 @@ func (c *Controller) queueHooks(d *commandDetailsT) error {
|
|||
if hm, ok := c.hookcols[d.key]; ok {
|
||||
for _, hook := range hm {
|
||||
// match the fence
|
||||
msgs := FenceMatch(hook.Name, hook.ScanWriter, hook.Fence, d)
|
||||
msgs := FenceMatch(hook.Name, hook.ScanWriter, hook.Fence, hook.Metas, d)
|
||||
if len(msgs) > 0 {
|
||||
// append each msg to the big list
|
||||
hmsgs = append(hmsgs, msgs...)
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package controller
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"strconv"
|
||||
"time"
|
||||
|
@ -13,13 +12,8 @@ import (
|
|||
)
|
||||
|
||||
// FenceMatch executes a fence match returns back json messages for fence detection.
|
||||
func FenceMatch(hookName string, sw *scanWriter, fence *liveFenceSwitches, details *commandDetailsT) [][]byte {
|
||||
overall := time.Now()
|
||||
defer func() {
|
||||
return
|
||||
fmt.Printf(">> %v\n", time.Since(overall))
|
||||
}()
|
||||
msgs := fenceMatch(hookName, sw, fence, details)
|
||||
func FenceMatch(hookName string, sw *scanWriter, fence *liveFenceSwitches, metas []FenceMeta, details *commandDetailsT) [][]byte {
|
||||
msgs := fenceMatch(hookName, sw, fence, metas, details)
|
||||
if len(fence.accept) == 0 {
|
||||
return msgs
|
||||
}
|
||||
|
@ -42,10 +36,31 @@ func jsonTimeFormat(t time.Time) string {
|
|||
b = appendJSONTimeFormat(b, t)
|
||||
return string(b)
|
||||
}
|
||||
|
||||
func fenceMatch(hookName string, sw *scanWriter, fence *liveFenceSwitches, details *commandDetailsT) [][]byte {
|
||||
func appendHookDetails(b []byte, hookName string, metas []FenceMeta) []byte {
|
||||
if len(hookName) > 0 {
|
||||
b = append(b, `,"hook":`...)
|
||||
b = appendJSONString(b, hookName)
|
||||
}
|
||||
if len(metas) > 0 {
|
||||
b = append(b, `,"meta":{`...)
|
||||
for i, meta := range metas {
|
||||
if i > 0 {
|
||||
b = append(b, ',')
|
||||
}
|
||||
b = appendJSONString(b, meta.Name)
|
||||
b = append(b, ':')
|
||||
b = appendJSONString(b, meta.Value)
|
||||
}
|
||||
b = append(b, '}')
|
||||
}
|
||||
return b
|
||||
}
|
||||
func hookJSONString(hookName string, metas []FenceMeta) string {
|
||||
return string(appendHookDetails(nil, hookName, metas))
|
||||
}
|
||||
func fenceMatch(hookName string, sw *scanWriter, fence *liveFenceSwitches, metas []FenceMeta, details *commandDetailsT) [][]byte {
|
||||
if details.command == "drop" {
|
||||
return [][]byte{[]byte(`{"command":"drop","hook":` + jsonString(hookName) + `,"time":` + jsonTimeFormat(details.timestamp) + `}`)}
|
||||
return [][]byte{[]byte(`{"command":"drop"` + hookJSONString(hookName, metas) + `,"time":` + jsonTimeFormat(details.timestamp) + `}`)}
|
||||
}
|
||||
if len(fence.glob) > 0 && !(len(fence.glob) == 1 && fence.glob[0] == '*') {
|
||||
match, _ := glob.Match(fence.glob, details.id)
|
||||
|
@ -65,7 +80,7 @@ func fenceMatch(hookName string, sw *scanWriter, fence *liveFenceSwitches, detai
|
|||
}
|
||||
}
|
||||
if details.command == "del" {
|
||||
return [][]byte{[]byte(`{"command":"del","hook":` + jsonString(hookName) + `,"id":` + jsonString(details.id) + `,"time":` + jsonTimeFormat(details.timestamp) + `}`)}
|
||||
return [][]byte{[]byte(`{"command":"del"` + hookJSONString(hookName, metas) + `,"id":` + jsonString(details.id) + `,"time":` + jsonTimeFormat(details.timestamp) + `}`)}
|
||||
}
|
||||
|
||||
var roamkeys, roamids []string
|
||||
|
@ -174,18 +189,18 @@ func fenceMatch(hookName string, sw *scanWriter, fence *liveFenceSwitches, detai
|
|||
msgs := make([][]byte, 0, 4)
|
||||
if fence.detect == nil || fence.detect[detect] {
|
||||
if len(res) > 0 && res[0] == '{' {
|
||||
res = makemsg(details.command, group, detect, hookName, details.key, details.timestamp, res[1:])
|
||||
res = makemsg(details.command, group, detect, hookName, metas, details.key, details.timestamp, res[1:])
|
||||
}
|
||||
msgs = append(msgs, res)
|
||||
}
|
||||
switch detect {
|
||||
case "enter":
|
||||
if fence.detect == nil || fence.detect["inside"] {
|
||||
msgs = append(msgs, makemsg(details.command, group, "inside", hookName, details.key, details.timestamp, res[1:]))
|
||||
msgs = append(msgs, makemsg(details.command, group, "inside", hookName, metas, details.key, details.timestamp, res[1:]))
|
||||
}
|
||||
case "exit", "cross":
|
||||
if fence.detect == nil || fence.detect["outside"] {
|
||||
msgs = append(msgs, makemsg(details.command, group, "outside", hookName, details.key, details.timestamp, res[1:]))
|
||||
msgs = append(msgs, makemsg(details.command, group, "outside", hookName, metas, details.key, details.timestamp, res[1:]))
|
||||
}
|
||||
case "roam":
|
||||
if len(msgs) > 0 {
|
||||
|
@ -244,12 +259,13 @@ func fenceMatch(hookName string, sw *scanWriter, fence *liveFenceSwitches, detai
|
|||
return msgs
|
||||
}
|
||||
|
||||
func makemsg(command, group, detect, hookName string, key string, t time.Time, tail []byte) []byte {
|
||||
func makemsg(command, group, detect, hookName string, metas []FenceMeta, key string, t time.Time, tail []byte) []byte {
|
||||
var buf []byte
|
||||
buf = append(append(buf, `{"command":"`...), command...)
|
||||
buf = append(append(buf, `","group":"`...), group...)
|
||||
buf = append(append(buf, `","detect":"`...), detect...)
|
||||
buf = appendJSONString(append(buf, `","hook":`...), hookName)
|
||||
buf = append(buf, '"')
|
||||
buf = appendHookDetails(buf, hookName, metas)
|
||||
buf = appendJSONString(append(buf, `,"key":`...), key)
|
||||
buf = appendJSONTimeFormat(append(buf, `,"time":`...), t)
|
||||
buf = append(append(buf, ','), tail...)
|
||||
|
|
|
@ -66,21 +66,36 @@ func (c *Controller) cmdSetHook(msg *server.Message) (res string, d commandDetai
|
|||
}
|
||||
endpoints = append(endpoints, url)
|
||||
}
|
||||
|
||||
commandvs := vs
|
||||
if vs, cmd, ok = tokenval(vs); !ok || cmd == "" {
|
||||
return "", d, errInvalidNumberOfArguments
|
||||
}
|
||||
|
||||
cmdlc := strings.ToLower(cmd)
|
||||
var commandvs []resp.Value
|
||||
var cmdlc string
|
||||
var types []string
|
||||
switch cmdlc {
|
||||
default:
|
||||
return "", d, errInvalidArgument(cmd)
|
||||
case "nearby":
|
||||
types = nearbyTypes
|
||||
case "within", "intersects":
|
||||
types = withinOrIntersectsTypes
|
||||
metaMap := make(map[string]string)
|
||||
for {
|
||||
commandvs = vs
|
||||
if vs, cmd, ok = tokenval(vs); !ok || cmd == "" {
|
||||
return "", d, errInvalidNumberOfArguments
|
||||
}
|
||||
cmdlc = strings.ToLower(cmd)
|
||||
switch cmdlc {
|
||||
default:
|
||||
return "", d, errInvalidArgument(cmd)
|
||||
case "meta":
|
||||
var metakey string
|
||||
var metaval string
|
||||
if vs, metakey, ok = tokenval(vs); !ok || metakey == "" {
|
||||
return "", d, errInvalidNumberOfArguments
|
||||
}
|
||||
if vs, metaval, ok = tokenval(vs); !ok || metaval == "" {
|
||||
return "", d, errInvalidNumberOfArguments
|
||||
}
|
||||
metaMap[metakey] = metaval
|
||||
continue
|
||||
case "nearby":
|
||||
types = nearbyTypes
|
||||
case "within", "intersects":
|
||||
types = withinOrIntersectsTypes
|
||||
}
|
||||
break
|
||||
}
|
||||
s, err := c.cmdSearchArgs(cmdlc, vs, types)
|
||||
if err != nil {
|
||||
|
@ -99,6 +114,12 @@ func (c *Controller) cmdSetHook(msg *server.Message) (res string, d commandDetai
|
|||
}
|
||||
cmsg.Command = strings.ToLower(cmsg.Values[0].String())
|
||||
|
||||
metas := make([]FenceMeta, 0, len(metaMap))
|
||||
for key, val := range metaMap {
|
||||
metas = append(metas, FenceMeta{key, val})
|
||||
}
|
||||
sort.Sort(hookMetaByName(metas))
|
||||
|
||||
hook := &Hook{
|
||||
Key: s.key,
|
||||
Name: name,
|
||||
|
@ -107,6 +128,7 @@ func (c *Controller) cmdSetHook(msg *server.Message) (res string, d commandDetai
|
|||
Message: cmsg,
|
||||
db: c.qdb,
|
||||
epm: c.epc,
|
||||
Metas: metas,
|
||||
}
|
||||
hook.cond = sync.NewCond(&hook.mu)
|
||||
|
||||
|
@ -117,27 +139,15 @@ func (c *Controller) cmdSetHook(msg *server.Message) (res string, d commandDetai
|
|||
}
|
||||
|
||||
if h, ok := c.hooks[name]; ok {
|
||||
// lets see if the previous hook matches the new hook
|
||||
if h.Key == hook.Key && h.Name == hook.Name {
|
||||
if len(h.Endpoints) == len(hook.Endpoints) {
|
||||
match := true
|
||||
for i, endpoint := range h.Endpoints {
|
||||
if endpoint != hook.Endpoints[i] {
|
||||
match = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if match && resp.ArrayValue(h.Message.Values).Equals(resp.ArrayValue(hook.Message.Values)) {
|
||||
// it was a match so we do nothing. But let's signal just
|
||||
// for good measure.
|
||||
h.Signal()
|
||||
switch msg.OutputType {
|
||||
case server.JSON:
|
||||
return server.OKMessage(msg, start), d, nil
|
||||
case server.RESP:
|
||||
return ":0\r\n", d, nil
|
||||
}
|
||||
}
|
||||
if h.Equals(hook) {
|
||||
// it was a match so we do nothing. But let's signal just
|
||||
// for good measure.
|
||||
h.Signal()
|
||||
switch msg.OutputType {
|
||||
case server.JSON:
|
||||
return server.OKMessage(msg, start), d, nil
|
||||
case server.RESP:
|
||||
return ":0\r\n", d, nil
|
||||
}
|
||||
}
|
||||
h.Close()
|
||||
|
@ -323,6 +333,7 @@ type Hook struct {
|
|||
Message *server.Message
|
||||
Fence *liveFenceSwitches
|
||||
ScanWriter *scanWriter
|
||||
Metas []FenceMeta
|
||||
db *buntdb.DB
|
||||
closed bool
|
||||
opened bool
|
||||
|
@ -330,6 +341,46 @@ type Hook struct {
|
|||
epm *endpoint.EndpointManager
|
||||
}
|
||||
|
||||
func (h *Hook) Equals(hook *Hook) bool {
|
||||
if h.Key != hook.Key ||
|
||||
h.Name != hook.Name ||
|
||||
len(h.Endpoints) != len(hook.Endpoints) ||
|
||||
len(h.Metas) != len(hook.Metas) {
|
||||
return false
|
||||
}
|
||||
for i, endpoint := range h.Endpoints {
|
||||
if endpoint != hook.Endpoints[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
for i, meta := range h.Metas {
|
||||
if meta.Name != hook.Metas[i].Name ||
|
||||
meta.Value != hook.Metas[i].Value {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return resp.ArrayValue(h.Message.Values).Equals(
|
||||
resp.ArrayValue(hook.Message.Values))
|
||||
}
|
||||
|
||||
type FenceMeta struct {
|
||||
Name, Value string
|
||||
}
|
||||
|
||||
type hookMetaByName []FenceMeta
|
||||
|
||||
func (arr hookMetaByName) Len() int {
|
||||
return len(arr)
|
||||
}
|
||||
|
||||
func (arr hookMetaByName) Less(a, b int) bool {
|
||||
return arr[a].Name < arr[b].Name
|
||||
}
|
||||
|
||||
func (arr hookMetaByName) Swap(a, b int) {
|
||||
arr[a], arr[b] = arr[b], arr[a]
|
||||
}
|
||||
|
||||
// Open is called when a hook is first created. It calls the manager
|
||||
// function in a goroutine
|
||||
func (h *Hook) Open() {
|
||||
|
|
|
@ -162,7 +162,7 @@ func (c *Controller) goLive(inerr error, conn net.Conn, rd *server.AnyReaderWrit
|
|||
}
|
||||
fence := lb.fence
|
||||
lb.cond.L.Unlock()
|
||||
msgs := FenceMatch("", sw, fence, details)
|
||||
msgs := FenceMatch("", sw, fence, nil, details)
|
||||
for _, msg := range msgs {
|
||||
if err := writeMessage(conn, []byte(msg), true, connType, websocket); err != nil {
|
||||
return nil // nil return is fine here
|
||||
|
|
|
@ -1104,6 +1104,13 @@
|
|||
"name": "endpoint",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"command": "META",
|
||||
"name": ["name", "value"],
|
||||
"type": ["string", "string"],
|
||||
"optional": true,
|
||||
"multiple": true
|
||||
},
|
||||
{
|
||||
"enum": ["NEARBY", "WITHIN", "INTERSECTS"]
|
||||
},
|
||||
|
|
|
@ -1266,6 +1266,13 @@ var commandsJSON = `{
|
|||
"name": "endpoint",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"command": "META",
|
||||
"name": ["name", "value"],
|
||||
"type": ["string", "string"],
|
||||
"optional": true,
|
||||
"multiple": true
|
||||
},
|
||||
{
|
||||
"enum": ["NEARBY", "WITHIN", "INTERSECTS"]
|
||||
},
|
||||
|
|
Loading…
Reference in New Issue