Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions .apigentools-info
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@
"spec_versions": {
"v1": {
"apigentools_version": "1.4.1.dev11",
"regenerated": "2021-08-17 12:43:10.074159",
"spec_repo_commit": "20a3ffe"
"regenerated": "2021-08-17 14:45:58.123500",
"spec_repo_commit": "465f7d3"
},
"v2": {
"apigentools_version": "1.4.1.dev11",
"regenerated": "2021-08-17 12:43:40.554365",
"spec_repo_commit": "20a3ffe"
"regenerated": "2021-08-17 14:46:23.827609",
"spec_repo_commit": "465f7d3"
}
}
}
4 changes: 4 additions & 0 deletions .generator/templates/.editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
root = true

[*]
indent_style = tab
2 changes: 1 addition & 1 deletion .generator/templates/model_oneof.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ type {{classname}} struct {
{{#lambda.titlecase}}{{{.}}}{{/lambda.titlecase}} *{{{.}}}
{{/oneOf}}

// UnparsedObject contains the raw value of the object if there was an error when deserializing into the struct
// UnparsedObject contains the raw value of the object if there was an error when deserializing into the struct
UnparsedObject interface{}
}

Expand Down
2 changes: 1 addition & 1 deletion .generator/templates/model_simple.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -438,7 +438,7 @@ func (o *{{{classname}}}) UnmarshalJSON(bytes []byte) (err error) {
}
{{#vars}}
{{#allowableValues}}
if v := all.{{name}}; {{^required}}v != nil && {{/required}}!v.IsValid() {
if v := all.{{name}}; {{^required}}{{^isNullable}}v != nil && {{/isNullable}}{{/required}}!v.IsValid() {
err = json.Unmarshal(bytes, &raw)
if err != nil {
return err
Expand Down
40 changes: 40 additions & 0 deletions .generator/templates/utils.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -317,3 +317,43 @@ func (v *NullableTime) UnmarshalJSON(src []byte) error {
v.isSet = true
return json.Unmarshal(src, &v.value)
}

func ContainsUnparsedObject(i interface{}) bool {
v := reflect.ValueOf(i)
switch v.Kind() {
case reflect.Array, reflect.Slice:
for i := 0; i < v.Len(); i++ {
if ContainsUnparsedObject(v.Index(i).Interface()) {
return true
}
}
case reflect.Map:
for _, k := range v.MapKeys() {
if ContainsUnparsedObject(v.MapIndex(k).Interface()) {
return true
}
}
case reflect.Struct:
if u := v.FieldByName("UnparsedObject"); u.IsValid() && !u.IsNil() {
return true
}
for i := 0; i < v.NumField(); i++ {
if fn := v.Type().Field(i).Name; string(fn[0]) == strings.ToUpper(string(fn[0])) && fn != "UnparsedObject" && ContainsUnparsedObject(v.Field(i).Interface()) {
return true
} else if fn == "value" { // Special case for Nullables
if get := v.MethodByName("Get"); get.IsValid() && ContainsUnparsedObject(get.Call([]reflect.Value{})[0].Interface()) {
return true
}
}
}
case reflect.Interface, reflect.Ptr:
if !v.IsNil() {
return ContainsUnparsedObject(v.Elem().Interface())
}
default:
if m := v.MethodByName("IsValid"); m.IsValid() {
return !m.Call([]reflect.Value{})[0].Bool()
}
}
return false
}
42 changes: 42 additions & 0 deletions api/v1/datadog/utils.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

42 changes: 42 additions & 0 deletions api/v2/datadog/utils.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions tests/api/deserialization_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ func TestDeserializationUnkownNestedOneOfInList(t *testing.T) {
assert.Len(resp.Config.GetAssertions(), 3)
// Unknown assertion is Unparsed
assert.Equal("A non existent operator", resp.Config.GetAssertions()[2].UnparsedObject.(map[string]interface{})["operator"])
assert.True(datadogV1.ContainsUnparsedObject(resp))
}

func TestDeserializationUnkownNestedEnumInList(t *testing.T) {
Expand Down Expand Up @@ -190,6 +191,7 @@ func TestDeserializationUnkownNestedEnumInList(t *testing.T) {
// Options object has the 3 expected device IDs
assert.Len(resp.Options.GetDeviceIds(), 3)
assert.Equal(datadogV1.SyntheticsDeviceID("A non existent device ID"), resp.Options.GetDeviceIds()[2])
assert.True(datadogV1.ContainsUnparsedObject(resp))
}

func TestDeserializationUnkownTopLevelEnum(t *testing.T) {
Expand Down Expand Up @@ -251,6 +253,7 @@ func TestDeserializationUnkownTopLevelEnum(t *testing.T) {
assert.NotNil(resp.UnparsedObject)
assert.Equal("A non existent test type", resp.UnparsedObject["type"])
assert.Equal("Check on www.10.0.0.1.xip.io", resp.UnparsedObject["name"])
assert.True(datadogV1.ContainsUnparsedObject(resp))
}

func TestDeserializationUnkownNestedEnum(t *testing.T) {
Expand Down Expand Up @@ -315,6 +318,7 @@ func TestDeserializationUnkownNestedEnum(t *testing.T) {
assert.NotNil(resp.Config.Request.UnparsedObject)
assert.Equal("A non existent method", resp.Config.Request.UnparsedObject["method"])
assert.Equal(float64(30), resp.Config.Request.UnparsedObject["timeout"])
assert.True(datadogV1.ContainsUnparsedObject(resp))
}

func TestDeserializationUnkownNestedOneOf(t *testing.T) {
Expand Down Expand Up @@ -368,4 +372,5 @@ func TestDeserializationUnkownNestedOneOf(t *testing.T) {
// OneOf is unparsed
assert.NotNil(resp.Data.Attributes.Destination.Get().UnparsedObject)
assert.Equal("A non existent destination", resp.Data.Attributes.Destination.Get().UnparsedObject.(map[string]interface{})["type"])
assert.True(datadogV1.ContainsUnparsedObject(resp))
}
82 changes: 82 additions & 0 deletions tests/api/utils_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package api

import (
"context"
"testing"

datadogV1 "github.com/DataDog/datadog-api-client-go/api/v1/datadog"
datadogV2 "github.com/DataDog/datadog-api-client-go/api/v2/datadog"
"github.com/DataDog/datadog-api-client-go/tests"
)

func TestContainsUnparsedObject(t *testing.T) {
assert := tests.Assert(context.Background(), t)
testCases := []struct {
name string
value interface{}
expected bool
}{
{
"top level unparsed struct",
datadogV1.Dashboard{UnparsedObject: map[string]interface{}{"foo": "bar"}},
true,
},
{
"nested unparsed struct",
datadogV1.SyntheticsAPITest{Name: datadogV1.PtrString("foo"), Config: &datadogV1.SyntheticsAPITestConfig{UnparsedObject: map[string]interface{}{"foo": "bar"}}},
true,
},
{
"unparsed struct in array",
datadogV1.Dashboard{Widgets: []datadogV1.Widget{{Definition: datadogV1.WidgetDefinition{}}, {UnparsedObject: map[string]interface{}{"foo": "bar"}}}},
true,
},
{
"unparsed enum in array",
datadogV1.SyntheticsTestOptions{DeviceIds: &[]datadogV1.SyntheticsDeviceID{"edge", "foobar"}},
true,
},
{
"unparsed enum in map",
map[string]datadogV1.SyntheticsDeviceID{"foo": "edge", "bar": "foobar"},
true,
},
{
"unparsed nullable",
datadogV2.NewNullableLogsArchiveDestination(&datadogV2.LogsArchiveDestination{UnparsedObject: map[string]interface{}{"foo": "bar"}}),
true,
},
{
"unparsed nested in nullable",
datadogV2.NewNullableLogsArchiveDestination(&datadogV2.LogsArchiveDestination{LogsArchiveDestinationAzure: &datadogV2.LogsArchiveDestinationAzure{UnparsedObject: map[string]interface{}{"foo": "bar"}}}),
true,
},
{
"valid nullable",
datadogV2.NewNullableLogsArchiveDestination(&datadogV2.LogsArchiveDestination{LogsArchiveDestinationAzure: &datadogV2.LogsArchiveDestinationAzure{Type: datadogV2.LOGSARCHIVEDESTINATIONAZURETYPE_AZURE}}),
false,
},
{
"valid struct",
datadogV1.SyntheticsAPITest{Name: datadogV1.PtrString("foo"), Config: &datadogV1.SyntheticsAPITestConfig{Assertions: &[]datadogV1.SyntheticsAssertion{{SyntheticsAssertionTarget: &datadogV1.SyntheticsAssertionTarget{Type: datadogV1.SYNTHETICSASSERTIONTYPE_BODY}}}}},
false,
},
{
"valid simple type",
"a simple string",
false,
},
{
"valid simple pointer",
datadogV1.PtrString("a simple pointer to string"),
false,
},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
assert.Equal(tc.expected, datadogV1.ContainsUnparsedObject(tc.value))
})
}
}