func()

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
}