зеркало из https://github.com/Azure/ARO-RP.git
207 строки
5.8 KiB
Go
207 строки
5.8 KiB
Go
package arm
|
|
|
|
// Copyright (c) Microsoft Corporation.
|
|
// Licensed under the Apache License 2.0.
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"reflect"
|
|
"strings"
|
|
|
|
sdkcosmos "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/cosmos/armcosmos/v2"
|
|
gofrsuuid "github.com/gofrs/uuid"
|
|
)
|
|
|
|
// MarshalJSON marshals the nested r.Resource ignoring any MarshalJSON() methods
|
|
// on its types. It then merges remaining fields of r over the result
|
|
func (r *Resource) MarshalJSON() ([]byte, error) {
|
|
var b []byte
|
|
var err error
|
|
|
|
// hack to handle newer track2 sdk which doesn't have json tags
|
|
if strings.HasPrefix(r.Type, "Microsoft.DocumentDB/databaseAccounts/sqlDatabases") {
|
|
if reflect.TypeOf(r.Resource) == reflect.TypeOf(&sdkcosmos.SQLDatabaseCreateUpdateParameters{}) {
|
|
b, err = r.Resource.(*sdkcosmos.SQLDatabaseCreateUpdateParameters).MarshalJSON()
|
|
} else if reflect.TypeOf(r.Resource) == reflect.TypeOf(&sdkcosmos.SQLContainerCreateUpdateParameters{}) {
|
|
b, err = r.Resource.(*sdkcosmos.SQLContainerCreateUpdateParameters).MarshalJSON()
|
|
} else if reflect.TypeOf(r.Resource) == reflect.TypeOf(&sdkcosmos.SQLTriggerCreateUpdateParameters{}) {
|
|
b, err = r.Resource.(*sdkcosmos.SQLTriggerCreateUpdateParameters).MarshalJSON()
|
|
}
|
|
} else if strings.HasPrefix(r.Type, "Microsoft.DocumentDB/databaseAccounts") {
|
|
b, err = r.Resource.(*sdkcosmos.DatabaseAccountCreateUpdateParameters).MarshalJSON()
|
|
}
|
|
|
|
if err != nil {
|
|
return b, err
|
|
}
|
|
|
|
if b != nil {
|
|
dataMap := map[string]interface{}{}
|
|
err = json.Unmarshal(b, &dataMap)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
dataMap["apiVersion"] = r.APIVersion
|
|
if r.DependsOn != nil {
|
|
dataMap["dependsOn"] = r.DependsOn
|
|
}
|
|
return json.Marshal(dataMap)
|
|
}
|
|
|
|
resource := reflect.ValueOf(shadowCopy(r.Resource))
|
|
outer := reflect.ValueOf(*r)
|
|
|
|
if resource.Kind() != reflect.Struct {
|
|
return nil, fmt.Errorf("Resource field must be a struct")
|
|
}
|
|
|
|
// Create slices of field types and values that combine `r.Resource` and
|
|
// outer `*r` structs. Fields from the outer struct `*r` override fields
|
|
// from `r.Resource`.
|
|
fields := make([]reflect.StructField, 0, resource.NumField()+outer.NumField())
|
|
values := make([]reflect.Value, 0, resource.NumField()+outer.NumField())
|
|
indexes := map[string]int{}
|
|
|
|
// Copy fields and values from `r.Resource`
|
|
for i := 0; i < resource.NumField(); i++ {
|
|
field := resource.Type().Field(i)
|
|
fields = append(fields, field)
|
|
values = append(values, resource.Field(i))
|
|
indexes[field.Name] = i
|
|
}
|
|
|
|
// Copy fields and values from `*r` and override if they already exist
|
|
for i := 1; i < outer.NumField(); i++ {
|
|
field := outer.Type().Field(i)
|
|
|
|
if j, found := indexes[field.Name]; found {
|
|
field.Type = emptyInterfaceType
|
|
fields[j] = field
|
|
if !outer.Field(i).IsZero() {
|
|
values[j] = outer.Field(i)
|
|
}
|
|
} else {
|
|
fields = append(fields, field)
|
|
values = append(values, outer.Field(i))
|
|
indexes[field.Name] = len(fields) - 1
|
|
}
|
|
}
|
|
|
|
combined := reflect.New(reflect.StructOf(fields)).Elem()
|
|
|
|
for i, v := range values {
|
|
combined.Field(i).Set(v)
|
|
}
|
|
|
|
return json.Marshal(combined.Interface())
|
|
}
|
|
|
|
// UnmarshalJSON is not implemented
|
|
func (r *Resource) UnmarshalJSON(b []byte) error {
|
|
return fmt.Errorf("not implemented")
|
|
}
|
|
|
|
var (
|
|
stringType = reflect.TypeOf("")
|
|
emptyInterfaceType = reflect.ValueOf([]interface{}(nil)).Type().Elem()
|
|
)
|
|
|
|
// shadowCopy returns a copy of the input object wherein all the struct types
|
|
// have been replaced with dynamically created ones. The idea is that the
|
|
// JSONMarshal() methods get dropped in the process and so the returned object
|
|
// marshals natively. Type cycles are permitted, but (as in encoding/json)
|
|
// value cycles are not. Golang reflect doesn't support dynamically creating
|
|
// named types; to get around this we go weakly typed
|
|
func shadowCopy(i interface{}) interface{} {
|
|
return _shadowCopy(reflect.ValueOf(i)).Interface()
|
|
}
|
|
|
|
func _shadowCopy(v reflect.Value) reflect.Value {
|
|
switch v.Kind() {
|
|
case reflect.Array:
|
|
var t reflect.Type
|
|
if v.Type() == reflect.TypeOf(gofrsuuid.UUID{}) {
|
|
// keep uuid.UUID - encoding/json will detect it and marshal it into
|
|
// a string
|
|
t = v.Type()
|
|
} else {
|
|
t = reflect.ArrayOf(v.Len(), emptyInterfaceType)
|
|
}
|
|
a := reflect.New(t).Elem()
|
|
for i := 0; i < v.Len(); i++ {
|
|
a.Index(i).Set(_shadowCopy(v.Index(i)))
|
|
}
|
|
return a
|
|
|
|
case reflect.Interface, reflect.Ptr:
|
|
t := emptyInterfaceType
|
|
if v.IsNil() {
|
|
return reflect.Zero(t)
|
|
}
|
|
i := reflect.New(t).Elem()
|
|
i.Set(_shadowCopy(v.Elem()))
|
|
return i
|
|
|
|
case reflect.Map:
|
|
// this is not fully generic but Go json marshaling requires
|
|
// map[string]interface{}
|
|
t := reflect.MapOf(stringType, emptyInterfaceType)
|
|
if v.IsNil() {
|
|
return reflect.Zero(t)
|
|
}
|
|
m := reflect.MakeMap(t)
|
|
iter := v.MapRange()
|
|
for iter.Next() {
|
|
m.SetMapIndex(iter.Key(), _shadowCopy(iter.Value()))
|
|
}
|
|
return m
|
|
|
|
case reflect.Slice:
|
|
var t reflect.Type
|
|
if v.Type().Elem().Kind() == reflect.Uint8 {
|
|
// keep []byte - encoding/json will detect it and marshal it into a
|
|
// base64 encoded string
|
|
t = v.Type()
|
|
} else {
|
|
t = reflect.SliceOf(emptyInterfaceType)
|
|
}
|
|
if v.IsNil() {
|
|
return reflect.Zero(t)
|
|
}
|
|
s := reflect.MakeSlice(t, v.Len(), v.Len())
|
|
for i := 0; i < v.Len(); i++ {
|
|
s.Index(i).Set(_shadowCopy(v.Index(i)))
|
|
}
|
|
return s
|
|
|
|
case reflect.Struct:
|
|
fields := make([]reflect.StructField, 0, v.Type().NumField())
|
|
values := make([]reflect.Value, 0, v.Type().NumField())
|
|
for i := 0; i < v.Type().NumField(); i++ {
|
|
if v.Type().Field(i).PkgPath != "" {
|
|
continue
|
|
}
|
|
f := _shadowCopy(v.Field(i))
|
|
values = append(values, f)
|
|
|
|
field := v.Type().Field(i)
|
|
field.Type = emptyInterfaceType
|
|
fields = append(fields, field)
|
|
}
|
|
t := reflect.StructOf(fields)
|
|
|
|
s := reflect.New(t).Elem()
|
|
for i, v := range values {
|
|
if !v.IsZero() {
|
|
s.Field(i).Set(v)
|
|
}
|
|
}
|
|
return s
|
|
|
|
default:
|
|
return v
|
|
}
|
|
}
|