code.vegaprotocol.io/vega@v0.79.0/wallet/api/openrpc_go_helper_for_test.go (about)

     1  // Copyright (C) 2023 Gobalsky Labs Limited
     2  //
     3  // This program is free software: you can redistribute it and/or modify
     4  // it under the terms of the GNU Affero General Public License as
     5  // published by the Free Software Foundation, either version 3 of the
     6  // License, or (at your option) any later version.
     7  //
     8  // This program is distributed in the hope that it will be useful,
     9  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    10  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    11  // GNU Affero General Public License for more details.
    12  //
    13  // You should have received a copy of the GNU Affero General Public License
    14  // along with this program.  If not, see <http://www.gnu.org/licenses/>.
    15  
    16  package api_test
    17  
    18  import (
    19  	"fmt"
    20  	"reflect"
    21  	"strings"
    22  	"testing"
    23  )
    24  
    25  func parseASTFromGo(t *testing.T, i interface{}) (astNode, error) {
    26  	t.Helper()
    27  
    28  	if i == nil {
    29  		return astNode{}, nil
    30  	}
    31  
    32  	rootType := reflect.TypeOf(i)
    33  	nestedProperties, err := resolveNestedProperties(rootType)
    34  	if err != nil {
    35  		return astNode{}, fmt.Errorf("could not reflect the AST for struct %q: %w", rootType.String(), err)
    36  	}
    37  
    38  	return astNode{
    39  		nodeType:         nodeTypeObject,
    40  		nestedProperties: nestedProperties,
    41  	}, nil
    42  }
    43  
    44  func resolveNestedProperties(rootType reflect.Type) ([]astNode, error) {
    45  	unwrappedType := unwrapType(rootType)
    46  
    47  	if unwrappedType.Kind() == reflect.Interface {
    48  		return nil, nil
    49  	}
    50  
    51  	if unwrappedType.Kind() == reflect.Array || unwrappedType.Kind() == reflect.Slice {
    52  		return resolveNestedPropertiesForArray(unwrappedType)
    53  	}
    54  
    55  	if unwrappedType.Kind() == reflect.Struct && !structShouldBeString(unwrappedType) {
    56  		return resolveNestedPropertiesForStruct(unwrappedType)
    57  	}
    58  
    59  	if unwrappedType.Kind() == reflect.Map {
    60  		return resolveNestedPropertiesForMap(unwrappedType)
    61  	}
    62  
    63  	return nil, nil
    64  }
    65  
    66  func structShouldBeString(unwrappedType reflect.Type) bool {
    67  	return unwrappedType.String() == "time.Time"
    68  }
    69  
    70  func resolveNestedPropertiesForMap(unwrappedType reflect.Type) ([]astNode, error) {
    71  	elemType := unwrappedType.Elem()
    72  	unwrappedElemType := unwrapType(elemType)
    73  
    74  	jsonTypeForElem, err := goTypeToJSONType(unwrappedElemType)
    75  	if err != nil {
    76  		return nil, fmt.Errorf("could not figure out the type for element field %q: %w", elemType.String(), err)
    77  	}
    78  
    79  	nestedPropertiesForField, err := resolveNestedProperties(unwrappedElemType)
    80  	if err != nil {
    81  		return nil, fmt.Errorf("could not resolve nested properties for field %q: %w", unwrappedElemType.String(), err)
    82  	}
    83  
    84  	return []astNode{{
    85  		nodeType:         jsonTypeForElem,
    86  		nestedProperties: nestedPropertiesForField,
    87  	}}, nil
    88  }
    89  
    90  func resolveNestedPropertiesForArray(unwrappedType reflect.Type) ([]astNode, error) {
    91  	elemType := unwrappedType.Elem()
    92  	unwrappedElemType := unwrapType(elemType)
    93  
    94  	if unwrappedElemType.Kind() == reflect.Struct && !structShouldBeString(unwrappedType) {
    95  		itemsProperty, err := resolveNestedPropertiesForStruct(unwrappedElemType)
    96  		if err != nil {
    97  			return nil, fmt.Errorf("could not reflect on the item properties %q: %w", unwrappedType.String(), err)
    98  		}
    99  		return []astNode{{
   100  			nodeType:         nodeTypeObject,
   101  			nestedProperties: itemsProperty,
   102  		}}, nil
   103  	}
   104  
   105  	jsonTypeForElem, err := goTypeToJSONType(unwrappedElemType)
   106  	if err != nil {
   107  		return nil, fmt.Errorf("could not figure out the type for element field %q: %w", elemType.String(), err)
   108  	}
   109  
   110  	// No name, nor nested properties.
   111  	return []astNode{{nodeType: jsonTypeForElem}}, nil
   112  }
   113  
   114  func resolveNestedPropertiesForStruct(rootType reflect.Type) ([]astNode, error) {
   115  	numField := rootType.NumField()
   116  	nestedProperties := make([]astNode, 0, numField)
   117  	numNodesToAccountFor := numField
   118  
   119  	for fieldIdx := 0; fieldIdx < numField; fieldIdx++ {
   120  		field := rootType.Field(fieldIdx)
   121  
   122  		jsonName, shouldBeAccountedFor, err := retrieveFieldName(field)
   123  		if err != nil {
   124  			return nil, fmt.Errorf("could not retrieve the JSON name for field %q at index %d: %w", field.Name, fieldIdx, err)
   125  		}
   126  		if !shouldBeAccountedFor {
   127  			numNodesToAccountFor--
   128  			continue
   129  		}
   130  
   131  		unwrappedType := unwrapType(field.Type)
   132  
   133  		jsonType, err := goTypeToJSONType(unwrappedType)
   134  		if err != nil {
   135  			return nil, fmt.Errorf("could not figure out the type for field %q at index %d: %w", field.Name, fieldIdx, err)
   136  		}
   137  
   138  		var nestedPropertiesForField []astNode
   139  
   140  		if jsonType == nodeTypeArray || jsonType == nodeTypeObject {
   141  			nestedPropertiesForField, err = resolveNestedProperties(unwrappedType)
   142  			if err != nil {
   143  				return nil, fmt.Errorf("could not resolve nested properties for field %q at index %d: %w", field.Name, fieldIdx, err)
   144  			}
   145  		}
   146  
   147  		nestedProperties = append(nestedProperties, astNode{
   148  			name:             jsonName,
   149  			nodeType:         jsonType,
   150  			nestedProperties: nestedPropertiesForField,
   151  		})
   152  	}
   153  
   154  	return deterministNestedProperties(nestedProperties[:numNodesToAccountFor]), nil
   155  }
   156  
   157  func unwrapType(field reflect.Type) reflect.Type {
   158  	if field.Kind() == reflect.Pointer {
   159  		return field.Elem()
   160  	}
   161  	return field
   162  }
   163  
   164  func retrieveFieldName(field reflect.StructField) (string, bool, error) {
   165  	protobufValue, exist := field.Tag.Lookup("protobuf")
   166  	if exist {
   167  		names := strings.Split(protobufValue, ",")
   168  		for _, name := range names {
   169  			if strings.HasPrefix(name, "json=") {
   170  				return name[5:], true, nil
   171  			}
   172  		}
   173  	}
   174  
   175  	protobufOneofValue, exist := field.Tag.Lookup("protobuf_oneof")
   176  	if exist {
   177  		return protobufOneofValue, true, nil
   178  	}
   179  
   180  	jsonValue, exist := field.Tag.Lookup("json")
   181  	if !exist {
   182  		if field.IsExported() {
   183  			return "", false, fmt.Errorf("field is exported but does not have a JSON tag")
   184  		}
   185  
   186  		// No json tag, so it is not meant to be used in the API.
   187  		return "", false, nil
   188  	}
   189  
   190  	// the first value is the name in the JSON tag
   191  	jsonName := strings.Split(jsonValue, ",")[0]
   192  
   193  	if strings.ToLower(field.Name) != strings.ToLower(jsonName) { //nolint:staticcheck
   194  		return "", false, fmt.Errorf("field name %q does not match JSON name %q", field.Name, jsonName)
   195  	}
   196  
   197  	return jsonName, true, nil
   198  }
   199  
   200  func goTypeToJSONType(fieldType reflect.Type) (nodeType, error) {
   201  	switch fieldType.Kind() {
   202  	case reflect.String:
   203  		return nodeTypeString, nil
   204  	case reflect.Int,
   205  		reflect.Int8,
   206  		reflect.Int16,
   207  		reflect.Int32,
   208  		reflect.Int64,
   209  		reflect.Uint,
   210  		reflect.Uint8,
   211  		reflect.Uint16,
   212  		reflect.Uint32,
   213  		reflect.Uint64,
   214  		reflect.Uintptr,
   215  		reflect.Float32,
   216  		reflect.Float64,
   217  		reflect.Complex64,
   218  		reflect.Complex128:
   219  		return nodeTypeNumber, nil
   220  	case reflect.Bool:
   221  		return nodeTypeBoolean, nil
   222  	case reflect.Struct, reflect.Map, reflect.Interface:
   223  		if structShouldBeString(fieldType) {
   224  			return nodeTypeString, nil
   225  		}
   226  		return nodeTypeObject, nil
   227  	case reflect.Array, reflect.Slice:
   228  		if fieldType.Elem().Kind() == reflect.Uint8 {
   229  			return nodeTypeString, nil
   230  		}
   231  		return nodeTypeArray, nil
   232  	default:
   233  		return nodeTypeUnknown, fmt.Errorf("struct type %q cannot be converted to node type", fieldType.Kind().String())
   234  	}
   235  }