diff --git a/x-pack/filebeat/docs/inputs/input-httpjson.asciidoc b/x-pack/filebeat/docs/inputs/input-httpjson.asciidoc index 0585f10d46e..5a80df9d554 100644 --- a/x-pack/filebeat/docs/inputs/input-httpjson.asciidoc +++ b/x-pack/filebeat/docs/inputs/input-httpjson.asciidoc @@ -205,7 +205,7 @@ Some built-in helper functions are provided to work with the input state inside - `hmac`: calculates the hmac signature of a list of strings concatenated together. Supports sha1 or sha256. Example `[[hmac "sha256" "secret" "string1" "string2" (formatDate (now) "RFC1123")]]` - `base64Encode`: Joins and base64 encodes all supplied strings. Example `[[base64Encode "string1" "string2"]]` - `base64EncodeNoPad`: Joins and base64 encodes all supplied strings without padding. Example `[[base64EncodeNoPad "string1" "string2"]]` -- `join`: joins a list of strings using the specified separator. Example: `[[join .body.arr ","]]` +- `join`: joins a list using the specified separator. Example: `[[join .body.arr ","]]` - `sprintf`: formats according to a format specifier and returns the resulting string. Refer to https://pkg.go.dev/fmt#Sprintf[the Go docs] for usage. Example: `[[sprintf "%d:%q" 34 "quote this"]]` In addition to the provided functions, any of the native functions for https://golang.org/pkg/time/#Time[`time.Time`], https://golang.org/pkg/net/http/#Header[`http.Header`], and https://golang.org/pkg/net/url/#Values[`url.Values`] types can be used on the corresponding objects. Examples: `[[(now).Day]]`, `[[.last_response.header.Get "key"]]` diff --git a/x-pack/filebeat/input/httpjson/internal/v2/value_tpl.go b/x-pack/filebeat/input/httpjson/internal/v2/value_tpl.go index 9f91fcdccce..779f6f116f4 100644 --- a/x-pack/filebeat/input/httpjson/internal/v2/value_tpl.go +++ b/x-pack/filebeat/input/httpjson/internal/v2/value_tpl.go @@ -58,7 +58,7 @@ func (t *valueTpl) Unpack(in string) error { "hmac": hmacString, "base64Encode": base64Encode, "base64EncodeNoPad": base64EncodeNoPad, - "join": strings.Join, + "join": join, "sprintf": fmt.Sprintf, }). Delims(leftDelim, rightDelim). @@ -289,3 +289,30 @@ func hmacString(hmacType string, hmacKey string, values ...string) string { // Get result and encode as hexadecimal string return hex.EncodeToString(mac.Sum(nil)) } + +// join concatenates the elements of its first argument to create a single string. The separator +// string sep is placed between elements in the resulting string. If the first argument is not of +// type string or []string, its elements will be stringified. +func join(v interface{}, sep string) string { + // check for []string or string to avoid using reflect + switch t := v.(type) { + case []string: + return strings.Join(t, sep) + case string: + return t + } + + // if we have a slice of a different type, convert it to []string + switch reflect.TypeOf(v).Kind() { + case reflect.Slice, reflect.Array: + s := reflect.ValueOf(v) + vs := make([]string, s.Len()) + for i := 0; i < s.Len(); i++ { + vs[i] = fmt.Sprint(s.Index(i)) + } + return strings.Join(vs, sep) + } + + // return the stringified single value + return fmt.Sprint(v) +} diff --git a/x-pack/filebeat/input/httpjson/internal/v2/value_tpl_test.go b/x-pack/filebeat/input/httpjson/internal/v2/value_tpl_test.go index 4586dec7711..68031d746fa 100644 --- a/x-pack/filebeat/input/httpjson/internal/v2/value_tpl_test.go +++ b/x-pack/filebeat/input/httpjson/internal/v2/value_tpl_test.go @@ -292,24 +292,36 @@ func TestValueTpl(t *testing.T) { expectedError: errEmptyTemplateResult.Error(), }, { - name: "func join", - value: `[[join .last_response.body.arr ","]]`, + name: "func join", + value: `[[join .last_response.body.strarr ","]] [[join .last_response.body.iarr ","]] ` + + `[[join .last_response.body.narr ","]] [[join .last_response.body.singlevalstr ","]] ` + + `[[join .last_response.body.singlevalint ","]]`, paramCtx: &transformContext{ firstEvent: &common.MapStr{}, lastEvent: &common.MapStr{}, lastResponse: newTestResponse( common.MapStr{ - "arr": []string{ + "strarr": []string{ "foo", "bar", }, + "iarr": []interface{}{ + "foo", + 2, + }, + "narr": []int{ + 1, + 2, + }, + "singlevalstr": "foo", + "singlevalint": 2, }, http.Header{}, "", ), }, paramTr: transformable{}, - expectedVal: "foo,bar", + expectedVal: "foo,bar foo,2 1,2 foo 2", }, { name: "func sprintf",