github.com/GuanceCloud/cliutils@v1.1.21/pipeline/ptinput/funcs/fn_json.go (about)

     1  // Unless explicitly stated otherwise all files in this repository are licensed
     2  // under the MIT License.
     3  // This product includes software developed at Guance Cloud (https://www.guance.com/).
     4  // Copyright 2021-present Guance, Inc.
     5  
     6  package funcs
     7  
     8  import (
     9  	"encoding/json"
    10  	"fmt"
    11  	"reflect"
    12  	"strings"
    13  
    14  	"github.com/GuanceCloud/cliutils/pipeline/ptinput"
    15  	"github.com/GuanceCloud/platypus/pkg/ast"
    16  	"github.com/GuanceCloud/platypus/pkg/engine/runtime"
    17  	"github.com/GuanceCloud/platypus/pkg/errchain"
    18  )
    19  
    20  func JSONChecking(ctx *runtime.Task, funcExpr *ast.CallExpr) *errchain.PlError {
    21  	if err := normalizeFuncArgsDeprecated(funcExpr, []string{
    22  		"input", "json_path", "newkey",
    23  		"trim_space", "delete_after_extract",
    24  	}, 2); err != nil {
    25  		return runtime.NewRunError(ctx, err.Error(), funcExpr.NamePos)
    26  	}
    27  
    28  	if _, err := getKeyName(funcExpr.Param[0]); err != nil {
    29  		return runtime.NewRunError(ctx, err.Error(), funcExpr.Param[0].StartPos())
    30  	}
    31  
    32  	lastIdxExpr := false
    33  	switch funcExpr.Param[1].NodeType { //nolint:exhaustive
    34  	case ast.TypeAttrExpr, ast.TypeIdentifier, ast.TypeIndexExpr:
    35  		var err error
    36  		lastIdxExpr, err = lastIsIndex(funcExpr.Param[1])
    37  		if err != nil {
    38  			return runtime.NewRunError(ctx, err.Error(), funcExpr.Param[1].StartPos())
    39  		}
    40  	default:
    41  		return runtime.NewRunError(ctx, fmt.Sprintf("expect AttrExpr, IndexExpr or Identifier, got %s",
    42  			funcExpr.Param[1].NodeType), funcExpr.Param[1].StartPos())
    43  	}
    44  
    45  	if funcExpr.Param[2] != nil {
    46  		switch funcExpr.Param[2].NodeType { //nolint:exhaustive
    47  		case ast.TypeAttrExpr, ast.TypeIdentifier, ast.TypeStringLiteral:
    48  		default:
    49  			return runtime.NewRunError(ctx, fmt.Sprintf("expect AttrExpr or Identifier, got %s",
    50  				funcExpr.Param[2].NodeType), funcExpr.Param[2].StartPos())
    51  		}
    52  	}
    53  
    54  	if funcExpr.Param[3] != nil {
    55  		switch funcExpr.Param[3].NodeType { //nolint:exhaustive
    56  		case ast.TypeBoolLiteral:
    57  		default:
    58  			return runtime.NewRunError(ctx, fmt.Sprintf("expect BoolLiteral, got %s",
    59  				funcExpr.Param[3].NodeType), funcExpr.Param[3].StartPos())
    60  		}
    61  	}
    62  
    63  	if funcExpr.Param[4] != nil {
    64  		switch funcExpr.Param[4].NodeType { //nolint:exhaustive
    65  		case ast.TypeBoolLiteral:
    66  			if funcExpr.Param[4].BoolLiteral().Val == lastIdxExpr {
    67  				return runtime.NewRunError(ctx, "does not support deleting elements in the list",
    68  					funcExpr.Param[4].StartPos())
    69  			}
    70  		default:
    71  			return runtime.NewRunError(ctx, fmt.Sprintf("expect BoolLiteral, got %s",
    72  				funcExpr.Param[3].NodeType), funcExpr.Param[4].StartPos())
    73  		}
    74  	}
    75  
    76  	return nil
    77  }
    78  
    79  func JSON(ctx *runtime.Task, funcExpr *ast.CallExpr) *errchain.PlError {
    80  	var jpath *ast.Node
    81  
    82  	srcKey, err := getKeyName(funcExpr.Param[0])
    83  	if err != nil {
    84  		return runtime.NewRunError(ctx, err.Error(), funcExpr.Param[0].StartPos())
    85  	}
    86  
    87  	switch funcExpr.Param[1].NodeType { //nolint:exhaustive
    88  	case ast.TypeAttrExpr, ast.TypeIdentifier, ast.TypeIndexExpr:
    89  		jpath = funcExpr.Param[1]
    90  	// TODO StringLiteral
    91  	default:
    92  		return runtime.NewRunError(ctx, fmt.Sprintf("expect AttrExpr or Identifier, got %s",
    93  			funcExpr.Param[1].NodeType), funcExpr.Param[1].StartPos())
    94  	}
    95  
    96  	targetKey, _ := getKeyName(jpath)
    97  
    98  	if funcExpr.Param[2] != nil {
    99  		switch funcExpr.Param[2].NodeType { //nolint:exhaustive
   100  		case ast.TypeAttrExpr, ast.TypeIdentifier, ast.TypeStringLiteral:
   101  			targetKey, _ = getKeyName(funcExpr.Param[2])
   102  		default:
   103  			return runtime.NewRunError(ctx, fmt.Sprintf("expect AttrExpr or Identifier, got %s",
   104  				funcExpr.Param[2].NodeType), funcExpr.Param[2].StartPos())
   105  		}
   106  	}
   107  
   108  	cont, err := ctx.GetKeyConv2Str(srcKey)
   109  	if err != nil {
   110  		l.Debug(err)
   111  		return nil
   112  	}
   113  
   114  	deleteAfterExtract := false
   115  	if funcExpr.Param[4] != nil {
   116  		switch funcExpr.Param[4].NodeType { //nolint:exhaustive
   117  		case ast.TypeBoolLiteral:
   118  			deleteAfterExtract = funcExpr.Param[4].BoolLiteral().Val
   119  		default:
   120  			return runtime.NewRunError(ctx, fmt.Sprintf("expect BoolLiteral, got %s",
   121  				funcExpr.Param[3].NodeType), funcExpr.Param[4].StartPos())
   122  		}
   123  	}
   124  
   125  	v, dstS, err := GsonGet(cont, jpath, deleteAfterExtract)
   126  	if err != nil {
   127  		l.Debug(err)
   128  		return nil
   129  	}
   130  
   131  	trimSpace := true
   132  	if funcExpr.Param[3] != nil {
   133  		switch funcExpr.Param[3].NodeType { //nolint:exhaustive
   134  		case ast.TypeBoolLiteral:
   135  			trimSpace = funcExpr.Param[3].BoolLiteral().Val
   136  		default:
   137  			return runtime.NewRunError(ctx, fmt.Sprintf("expect BoolLiteral, got %s",
   138  				funcExpr.Param[3].NodeType), funcExpr.Param[3].StartPos())
   139  		}
   140  	}
   141  
   142  	if vStr, ok := v.(string); ok && trimSpace {
   143  		v = strings.TrimSpace(vStr)
   144  	}
   145  
   146  	var dtype ast.DType
   147  	switch v.(type) {
   148  	case bool:
   149  		dtype = ast.Bool
   150  	case float64:
   151  		dtype = ast.Float
   152  	case string:
   153  		dtype = ast.String
   154  	case []any:
   155  		dtype = ast.List
   156  	case map[string]any:
   157  		dtype = ast.Map
   158  	default:
   159  		return nil
   160  	}
   161  	if ok := addKey2PtWithVal(ctx.InData(), targetKey, v, dtype, ptinput.KindPtDefault); !ok {
   162  		return nil
   163  	}
   164  
   165  	if deleteAfterExtract {
   166  		_ = addKey2PtWithVal(ctx.InData(), srcKey, dstS, ast.String, ptinput.KindPtDefault)
   167  	}
   168  
   169  	return nil
   170  }
   171  
   172  func GsonGet(s string, node *ast.Node, deleteAfter bool) (any, string, error) {
   173  	var m any
   174  
   175  	err := json.Unmarshal([]byte(s), &m)
   176  	if err != nil {
   177  		return "", "", err
   178  	}
   179  
   180  	val, err := jsonGet(m, node, deleteAfter)
   181  	if err != nil {
   182  		return "", "", err
   183  	}
   184  
   185  	dst := s
   186  	if deleteAfter {
   187  		dstB, err := json.Marshal(m)
   188  		if err != nil {
   189  			return "", "", err
   190  		}
   191  		dst = string(dstB)
   192  	}
   193  	return val, dst, nil
   194  }
   195  
   196  func jsonGet(val any, node *ast.Node, deleteAfter bool) (any, error) {
   197  	switch node.NodeType { //nolint:exhaustive
   198  	case ast.TypeStringLiteral:
   199  		return getByIdentifier(val, &ast.Identifier{
   200  			Name: node.StringLiteral().Val,
   201  		}, deleteAfter)
   202  	case ast.TypeAttrExpr:
   203  		return getByAttr(val, node.AttrExpr(), deleteAfter)
   204  
   205  	case ast.TypeIdentifier:
   206  		return getByIdentifier(val, node.Identifier(), deleteAfter)
   207  
   208  	case ast.TypeIndexExpr:
   209  		child, err := getByIdentifier(val, node.IndexExpr().Obj, false)
   210  		if err != nil {
   211  			return nil, err
   212  		}
   213  		return getByIndex(child, node.IndexExpr(), 0, deleteAfter)
   214  	default:
   215  		return nil, fmt.Errorf("json unsupport get from %s", node.NodeType)
   216  	}
   217  }
   218  
   219  func getByAttr(val any, i *ast.AttrExpr, deleteAfter bool) (any, error) {
   220  	if i.Attr != nil {
   221  		child, err := jsonGet(val, i.Obj, false)
   222  		if err != nil {
   223  			return nil, err
   224  		}
   225  		return jsonGet(child, i.Attr, deleteAfter)
   226  	} else {
   227  		child, err := jsonGet(val, i.Obj, deleteAfter)
   228  		if err != nil {
   229  			return nil, err
   230  		}
   231  		return child, nil
   232  	}
   233  }
   234  
   235  func getByIdentifier(val any, i *ast.Identifier, deleteAfter bool) (any, error) {
   236  	if i == nil {
   237  		return val, nil
   238  	}
   239  
   240  	switch v := val.(type) {
   241  	case map[string]any:
   242  		if child, ok := v[i.Name]; !ok {
   243  			return nil, fmt.Errorf("%v not found", i.Name)
   244  		} else {
   245  			if deleteAfter {
   246  				delete(v, i.Name)
   247  			}
   248  			return child, nil
   249  		}
   250  	default:
   251  		return nil, fmt.Errorf("%v unsupport identifier get", reflect.TypeOf(v))
   252  	}
   253  }
   254  
   255  func getByIndex(val any, i *ast.IndexExpr, dimension int, deleteAfter bool) (any, error) {
   256  	switch v := val.(type) {
   257  	case []any:
   258  		if dimension >= len(i.Index) {
   259  			return nil, fmt.Errorf("dimension exceed")
   260  		}
   261  
   262  		var index int
   263  
   264  		switch i.Index[dimension].NodeType { //nolint:exhaustive
   265  		case ast.TypeIntegerLiteral:
   266  			index = int(i.Index[dimension].IntegerLiteral().Val)
   267  		case ast.TypeFloatLiteral:
   268  			index = int(i.Index[dimension].FloatLiteral().Val)
   269  
   270  		default:
   271  			return nil, fmt.Errorf("index value is not int")
   272  		}
   273  
   274  		if index < 0 {
   275  			index = len(v) + index
   276  		}
   277  
   278  		if index < 0 || index >= len(v) {
   279  			return nil, fmt.Errorf("index out of range")
   280  		}
   281  
   282  		child := v[index]
   283  		if dimension == len(i.Index)-1 {
   284  			return child, nil
   285  		} else {
   286  			return getByIndex(child, i, dimension+1, deleteAfter)
   287  		}
   288  	default:
   289  		return nil, fmt.Errorf("%v unsupport index get", reflect.TypeOf(v))
   290  	}
   291  }
   292  
   293  func lastIsIndex(expr *ast.Node) (bool, error) {
   294  	switch expr.NodeType { //nolint:exhaustive
   295  	case ast.TypeAttrExpr:
   296  		return lastIsIndex(expr.AttrExpr().Attr)
   297  	case ast.TypeIdentifier:
   298  		return false, nil
   299  	case ast.TypeIndexExpr:
   300  		return true, nil
   301  	default:
   302  		return false, fmt.Errorf("expect AttrExpr, IndexExpr or Identifier, got %s",
   303  			expr.NodeType)
   304  	}
   305  }