...

Source file src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/objectmeta/jsonpath_test.go

Documentation: k8s.io/apiextensions-apiserver/pkg/apiserver/schema/objectmeta

     1  /*
     2  Copyright 2018 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package objectmeta
    18  
    19  import (
    20  	"bytes"
    21  	"fmt"
    22  
    23  	"k8s.io/apimachinery/pkg/runtime"
    24  )
    25  
    26  type (
    27  	jsonPathNode struct {
    28  		index *int
    29  		field string
    30  	}
    31  	JSONPath []jsonPathNode
    32  )
    33  
    34  func (p JSONPath) String() string {
    35  	var buf bytes.Buffer
    36  	for _, n := range p {
    37  		if n.index == nil {
    38  			buf.WriteString("." + n.field)
    39  		} else {
    40  			buf.WriteString(fmt.Sprintf("[%d]", *n.index))
    41  		}
    42  	}
    43  	return buf.String()
    44  }
    45  
    46  func jsonPaths(base JSONPath, j map[string]interface{}) []JSONPath {
    47  	res := make([]JSONPath, 0, len(j))
    48  	for k, old := range j {
    49  		kPth := append(append([]jsonPathNode(nil), base...), jsonPathNode{field: k})
    50  		res = append(res, kPth)
    51  
    52  		switch old := old.(type) {
    53  		case map[string]interface{}:
    54  			res = append(res, jsonPaths(kPth, old)...)
    55  		case []interface{}:
    56  			res = append(res, jsonIterSlice(kPth, old)...)
    57  		}
    58  	}
    59  	return res
    60  }
    61  
    62  func jsonIterSlice(base JSONPath, j []interface{}) []JSONPath {
    63  	res := make([]JSONPath, 0, len(j))
    64  	for i, old := range j {
    65  		index := i
    66  		iPth := append(append([]jsonPathNode(nil), base...), jsonPathNode{index: &index})
    67  		res = append(res, iPth)
    68  
    69  		switch old := old.(type) {
    70  		case map[string]interface{}:
    71  			res = append(res, jsonPaths(iPth, old)...)
    72  		case []interface{}:
    73  			res = append(res, jsonIterSlice(iPth, old)...)
    74  		}
    75  	}
    76  	return res
    77  }
    78  
    79  func JSONPathValue(j map[string]interface{}, pth JSONPath, base int) (interface{}, error) {
    80  	if len(pth) == base {
    81  		return nil, fmt.Errorf("empty json path is invalid for object")
    82  	}
    83  	if pth[base].index != nil {
    84  		return nil, fmt.Errorf("index json path is invalid for object")
    85  	}
    86  	field, ok := j[pth[base].field]
    87  	if !ok || len(pth) == base+1 {
    88  		if len(pth) > base+1 {
    89  			return nil, fmt.Errorf("invalid non-terminal json path %q for non-existing field", pth)
    90  		}
    91  		return j[pth[base].field], nil
    92  	}
    93  	switch field := field.(type) {
    94  	case map[string]interface{}:
    95  		return JSONPathValue(field, pth, base+1)
    96  	case []interface{}:
    97  		return jsonPathValueSlice(field, pth, base+1)
    98  	default:
    99  		return nil, fmt.Errorf("invalid non-terminal json path %q for field", pth[:base+1])
   100  	}
   101  }
   102  
   103  func jsonPathValueSlice(j []interface{}, pth JSONPath, base int) (interface{}, error) {
   104  	if len(pth) == base {
   105  		return nil, fmt.Errorf("empty json path %q is invalid for object", pth)
   106  	}
   107  	if pth[base].index == nil {
   108  		return nil, fmt.Errorf("field json path %q is invalid for object", pth[:base+1])
   109  	}
   110  	if *pth[base].index >= len(j) {
   111  		return nil, fmt.Errorf("invalid index %q for array of size %d", pth[:base+1], len(j))
   112  	}
   113  	if len(pth) == base+1 {
   114  		return j[*pth[base].index], nil
   115  	}
   116  	switch item := j[*pth[base].index].(type) {
   117  	case map[string]interface{}:
   118  		return JSONPathValue(item, pth, base+1)
   119  	case []interface{}:
   120  		return jsonPathValueSlice(item, pth, base+1)
   121  	default:
   122  		return nil, fmt.Errorf("invalid non-terminal json path %q for index", pth[:base+1])
   123  	}
   124  }
   125  
   126  func SetJSONPath(j map[string]interface{}, pth JSONPath, base int, value interface{}) error {
   127  	if len(pth) == base {
   128  		return fmt.Errorf("empty json path is invalid for object")
   129  	}
   130  	if pth[base].index != nil {
   131  		return fmt.Errorf("index json path is invalid for object")
   132  	}
   133  	field, ok := j[pth[base].field]
   134  	if !ok || len(pth) == base+1 {
   135  		if len(pth) > base+1 {
   136  			return fmt.Errorf("invalid non-terminal json path %q for non-existing field", pth)
   137  		}
   138  		j[pth[base].field] = runtime.DeepCopyJSONValue(value)
   139  		return nil
   140  	}
   141  	switch field := field.(type) {
   142  	case map[string]interface{}:
   143  		return SetJSONPath(field, pth, base+1, value)
   144  	case []interface{}:
   145  		return setJSONPathSlice(field, pth, base+1, value)
   146  	default:
   147  		return fmt.Errorf("invalid non-terminal json path %q for field", pth[:base+1])
   148  	}
   149  }
   150  
   151  func setJSONPathSlice(j []interface{}, pth JSONPath, base int, value interface{}) error {
   152  	if len(pth) == base {
   153  		return fmt.Errorf("empty json path %q is invalid for object", pth)
   154  	}
   155  	if pth[base].index == nil {
   156  		return fmt.Errorf("field json path %q is invalid for object", pth[:base+1])
   157  	}
   158  	if *pth[base].index >= len(j) {
   159  		return fmt.Errorf("invalid index %q for array of size %d", pth[:base+1], len(j))
   160  	}
   161  	if len(pth) == base+1 {
   162  		j[*pth[base].index] = runtime.DeepCopyJSONValue(value)
   163  		return nil
   164  	}
   165  	switch item := j[*pth[base].index].(type) {
   166  	case map[string]interface{}:
   167  		return SetJSONPath(item, pth, base+1, value)
   168  	case []interface{}:
   169  		return setJSONPathSlice(item, pth, base+1, value)
   170  	default:
   171  		return fmt.Errorf("invalid non-terminal json path %q for index", pth[:base+1])
   172  	}
   173  }
   174  
   175  func DeleteJSONPath(j map[string]interface{}, pth JSONPath, base int) error {
   176  	if len(pth) == base {
   177  		return fmt.Errorf("empty json path is invalid for object")
   178  	}
   179  	if pth[base].index != nil {
   180  		return fmt.Errorf("index json path is invalid for object")
   181  	}
   182  	field, ok := j[pth[base].field]
   183  	if !ok || len(pth) == base+1 {
   184  		if len(pth) > base+1 {
   185  			return fmt.Errorf("invalid non-terminal json path %q for non-existing field", pth)
   186  		}
   187  		delete(j, pth[base].field)
   188  		return nil
   189  	}
   190  	switch field := field.(type) {
   191  	case map[string]interface{}:
   192  		return DeleteJSONPath(field, pth, base+1)
   193  	case []interface{}:
   194  		if len(pth) == base+2 {
   195  			if pth[base+1].index == nil {
   196  				return fmt.Errorf("field json path %q is invalid for object", pth)
   197  			}
   198  			j[pth[base].field] = append(field[:*pth[base+1].index], field[*pth[base+1].index+1:]...)
   199  			return nil
   200  		}
   201  		return deleteJSONPathSlice(field, pth, base+1)
   202  	default:
   203  		return fmt.Errorf("invalid non-terminal json path %q for field", pth[:base+1])
   204  	}
   205  }
   206  
   207  func deleteJSONPathSlice(j []interface{}, pth JSONPath, base int) error {
   208  	if len(pth) == base {
   209  		return fmt.Errorf("empty json path %q is invalid for object", pth)
   210  	}
   211  	if pth[base].index == nil {
   212  		return fmt.Errorf("field json path %q is invalid for object", pth[:base+1])
   213  	}
   214  	if *pth[base].index >= len(j) {
   215  		return fmt.Errorf("invalid index %q for array of size %d", pth[:base+1], len(j))
   216  	}
   217  	if len(pth) == base+1 {
   218  		return fmt.Errorf("cannot delete item at index %q in-place", pth[:base])
   219  	}
   220  	switch item := j[*pth[base].index].(type) {
   221  	case map[string]interface{}:
   222  		return DeleteJSONPath(item, pth, base+1)
   223  	case []interface{}:
   224  		if len(pth) == base+2 {
   225  			if pth[base+1].index == nil {
   226  				return fmt.Errorf("field json path %q is invalid for object", pth)
   227  			}
   228  			j[*pth[base].index] = append(item[:*pth[base+1].index], item[*pth[base+1].index+1:])
   229  			return nil
   230  		}
   231  		return deleteJSONPathSlice(item, pth, base+1)
   232  	default:
   233  		return fmt.Errorf("invalid non-terminal json path %q for index", pth[:base+1])
   234  	}
   235  }
   236  

View as plain text