in plugins/wasm-go/extensions/transformer/main.go [1014:1209]
func (h jsonHandler) handle(host, path string, oriData []byte, mapSourceData map[string]MapSourceData) (data []byte, err error) {
// arbitary order. for example: remove → rename → replace → add → append → map → dedupe
if !gjson.ValidBytes(oriData) {
return nil, errors.New("invalid json body")
}
data = oriData
for _, kvtOp := range h.kvtOps {
switch kvtOp.kvtOpType {
case RemoveK:
// remove
for _, remove := range kvtOp.removeKvtGroup {
if data, err = sjson.DeleteBytes(data, remove.key); err != nil {
return nil, errors.Wrap(err, errRemove.Error())
}
}
case RenameK:
// rename: 若指定 oldKey 不存在则无操作;否则将 oldKey 的值追加给 newKey,并删除 oldKey:value
for _, rename := range kvtOp.renameKvtGroup {
oldKey, newKey := rename.oldKey, rename.newKey
value := gjson.GetBytes(data, oldKey)
if !value.Exists() {
continue
}
if data, err = sjson.SetBytes(data, newKey, value.Value()); err != nil {
return nil, errors.Wrap(err, errRename.Error())
}
if data, err = sjson.DeleteBytes(data, oldKey); err != nil {
return nil, errors.Wrap(err, errRename.Error())
}
}
case ReplaceK:
// replace: 若指定 key 不存在,则无操作;否则替换 value 为 newValue
for _, replace := range kvtOp.replaceKvtGroup {
key, newValue, valueType := replace.key, replace.newValue, replace.typ
if !gjson.GetBytes(data, key).Exists() {
continue
}
if valueType == "string" && replace.reg != nil {
newValue = replace.reg.matchAndReplace(newValue, host, path)
}
convertedNewValue, err := convertByJsonType(valueType, newValue)
if err != nil {
return nil, errors.Wrap(err, errReplace.Error())
}
if data, err = sjson.SetBytes(data, key, convertedNewValue); err != nil {
return nil, errors.Wrap(err, errReplace.Error())
}
}
case AddK:
// add: 若指定 key 存在则无操作;否则添加 key:value
for _, add := range kvtOp.addKvtGroup {
key, value, valueType := add.key, add.value, add.typ
if gjson.GetBytes(data, key).Exists() {
continue
}
if valueType == "string" && add.reg != nil {
value = add.reg.matchAndReplace(value, host, path)
}
convertedValue, err := convertByJsonType(valueType, value)
if err != nil {
return nil, errors.Wrap(err, errAdd.Error())
}
if data, err = sjson.SetBytes(data, key, convertedValue); err != nil {
return nil, errors.Wrap(err, errAdd.Error())
}
}
case AppendK:
// append: 若指定 key 存在,则追加同名 kv;否则相当于添加操作
// 当原本的 value 为数组时,追加;当原本的 value 不为数组时,将原本的 value 和 appendValue 组成数组
for _, append_ := range kvtOp.appendKvtGroup {
key, appendValue, valueType := append_.key, append_.appendValue, append_.typ
if valueType == "string" && append_.reg != nil {
appendValue = append_.reg.matchAndReplace(appendValue, host, path)
}
convertedAppendValue, err := convertByJsonType(valueType, appendValue)
if err != nil {
return nil, errors.Wrapf(err, errAppend.Error())
}
oldValue := gjson.GetBytes(data, key)
if !oldValue.Exists() {
if data, err = sjson.SetBytes(data, key, convertedAppendValue); err != nil { // key: appendValue
return nil, errors.Wrap(err, errAppend.Error())
}
continue
}
// oldValue exists
if oldValue.IsArray() {
if len(oldValue.Array()) == 0 {
if data, err = sjson.SetBytes(data, key, []interface{}{convertedAppendValue}); err != nil { // key: [appendValue]
return nil, errors.Wrap(err, errAppend.Error())
}
continue
}
// len(oldValue.Array()) != 0
oldValues := make([]interface{}, 0, len(oldValue.Array())+1)
for _, val := range oldValue.Array() {
oldValues = append(oldValues, val.Value())
}
if data, err = sjson.SetBytes(data, key, append(oldValues, convertedAppendValue)); err != nil { // key: [oldValue..., appendValue]
return nil, errors.Wrap(err, errAppend.Error())
}
continue
}
// oldValue is not array
if data, err = sjson.SetBytes(data, key, []interface{}{oldValue.Value(), convertedAppendValue}); err != nil { // key: [oldValue, appendValue]
return nil, errors.Wrap(err, errAppend.Error())
}
}
case MapK:
// map: 若指定 fromKey 不存在则无操作;否则将 fromKey 的值映射给 toKey 的值
for _, map_ := range kvtOp.mapKvtGroup {
fromKey, toKey := map_.fromKey, map_.toKey
if kvtOp.mapSource == "headers" {
fromKey = strings.ToLower(fromKey)
}
source, exist := mapSourceData[kvtOp.mapSource]
if !exist {
proxywasm.LogWarnf("map key failed, source:%s not exists", kvtOp.mapSource)
continue
}
proxywasm.LogDebugf("search key:%s in source:%s", fromKey, kvtOp.mapSource)
if fromValue, ok := source.search(fromKey); ok {
switch source.mapSourceType {
case "headers", "querys", "bodyKv":
if data, err = sjson.SetBytes(data, toKey, fromValue); err != nil {
return nil, errors.Wrap(err, errMap.Error())
}
proxywasm.LogDebugf("map key:%s to key:%s success, value is: %v", fromKey, toKey, fromValue)
case "bodyJson":
if valueJson, ok := fromValue.(gjson.Result); ok {
if data, err = sjson.SetBytes(data, toKey, valueJson.Value()); err != nil {
return nil, errors.Wrap(err, errMap.Error())
}
proxywasm.LogDebugf("map key:%s to key:%s success, value is: %v", fromKey, toKey, fromValue)
}
}
}
}
case DedupeK:
// dedupe: 根据 strategy 去重:RETAIN_UNIQUE 保留所有唯一值,RETAIN_LAST 保留最后一个值,RETAIN_FIRST 保留第一个值 (default)
for _, dedupe := range kvtOp.dedupeKvtGroup {
key, strategy := dedupe.key, dedupe.strategy
value := gjson.GetBytes(data, key)
if !value.Exists() || !value.IsArray() {
continue
}
// value is array
values := value.Array()
if len(values) == 0 {
continue
}
var dedupedVal interface{}
switch strings.ToUpper(strategy) {
case "RETAIN_UNIQUE":
uniSet, uniques := make(map[string]struct{}), make([]interface{}, 0)
for _, v := range values {
vstr := v.String()
if _, ok := uniSet[vstr]; !ok {
uniSet[vstr] = struct{}{}
uniques = append(uniques, v.Value())
}
}
if len(uniques) == 1 {
dedupedVal = uniques[0] // key: uniques[0]
} else if len(uniques) > 1 {
dedupedVal = uniques // key: [uniques...]
}
case "RETAIN_LAST":
dedupedVal = values[len(values)-1].Value() // key: last
case "RETAIN_FIRST":
fallthrough
default:
dedupedVal = values[0].Value() // key: first
}
if dedupedVal == nil {
continue
}
if data, err = sjson.SetBytes(data, key, dedupedVal); err != nil {
return nil, errors.Wrap(err, errDedupe.Error())
}
}
}
}
return data, nil
}