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
116 changes: 61 additions & 55 deletions docparse/find.go
Original file line number Diff line number Diff line change
Expand Up @@ -473,6 +473,20 @@ func GetReference(prog *Program, context string, isEmbed bool, lookup, filePath
}
}

// Ensure the lookup key is unique when two packages share the same base name
// (e.g. task/reminder and time/reminder both produce "reminder.Request").
if existing, ok := prog.References[ref.Lookup]; ok && existing.Package != ref.Package {
for i := 2; ; i++ {
candidate := fmt.Sprintf("%s%d", ref.Lookup, i)
if ex, ok := prog.References[candidate]; !ok {
ref.Lookup = candidate
break
} else if ex.Package == ref.Package {
ref.Lookup = candidate
break
}
}
}
prog.References[ref.Lookup] = ref
var (
nested []string
Expand Down Expand Up @@ -541,42 +555,73 @@ func GetReference(prog *Program, context string, isEmbed bool, lookup, filePath
}
ref.Schema = schema

changed := false
if err := applyFieldWhitelists(prog, context, filePath, name, tagName, &ref); err != nil {
return nil, err
}

// Merge for embedded structs without a tag.
for _, n := range nested {
ref.Fields = append(ref.Fields, prog.References[n].Fields...)

if prog.References[n].Schema != nil {
for k, v := range prog.References[n].Schema.Properties {
if _, ok := ref.Schema.Properties[k]; !ok {
ref.Schema.Properties[k] = v
}
}
}
}

if ref.IsSlice {
sliceSchema := &Schema{
Type: "array",
Items: ref.Schema,
}
ref.Schema = sliceSchema
}

if ref.Wrapper != "" {
wrappedSchema := &Schema{
Title: ref.Name,
Type: "object",
Properties: map[string]*Schema{},
}

wrappedSchema.Properties[ref.Wrapper] = ref.Schema
ref.Schema = wrappedSchema
}

prog.References[ref.Lookup] = ref

return &ref, nil
}

func applyFieldWhitelists(prog *Program, context, filePath, name, tagName string, ref *Reference) error {
changed := false
for _, p := range ref.Schema.Properties {
// Check if any fields are whitelisted, if not continue onto next property
if len(p.FieldWhitelist) == 0 {
continue
}

changed = true

// Get the package so we can lookup the correct reference
split := strings.Split(p.Reference, ".")
lookupStruct := strings.Join(split[:len(split)-1], ".")
if lookupStruct != "" {
lookupStruct += "."
}

for i, f := range ref.Fields {
if lookupStruct+f.Name != p.Reference {
continue
}

// Find the referenced struct
reference, err := GetReference(prog, context, false, lookupStruct+f.Name, filePath)
if err != nil {
return nil, fmt.Errorf("could not get referenced struct %s", lookupStruct+f.Name)
return fmt.Errorf("could not get referenced struct %s", lookupStruct+f.Name)
}

fields := []*ast.Field{}
for _, field := range reference.Fields {
if sliceutil.Contains(p.FieldWhitelist, strings.ToLower(field.Name)) {
fields = append(fields, field.KindField)
}
}

// Construct the parameter using the given fields
ref.Fields[i] = Param{
Name: f.Name,
KindField: &ast.Field{
Expand All @@ -585,10 +630,8 @@ func GetReference(prog *Program, context string, isEmbed bool, lookup, filePath
},
Names: f.KindField.Names,
Type: &ast.StructType{
Struct: 0,
Fields: &ast.FieldList{
Opening: 0,
List: fields,
List: fields,
},
},
Tag: f.KindField.Tag,
Expand All @@ -597,51 +640,14 @@ func GetReference(prog *Program, context string, isEmbed bool, lookup, filePath
}
}
}

// If the fields have been changed, regenerate the schema with the new fields
if changed {
schema, err = structToSchema(prog, name, tagName, ref)
schema, err := structToSchema(prog, name, tagName, *ref)
if err != nil {
return nil, fmt.Errorf("%v can not be converted to JSON schema: %v", name, err)
return fmt.Errorf("%v can not be converted to JSON schema: %v", name, err)
}
ref.Schema = schema
}

// Merge for embedded structs without a tag.
for _, n := range nested {
ref.Fields = append(ref.Fields, prog.References[n].Fields...)

if prog.References[n].Schema != nil {
for k, v := range prog.References[n].Schema.Properties {
if _, ok := ref.Schema.Properties[k]; !ok {
ref.Schema.Properties[k] = v
}
}
}
}

if ref.IsSlice {
sliceSchema := &Schema{
Type: "array",
Items: ref.Schema,
}
ref.Schema = sliceSchema
}

if ref.Wrapper != "" {
wrappedSchema := &Schema{
Title: ref.Name,
Type: "object",
Properties: map[string]*Schema{},
}

wrappedSchema.Properties[ref.Wrapper] = ref.Schema
ref.Schema = wrappedSchema
}

prog.References[ref.Lookup] = ref

return &ref, nil
return nil
}

func findNested(prog *Program, context string, isEmbed bool, f *ast.Field, filePath, pkg string) (string, error) {
Expand Down
16 changes: 16 additions & 0 deletions testdata/openapi2/src/package-name-collision/in.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package package_name_collision

import (
sharedA "package-name-collision/sub-a/shared"
sharedB "package-name-collision/sub-b/shared"
)

// POST /foo create foo
//
// Request body: sharedA.Request
// Response 200: {empty}

// POST /bar create bar
//
// Request body: sharedB.Request
// Response 200: {empty}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package shared

type Request struct {
ID int64 `json:"id"`
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package shared

type Request struct {
Name string `json:"name"`
}
64 changes: 64 additions & 0 deletions testdata/openapi2/src/package-name-collision/want.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
swagger: "2.0"
info:
title: x
version: x
consumes:
- application/json
produces:
- application/json
tags:
- name: bar
- name: create
- name: foo
paths:
/bar:
post:
operationId: POST_bar
tags:
- create
- bar
consumes:
- application/json
produces:
- application/json
parameters:
- name: shared.Request2
in: body
required: true
schema:
$ref: '#/definitions/shared.Request2'
responses:
200:
description: 200 OK (no data)
/foo:
post:
operationId: POST_foo
tags:
- create
- foo
consumes:
- application/json
produces:
- application/json
parameters:
- name: shared.Request
in: body
required: true
schema:
$ref: '#/definitions/shared.Request'
responses:
200:
description: 200 OK (no data)
definitions:
shared.Request:
title: Request
type: object
properties:
id:
type: integer
shared.Request2:
title: Request
type: object
properties:
name:
type: string
Loading