From cc8348dc2a882ddb46c0306ca896e9b2078a510d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luka=20Perovi=C4=87?= <32458554+lukap3@users.noreply.github.com> Date: Thu, 6 Oct 2022 13:27:44 +0200 Subject: [PATCH 1/7] Move test types to test_types package --- openapi/generator_test.go | 81 +++++++----------------------------- testdata/test_types/types.go | 53 +++++++++++++++++++++++ 2 files changed, 69 insertions(+), 65 deletions(-) create mode 100644 testdata/test_types/types.go diff --git a/openapi/generator_test.go b/openapi/generator_test.go index ffad7df..bd0d502 100644 --- a/openapi/generator_test.go +++ b/openapi/generator_test.go @@ -3,6 +3,7 @@ package openapi import ( "encoding/json" "fmt" + "github.com/wI2L/fizz/testdata/test_types" "io/ioutil" "math" "reflect" @@ -26,56 +27,6 @@ var genConfig = &SpecGenConfig{ var rt = reflect.TypeOf -type ( - W struct { - A, B string - } - u struct { - S int - } - q int - X struct { - *X // ignored, recursive embedding - *Y - A string `validate:"required"` - B *int - C bool `deprecated:"true"` - D []*Y - E [3]*X - F *X - G *Y - H map[int]*Y // ignored, unsupported keys type - *u - uu *u // ignored, unexported field - q // ignored, embedded field of non-struct type - *Q - *V `json:"data"` - } - Y struct { - H float32 `validate:"required"` - I time.Time `format:"date"` - J *uint8 `deprecated:"oui"` // invalid value, interpreted as false - K *Z `validate:"required"` - N struct { - Na, Nb string - Nc time.Duration - } - l int // ignored - M int `json:"-"` - } - Z map[string]*Y - Q struct { - NnNnnN string `json:"nnNnnN"` - } - V struct { - L int - } -) - -func (*X) TypeName() string { return "XXX" } -func (*W) Format() string { return "wallet" } -func (*W) Type() string { return "string" } - // TestStructFieldName tests that the name of a // struct field can be correctly extracted. func TestStructFieldName(t *testing.T) { @@ -190,7 +141,7 @@ func TestSchemaFromComplex(t *testing.T) { g := gen(t) g.UseFullSchemaNames(false) - sor := g.newSchemaFromType(rt(new(X))) + sor := g.newSchemaFromType(rt(new(test_types.X))) assert.NotNil(t, sor) b, err := json.Marshal(sor) @@ -465,7 +416,7 @@ func TestAddOperation(t *testing.T) { }, }, } - _, err := g.AddOperation(path, "POST", "Test", reflect.TypeOf(&In{}), reflect.TypeOf(Z{}), infos) + _, err := g.AddOperation(path, "POST", "Test", reflect.TypeOf(&In{}), reflect.TypeOf(test_types.Z{}), infos) if err != nil { t.Error(err) } @@ -508,7 +459,7 @@ func TestAddOperation(t *testing.T) { } // Try to add the operation again with the same // identifier. Expected to fail. - _, err = g.AddOperation(path, "POST", "Test", reflect.TypeOf(&In{}), reflect.TypeOf(Z{}), infos) + _, err = g.AddOperation(path, "POST", "Test", reflect.TypeOf(&In{}), reflect.TypeOf(test_types.Z{}), infos) assert.NotNil(t, err) // Add an operation with a bad input type. @@ -524,25 +475,25 @@ func TestTypeName(t *testing.T) { t.Error(err) } // Typer interface. - name := g.typeName(rt(new(X))) + name := g.typeName(rt(new(test_types.X))) assert.Equal(t, "XXX", name) // Override. This has precedence // over the interface implementation. - err = g.OverrideTypeName(rt(new(X)), "") + err = g.OverrideTypeName(rt(new(test_types.X)), "") assert.NotNil(t, err) - assert.Equal(t, "XXX", g.typeName(rt(new(X)))) + assert.Equal(t, "XXX", g.typeName(rt(new(test_types.X)))) - g.OverrideTypeName(rt(new(X)), "xXx") - assert.Equal(t, "xXx", g.typeName(rt(X{}))) + g.OverrideTypeName(rt(new(test_types.X)), "xXx") + assert.Equal(t, "xXx", g.typeName(rt(test_types.X{}))) - err = g.OverrideTypeName(rt(new(X)), "YYY") + err = g.OverrideTypeName(rt(new(test_types.X)), "YYY") assert.NotNil(t, err) // Default. - assert.Equal(t, "OpenapiY", g.typeName(rt(new(Y)))) + assert.Equal(t, "Test_typesY", g.typeName(rt(new(test_types.Y)))) g.UseFullSchemaNames(false) - assert.Equal(t, "Y", g.typeName(rt(Y{}))) + assert.Equal(t, "Y", g.typeName(rt(test_types.Y{}))) // Unnamed type. assert.Equal(t, "", g.typeName(rt(struct{}{}))) @@ -708,18 +659,18 @@ func TestOverrideSchema(t *testing.T) { g := gen(t) // Type is mandatory. - err := g.OverrideDataType(rt(W{}), "", "wallet") + err := g.OverrideDataType(rt(test_types.W{}), "", "wallet") assert.NotNil(t, err) // Success. - err = g.OverrideDataType(rt(&W{}), "string", "wallet") + err = g.OverrideDataType(rt(&test_types.W{}), "string", "wallet") assert.Nil(t, err) // Data type already overidden. - err = g.OverrideDataType(rt(&W{}), "string", "wallet") + err = g.OverrideDataType(rt(&test_types.W{}), "string", "wallet") assert.NotNil(t, err) - sor := g.newSchemaFromType(rt(W{})) + sor := g.newSchemaFromType(rt(test_types.W{})) assert.NotNil(t, sor) schema := g.resolveSchema(sor) diff --git a/testdata/test_types/types.go b/testdata/test_types/types.go new file mode 100644 index 0000000..e5d8594 --- /dev/null +++ b/testdata/test_types/types.go @@ -0,0 +1,53 @@ +package test_types + +import "time" + +type ( + W struct { + A, B string + } + u struct { + S int + } + q int + X struct { + *X // ignored, recursive embedding + *Y + A string `validate:"required"` + B *int + C bool `deprecated:"true"` + D []*Y + E [3]*X + F *X + G *Y + H map[int]*Y // ignored, unsupported keys type + *u + uu *u // ignored, unexported field + q // ignored, embedded field of non-struct type + *Q + *V `json:"data"` + } + Y struct { + H float32 `validate:"required"` + I time.Time `format:"date"` + J *uint8 `deprecated:"oui"` // invalid value, interpreted as false + K *Z `validate:"required"` + N struct { + Na, Nb string + Nc time.Duration + } + l int // ignored + M int `json:"-"` + } + Z map[string]*Y + Q struct { + NnNnnN string `json:"nnNnnN"` + } + V struct { + L int + } +) + +func (*X) TypeName() string { return "XXX" } +func (*W) Format() string { return "wallet" } +func (*W) Type() string { return "string" } From 7429ccf4e011469f1dbbe23e183de7ff72dad6fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luka=20Perovi=C4=87?= <32458554+lukap3@users.noreply.github.com> Date: Thu, 6 Oct 2022 13:29:25 +0200 Subject: [PATCH 2/7] Add additional "duplicated" types --- testdata/test_types_extra/types.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 testdata/test_types_extra/types.go diff --git a/testdata/test_types_extra/types.go b/testdata/test_types_extra/types.go new file mode 100644 index 0000000..4f41d23 --- /dev/null +++ b/testdata/test_types_extra/types.go @@ -0,0 +1,12 @@ +package test_types_extra + +import "github.com/wI2L/fizz/testdata/test_types" + +type W struct { + C, D int +} + +type D struct { + W W + Wd test_types.W +} From 28563807334a9896bd0f8d51ef3c2b0fc48f0c08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luka=20Perovi=C4=87?= <32458554+lukap3@users.noreply.github.com> Date: Thu, 6 Oct 2022 13:30:00 +0200 Subject: [PATCH 3/7] Add error for duplicated type names --- openapi/generator.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/openapi/generator.go b/openapi/generator.go index 7156866..e4b383e 100644 --- a/openapi/generator.go +++ b/openapi/generator.go @@ -987,6 +987,12 @@ func (g *Generator) newSchemaFromStruct(t reflect.Type) *SchemaOrRef { // relative reference. Unnamed types, like anonymous structs, // will always be inlined in the specification. if name != "" { + if _, ok := g.api.Components.Schemas[name]; ok { + g.error(&TypeError{ + Message: "encountered duplicated type", + Type: t, + }) + } g.api.Components.Schemas[name] = sor return &SchemaOrRef{Reference: &Reference{ From d8542062faafbfd6a7894494fa83eb2a68b8229c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luka=20Perovi=C4=87?= <32458554+lukap3@users.noreply.github.com> Date: Thu, 6 Oct 2022 13:30:14 +0200 Subject: [PATCH 4/7] Add test for duplicated types --- openapi/generator_test.go | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/openapi/generator_test.go b/openapi/generator_test.go index bd0d502..28510c4 100644 --- a/openapi/generator_test.go +++ b/openapi/generator_test.go @@ -4,6 +4,7 @@ import ( "encoding/json" "fmt" "github.com/wI2L/fizz/testdata/test_types" + "github.com/wI2L/fizz/testdata/test_types_extra" "io/ioutil" "math" "reflect" @@ -135,6 +136,19 @@ func TestSchemaFromMapWithUnsupportedKeys(t *testing.T) { assert.NotEmpty(t, g.Errors()[0].Error()) } +// TestSchemaFromDuplicatedType tests that a +// schema cannot be created with 2 types with +// the same name +func TestSchemaFromDuplicatedType(t *testing.T) { + g := gen(t) + + schema := g.newSchemaFromType(rt(new(test_types_extra.D))) + assert.NotNil(t, schema) + assert.Len(t, g.Errors(), 1) + assert.Implements(t, (*error)(nil), g.Errors()[0]) + assert.NotEmpty(t, g.Errors()[0]) +} + // TestSchemaFromComplex tests that a schema // can be created from a complex type. func TestSchemaFromComplex(t *testing.T) { From 57b6a776bae3423ce1c45f1d16280155674b4c88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luka=20Perovi=C4=87?= <32458554+lukap3@users.noreply.github.com> Date: Thu, 6 Oct 2022 18:06:48 +0200 Subject: [PATCH 5/7] Move test types --- openapi/generator_test.go | 36 +++++++++---------- .../test_types/base_types}/types.go | 2 +- openapi/test_types/extra_types/types.go | 14 ++++++++ testdata/test_types_extra/types.go | 12 ------- 4 files changed, 33 insertions(+), 31 deletions(-) rename {testdata/test_types => openapi/test_types/base_types}/types.go (97%) create mode 100644 openapi/test_types/extra_types/types.go delete mode 100644 testdata/test_types_extra/types.go diff --git a/openapi/generator_test.go b/openapi/generator_test.go index 28510c4..08cb23d 100644 --- a/openapi/generator_test.go +++ b/openapi/generator_test.go @@ -3,8 +3,8 @@ package openapi import ( "encoding/json" "fmt" - "github.com/wI2L/fizz/testdata/test_types" - "github.com/wI2L/fizz/testdata/test_types_extra" + baseTypes "github.com/wI2L/fizz/openapi/test_types/base_types" + extraTypes "github.com/wI2L/fizz/openapi/test_types/extra_types" "io/ioutil" "math" "reflect" @@ -142,7 +142,7 @@ func TestSchemaFromMapWithUnsupportedKeys(t *testing.T) { func TestSchemaFromDuplicatedType(t *testing.T) { g := gen(t) - schema := g.newSchemaFromType(rt(new(test_types_extra.D))) + schema := g.newSchemaFromType(rt(new(extraTypes.D))) assert.NotNil(t, schema) assert.Len(t, g.Errors(), 1) assert.Implements(t, (*error)(nil), g.Errors()[0]) @@ -155,7 +155,7 @@ func TestSchemaFromComplex(t *testing.T) { g := gen(t) g.UseFullSchemaNames(false) - sor := g.newSchemaFromType(rt(new(test_types.X))) + sor := g.newSchemaFromType(rt(new(baseTypes.X))) assert.NotNil(t, sor) b, err := json.Marshal(sor) @@ -430,7 +430,7 @@ func TestAddOperation(t *testing.T) { }, }, } - _, err := g.AddOperation(path, "POST", "Test", reflect.TypeOf(&In{}), reflect.TypeOf(test_types.Z{}), infos) + _, err := g.AddOperation(path, "POST", "Test", reflect.TypeOf(&In{}), reflect.TypeOf(baseTypes.Z{}), infos) if err != nil { t.Error(err) } @@ -473,7 +473,7 @@ func TestAddOperation(t *testing.T) { } // Try to add the operation again with the same // identifier. Expected to fail. - _, err = g.AddOperation(path, "POST", "Test", reflect.TypeOf(&In{}), reflect.TypeOf(test_types.Z{}), infos) + _, err = g.AddOperation(path, "POST", "Test", reflect.TypeOf(&In{}), reflect.TypeOf(baseTypes.Z{}), infos) assert.NotNil(t, err) // Add an operation with a bad input type. @@ -489,25 +489,25 @@ func TestTypeName(t *testing.T) { t.Error(err) } // Typer interface. - name := g.typeName(rt(new(test_types.X))) + name := g.typeName(rt(new(baseTypes.X))) assert.Equal(t, "XXX", name) // Override. This has precedence // over the interface implementation. - err = g.OverrideTypeName(rt(new(test_types.X)), "") + err = g.OverrideTypeName(rt(new(baseTypes.X)), "") assert.NotNil(t, err) - assert.Equal(t, "XXX", g.typeName(rt(new(test_types.X)))) + assert.Equal(t, "XXX", g.typeName(rt(new(baseTypes.X)))) - g.OverrideTypeName(rt(new(test_types.X)), "xXx") - assert.Equal(t, "xXx", g.typeName(rt(test_types.X{}))) + g.OverrideTypeName(rt(new(baseTypes.X)), "xXx") + assert.Equal(t, "xXx", g.typeName(rt(baseTypes.X{}))) - err = g.OverrideTypeName(rt(new(test_types.X)), "YYY") + err = g.OverrideTypeName(rt(new(baseTypes.X)), "YYY") assert.NotNil(t, err) // Default. - assert.Equal(t, "Test_typesY", g.typeName(rt(new(test_types.Y)))) + assert.Equal(t, "Base_typesY", g.typeName(rt(new(baseTypes.Y)))) g.UseFullSchemaNames(false) - assert.Equal(t, "Y", g.typeName(rt(test_types.Y{}))) + assert.Equal(t, "Y", g.typeName(rt(baseTypes.Y{}))) // Unnamed type. assert.Equal(t, "", g.typeName(rt(struct{}{}))) @@ -673,18 +673,18 @@ func TestOverrideSchema(t *testing.T) { g := gen(t) // Type is mandatory. - err := g.OverrideDataType(rt(test_types.W{}), "", "wallet") + err := g.OverrideDataType(rt(baseTypes.W{}), "", "wallet") assert.NotNil(t, err) // Success. - err = g.OverrideDataType(rt(&test_types.W{}), "string", "wallet") + err = g.OverrideDataType(rt(&baseTypes.W{}), "string", "wallet") assert.Nil(t, err) // Data type already overidden. - err = g.OverrideDataType(rt(&test_types.W{}), "string", "wallet") + err = g.OverrideDataType(rt(&baseTypes.W{}), "string", "wallet") assert.NotNil(t, err) - sor := g.newSchemaFromType(rt(test_types.W{})) + sor := g.newSchemaFromType(rt(baseTypes.W{})) assert.NotNil(t, sor) schema := g.resolveSchema(sor) diff --git a/testdata/test_types/types.go b/openapi/test_types/base_types/types.go similarity index 97% rename from testdata/test_types/types.go rename to openapi/test_types/base_types/types.go index e5d8594..ee223cd 100644 --- a/testdata/test_types/types.go +++ b/openapi/test_types/base_types/types.go @@ -1,4 +1,4 @@ -package test_types +package base_types import "time" diff --git a/openapi/test_types/extra_types/types.go b/openapi/test_types/extra_types/types.go new file mode 100644 index 0000000..d75fa88 --- /dev/null +++ b/openapi/test_types/extra_types/types.go @@ -0,0 +1,14 @@ +package extra_types + +import ( + "github.com/wI2L/fizz/openapi/test_types/base_types" +) + +type W struct { + C, D int +} + +type D struct { + Winternal W + Wexternal base_types.W +} diff --git a/testdata/test_types_extra/types.go b/testdata/test_types_extra/types.go deleted file mode 100644 index 4f41d23..0000000 --- a/testdata/test_types_extra/types.go +++ /dev/null @@ -1,12 +0,0 @@ -package test_types_extra - -import "github.com/wI2L/fizz/testdata/test_types" - -type W struct { - C, D int -} - -type D struct { - W W - Wd test_types.W -} From 97759e71b59dca706806c192953ad7a91935208e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luka=20Perovi=C4=87?= <32458554+lukap3@users.noreply.github.com> Date: Tue, 25 Oct 2022 10:04:21 +0200 Subject: [PATCH 6/7] Add duplicated type error checks --- openapi/generator_test.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/openapi/generator_test.go b/openapi/generator_test.go index 05a7381..972d930 100644 --- a/openapi/generator_test.go +++ b/openapi/generator_test.go @@ -146,7 +146,11 @@ func TestSchemaFromDuplicatedType(t *testing.T) { assert.NotNil(t, schema) assert.Len(t, g.Errors(), 1) assert.Implements(t, (*error)(nil), g.Errors()[0]) - assert.NotEmpty(t, g.Errors()[0]) + + err := g.Errors()[0] + assert.NotEmpty(t, err) + assert.Equal(t, "encountered duplicated type", err.(*TypeError).Message) + assert.Equal(t, rt(baseTypes.W{}), err.(*TypeError).Type) } // TestSchemaFromComplex tests that a schema From 1ea05681e2cd569a819fd1e6cd37b230c5f6b82f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luka=20Perovi=C4=87?= <32458554+lukap3@users.noreply.github.com> Date: Tue, 25 Oct 2022 10:04:37 +0200 Subject: [PATCH 7/7] Add camelcase import alias --- openapi/test_types/extra_types/types.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openapi/test_types/extra_types/types.go b/openapi/test_types/extra_types/types.go index d75fa88..e4735ec 100644 --- a/openapi/test_types/extra_types/types.go +++ b/openapi/test_types/extra_types/types.go @@ -1,7 +1,7 @@ package extra_types import ( - "github.com/wI2L/fizz/openapi/test_types/base_types" + baseTypes "github.com/wI2L/fizz/openapi/test_types/base_types" ) type W struct { @@ -10,5 +10,5 @@ type W struct { type D struct { Winternal W - Wexternal base_types.W + Wexternal baseTypes.W }