Skip to content

Commit 0e87d63

Browse files
committed
fix: handle swagger:type array by falling through to underlying type resolution
When swagger:type is set to a value not handled by swaggerSchemaForType (e.g., "array"), the function returns an error. Previously this error was silently ignored and the function returned nil, losing all type info. Now, when swaggerSchemaForType returns an error, the code falls through to buildFromType with the underlying type, producing the correct schema. For example, swagger:type array on type StringSlice []string now produces {type: "array", items: {type: "string"}} with the field's description preserved, instead of a $ref that drops the description. * fixes #10 Signed-off-by: Kevin Doan <kevin.doan@ory.sh> Signed-off-by: KT-Doan <kevin.doan@ory.sh>
1 parent 4b94238 commit 0e87d63

File tree

4 files changed

+160
-8
lines changed

4 files changed

+160
-8
lines changed

application_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ func TestMain(m *testing.M) {
5151
func TestApplication_LoadCode(t *testing.T) {
5252
sctx := loadClassificationPkgsCtx(t)
5353
require.NotNil(t, sctx)
54-
require.Len(t, sctx.app.Models, 39)
54+
require.Len(t, sctx.app.Models, 45)
5555
require.Len(t, sctx.app.Meta, 1)
5656
require.Len(t, sctx.app.Routes, 7)
5757
require.Empty(t, sctx.app.Operations)

fixtures/goparsing/classification/models/nomodel.go

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -799,3 +799,52 @@ type NamedMapOfStoreOrderSlices GenericMap[string, GenericSlice[StoreOrder]]
799799
//
800800
// End of models related to named types with type arguments
801801
//
802+
803+
// SomeStringSlice is a named slice type with swagger:type array.
804+
// swagger:type array
805+
type SomeStringSlice []string
806+
807+
// swagger:model namedWithArrayType
808+
type NamedWithArrayType struct {
809+
// Tags for this item.
810+
Tags SomeStringSlice `json:"tags"`
811+
}
812+
813+
// SomeFixedArray is a named fixed-length array type with swagger:type array.
814+
// swagger:type array
815+
type SomeFixedArray [5]string
816+
817+
// swagger:model namedWithFixedArrayType
818+
type NamedWithFixedArrayType struct {
819+
// Labels for this item.
820+
Labels SomeFixedArray `json:"labels"`
821+
}
822+
823+
// SomeStructWithBadType is a struct type with an unsupported swagger:type value.
824+
// The unsupported value should be ignored and the type should fall through to $ref.
825+
//
826+
// swagger:type badvalue
827+
// swagger:model someStructWithBadType
828+
type SomeStructWithBadType struct {
829+
Name string `json:"name"`
830+
}
831+
832+
// swagger:model namedWithBadStructType
833+
type NamedWithBadStructType struct {
834+
// The nested struct with an unsupported swagger:type.
835+
Nested SomeStructWithBadType `json:"nested"`
836+
}
837+
838+
// SomeObjectType is a struct with swagger:type object.
839+
// swagger:type object
840+
// swagger:model someObjectType
841+
type SomeObjectType struct {
842+
Key string `json:"key"`
843+
Value string `json:"value"`
844+
}
845+
846+
// swagger:model namedWithObjectStructType
847+
type NamedWithObjectStructType struct {
848+
// Headers for this request.
849+
Headers SomeObjectType `json:"headers"`
850+
}

schema.go

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -429,10 +429,15 @@ func (s *schemaBuilder) buildNamedType(titpe *types.Named, tgt swaggerTypable) e
429429
cmt = new(ast.CommentGroup)
430430
}
431431

432-
if typeName, ok := typeName(cmt); ok {
433-
_ = swaggerSchemaForType(typeName, tgt)
434-
435-
return nil
432+
if tn, ok := typeName(cmt); ok {
433+
if err := swaggerSchemaForType(tn, tgt); err == nil {
434+
return nil
435+
}
436+
// For unsupported swagger:type values (e.g., "array"), fall through
437+
// to underlying type resolution so the full schema (including items
438+
// for slices) is properly built. Build directly from the underlying
439+
// type to bypass the named-type $ref creation.
440+
return s.buildFromType(titpe.Underlying(), tgt)
436441
}
437442

438443
if s.decl.Spec.Assign.IsValid() {
@@ -559,9 +564,12 @@ func (s *schemaBuilder) buildNamedStruct(tio *types.TypeName, cmt *ast.CommentGr
559564
return nil
560565
}
561566

562-
if typeName, ok := typeName(cmt); ok {
563-
_ = swaggerSchemaForType(typeName, tgt)
564-
return nil
567+
if tn, ok := typeName(cmt); ok {
568+
if err := swaggerSchemaForType(tn, tgt); err == nil {
569+
return nil
570+
}
571+
// For unsupported swagger:type values, fall through to makeRef
572+
// rather than silently returning an empty schema.
565573
}
566574

567575
return s.makeRef(decl, tgt)
@@ -583,6 +591,14 @@ func (s *schemaBuilder) buildNamedArray(tio *types.TypeName, cmt *ast.CommentGro
583591
tgt.Items().Typed("string", sfnm)
584592
return nil
585593
}
594+
// When swagger:type is set to an unsupported value (e.g., "array"),
595+
// skip the $ref and inline the array schema with proper items type.
596+
if tn, ok := typeName(cmt); ok {
597+
if err := swaggerSchemaForType(tn, tgt); err != nil {
598+
return s.buildFromType(elem, tgt.Items())
599+
}
600+
return nil
601+
}
586602
if decl, ok := s.ctx.FindModel(tio.Pkg().Path(), tio.Name()); ok {
587603
return s.makeRef(decl, tgt)
588604
}
@@ -600,6 +616,16 @@ func (s *schemaBuilder) buildNamedSlice(tio *types.TypeName, cmt *ast.CommentGro
600616
tgt.Items().Typed("string", sfnm)
601617
return nil
602618
}
619+
// When swagger:type is set to an unsupported value (e.g., "array"),
620+
// skip the $ref and inline the slice schema with proper items type.
621+
// This preserves the field's description that would be lost with $ref.
622+
if tn, ok := typeName(cmt); ok {
623+
if err := swaggerSchemaForType(tn, tgt); err != nil {
624+
// Unsupported type name (e.g., "array") — build inline from element type.
625+
return s.buildFromType(elem, tgt.Items())
626+
}
627+
return nil
628+
}
603629
if decl, ok := s.ctx.FindModel(tio.Pkg().Path(), tio.Name()); ok {
604630
return s.makeRef(decl, tgt)
605631
}

schema_test.go

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2582,3 +2582,80 @@ func TestSetEnumDoesNotPanic(t *testing.T) {
25822582

25832583
require.NoError(t, err)
25842584
}
2585+
2586+
func TestSwaggerTypeNamedArray(t *testing.T) {
2587+
sctx := loadClassificationPkgsCtx(t)
2588+
decl := getClassificationModel(sctx, "NamedWithArrayType")
2589+
require.NotNil(t, decl)
2590+
prs := &schemaBuilder{
2591+
ctx: sctx,
2592+
decl: decl,
2593+
}
2594+
models := make(map[string]spec.Schema)
2595+
require.NoError(t, prs.Build(models))
2596+
schema := models["namedWithArrayType"]
2597+
2598+
// swagger:type array on a named []string type should produce
2599+
// an inlined array with string items, not a $ref.
2600+
assertArrayProperty(t, &schema, "string", "tags", "", "Tags")
2601+
}
2602+
2603+
func TestSwaggerTypeNamedFixedArray(t *testing.T) {
2604+
sctx := loadClassificationPkgsCtx(t)
2605+
decl := getClassificationModel(sctx, "NamedWithFixedArrayType")
2606+
require.NotNil(t, decl)
2607+
prs := &schemaBuilder{
2608+
ctx: sctx,
2609+
decl: decl,
2610+
}
2611+
models := make(map[string]spec.Schema)
2612+
require.NoError(t, prs.Build(models))
2613+
schema := models["namedWithFixedArrayType"]
2614+
2615+
// swagger:type array on a named [5]string type should produce
2616+
// an inlined array with string items via buildNamedArray.
2617+
assertArrayProperty(t, &schema, "string", "labels", "", "Labels")
2618+
}
2619+
2620+
func TestSwaggerTypeBadValueOnStruct(t *testing.T) {
2621+
sctx := loadClassificationPkgsCtx(t)
2622+
decl := getClassificationModel(sctx, "NamedWithBadStructType")
2623+
require.NotNil(t, decl)
2624+
prs := &schemaBuilder{
2625+
ctx: sctx,
2626+
decl: decl,
2627+
}
2628+
models := make(map[string]spec.Schema)
2629+
require.NoError(t, prs.Build(models))
2630+
schema := models["namedWithBadStructType"]
2631+
2632+
// swagger:type with an unsupported value on a struct should not
2633+
// produce an empty schema — it should either inline the struct
2634+
// or create a $ref. The key assertion is that the property exists
2635+
// and is not empty (i.e., the error was not silently swallowed).
2636+
prop := schema.Properties["nested"]
2637+
hasType := len(prop.Type) > 0
2638+
hasRef := prop.Ref.String() != ""
2639+
hasProps := len(prop.Properties) > 0
2640+
assert.TrueT(t, hasType || hasRef || hasProps,
2641+
"expected nested property to have type, $ref, or properties — not an empty schema")
2642+
}
2643+
2644+
func TestSwaggerTypeObjectOnStruct(t *testing.T) {
2645+
sctx := loadClassificationPkgsCtx(t)
2646+
decl := getClassificationModel(sctx, "NamedWithObjectStructType")
2647+
require.NotNil(t, decl)
2648+
prs := &schemaBuilder{
2649+
ctx: sctx,
2650+
decl: decl,
2651+
}
2652+
models := make(map[string]spec.Schema)
2653+
require.NoError(t, prs.Build(models))
2654+
schema := models["namedWithObjectStructType"]
2655+
2656+
// swagger:type object on a struct should inline as type:object,
2657+
// preserving the field's description.
2658+
prop := schema.Properties["headers"]
2659+
assert.TrueT(t, prop.Type.Contains("object"))
2660+
assert.Empty(t, prop.Ref.String(), "should not have $ref when swagger:type object is set")
2661+
}

0 commit comments

Comments
 (0)