-
Notifications
You must be signed in to change notification settings - Fork 4.9k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Add `safemapstr` to provide `Put`: This method implements a way to put dotted keys into a MapStr while ensuring they don't override each other. For example: ``` a := MapStr{} a.Put("com.docker.swarm.task", "x") a.Put("com.docker.swarm.task.id", 1) a.Put("com.docker.swarm.task.name", "foobar") ``` Will result in `{"com":{"docker":{"swarm":{"task":"x"}}}}` DeDotPut detects this scenario and renames the common base key, by appending `.value`: ``` a := MapStr{} safemapstr.Put(a, "com.docker.swarm.task", "x") safemapstr.Put(a, "com.docker.swarm.task.id", 1) safemapstr.Put(a, "com.docker.swarm.task.name", "foobar") ``` Will result in `{"com":{"docker":{"swarm":{"task":{"id":1,"name":"foobar","value":"x"}}}}}` The plan is to use this method to convert all unstructured external data, like labels, annotations and so Related to #6421 * Apply some review comments * Use tryToMapStr when checking leaf node
- Loading branch information
1 parent
6bec4b1
commit bb6d3d8
Showing
3 changed files
with
135 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
package safemapstr | ||
|
||
import ( | ||
"strings" | ||
|
||
"github.com/elastic/beats/libbeat/common" | ||
) | ||
|
||
const alternativeKey = "value" | ||
|
||
// Put This method implements a way to put dotted keys into a MapStr while | ||
// ensuring they don't override each other. For example: | ||
// | ||
// a := MapStr{} | ||
// safemapstr.Put(a, "com.docker.swarm.task", "x") | ||
// safemapstr.Put(a, "com.docker.swarm.task.id", 1) | ||
// safemapstr.Put(a, "com.docker.swarm.task.name", "foobar") | ||
// | ||
// Will result in `{"com":{"docker":{"swarm":{"task":{"id":1,"name":"foobar","value":"x"}}}}}` | ||
// | ||
// Put detects this scenario and renames the common base key, by appending | ||
// `.value` | ||
func Put(data common.MapStr, key string, value interface{}) error { | ||
// XXX This implementation mimics `common.MapStr.Put`, both should be updated to have similar behavior | ||
keyParts := strings.SplitN(key, ".", 2) | ||
|
||
// If leaf node or key exists directly | ||
if len(keyParts) == 1 { | ||
oldValue, exists := data[key] | ||
if exists { | ||
oldMap, ok := tryToMapStr(oldValue) | ||
if ok { | ||
// This would replace a whole object, change its key to avoid that: | ||
oldMap[alternativeKey] = value | ||
return nil | ||
} | ||
} | ||
data[key] = value | ||
return nil | ||
} | ||
|
||
// Checks if first part of the key exists | ||
k := keyParts[0] | ||
d, exists := data[k] | ||
if !exists { | ||
d = common.MapStr{} | ||
data[k] = d | ||
} | ||
|
||
v, ok := tryToMapStr(d) | ||
if !ok { | ||
// This would replace a leaf with an object, change its key to avoid that: | ||
v = common.MapStr{alternativeKey: d} | ||
data[k] = v | ||
} | ||
|
||
return Put(v, keyParts[1], value) | ||
} | ||
|
||
func tryToMapStr(v interface{}) (common.MapStr, bool) { | ||
switch m := v.(type) { | ||
case common.MapStr: | ||
return m, true | ||
case map[string]interface{}: | ||
return common.MapStr(m), true | ||
default: | ||
return nil, false | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
package safemapstr | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/elastic/beats/libbeat/common" | ||
|
||
"github.com/stretchr/testify/assert" | ||
) | ||
|
||
func TestPut(t *testing.T) { | ||
m := common.MapStr{ | ||
"subMap": common.MapStr{ | ||
"a": 1, | ||
}, | ||
} | ||
|
||
// Add new value to the top-level. | ||
err := Put(m, "a", "ok") | ||
assert.NoError(t, err) | ||
assert.Equal(t, common.MapStr{"a": "ok", "subMap": common.MapStr{"a": 1}}, m) | ||
|
||
// Add new value to subMap. | ||
err = Put(m, "subMap.b", 2) | ||
assert.NoError(t, err) | ||
assert.Equal(t, common.MapStr{"a": "ok", "subMap": common.MapStr{"a": 1, "b": 2}}, m) | ||
|
||
// Overwrite a value in subMap. | ||
err = Put(m, "subMap.a", 2) | ||
assert.NoError(t, err) | ||
assert.Equal(t, common.MapStr{"a": "ok", "subMap": common.MapStr{"a": 2, "b": 2}}, m) | ||
|
||
// Add value to map that does not exist. | ||
m = common.MapStr{} | ||
err = Put(m, "subMap.newMap.a", 1) | ||
assert.NoError(t, err) | ||
assert.Equal(t, common.MapStr{"subMap": common.MapStr{"newMap": common.MapStr{"a": 1}}}, m) | ||
} | ||
|
||
func TestPutRenames(t *testing.T) { | ||
assert := assert.New(t) | ||
|
||
a := common.MapStr{} | ||
Put(a, "com.docker.swarm.task", "x") | ||
Put(a, "com.docker.swarm.task.id", 1) | ||
Put(a, "com.docker.swarm.task.name", "foobar") | ||
assert.Equal(common.MapStr{"com": common.MapStr{"docker": common.MapStr{"swarm": common.MapStr{ | ||
"task": common.MapStr{ | ||
"id": 1, | ||
"name": "foobar", | ||
"value": "x", | ||
}}}}}, a) | ||
|
||
// order is not important: | ||
b := common.MapStr{} | ||
Put(b, "com.docker.swarm.task.id", 1) | ||
Put(b, "com.docker.swarm.task.name", "foobar") | ||
Put(b, "com.docker.swarm.task", "x") | ||
assert.Equal(common.MapStr{"com": common.MapStr{"docker": common.MapStr{"swarm": common.MapStr{ | ||
"task": common.MapStr{ | ||
"id": 1, | ||
"name": "foobar", | ||
"value": "x", | ||
}}}}}, b) | ||
} |