Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

openapi3gen: allow overriding how a Schema is generated #920

Merged
merged 3 commits into from
Apr 6, 2024
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
7 changes: 7 additions & 0 deletions .github/docs/openapi3gen.txt
Original file line number Diff line number Diff line change
Expand Up @@ -70,3 +70,10 @@ type SchemaCustomizerFn func(name string, t reflect.Type, tag reflect.StructTag,
A SchemaCustomizerFn can return an ExcludeSchemaSentinel error to indicate
that the schema for this field should not be included in the final output

type SetSchemar interface {
SetSchema(*openapi3.Schema)
}
SetSchemar allows client to set their own schema definition according to
their specification. Useful when some custom datatype is needed and/or some
custom logic is needed on how the schema values would be generated

15 changes: 15 additions & 0 deletions openapi3gen/openapi3gen.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,13 @@ type Option func(*generatorOpt)
// the final output
type SchemaCustomizerFn func(name string, t reflect.Type, tag reflect.StructTag, schema *openapi3.Schema) error

// SetSchemar allows client to set their own schema definition according to
// their specification. Useful when some custom datatype is needed and/or some custom logic
// is needed on how the schema values would be generated
type SetSchemar interface {
SetSchema(*openapi3.Schema)
}

type generatorOpt struct {
useAllExportedFields bool
throwErrorOnCycle bool
Expand Down Expand Up @@ -347,6 +354,14 @@ func (g *Generator) generateWithoutSaving(parents []*theTypeInfo, t reflect.Type
schema.Type = &openapi3.Types{"object"}
}
}

default:
// Object has their own schema's implementation, so we'll use those
if v := reflect.New(t); v.CanInterface() {
if v, ok := v.Interface().(SetSchemar); ok {
v.SetSchema(schema)
}
}
}

if g.opts.schemaCustomizer != nil {
Expand Down
40 changes: 40 additions & 0 deletions openapi3gen/openapi3gen_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -590,3 +590,43 @@ func ExampleNewSchemaRefForValue_recursive() {
// "type": "object"
// }
}

type ID [16]byte

// T implements SetSchemar, allowing it to set an OpenAPI schema.
type T struct {
ID ID `json:"id"`
}

func (_ *ID) SetSchema(schema *openapi3.Schema) {
schema.Type = &openapi3.Types{"string"} // Assuming this matches your custom implementation
schema.Format = "uuid"
}

func ExampleID_SetSchema() {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Almost! See here you define an Example for the SetSchema method of the ID type. It doesn't help because these type and method live in test files so don't end up in the generated docs. Instead this should be named ExampleSetSchemar.

I'll amend this myself, no worries.

schemas := make(openapi3.Schemas)
instance := &T{
ID: ID{},
}

// Generate the schema for the instance
schemaRef, err := openapi3gen.NewSchemaRefForValue(instance, schemas)
if err != nil {
panic(err)
}
data, err := json.MarshalIndent(schemaRef, "", " ")
if err != nil {
panic(err)
}
fmt.Printf("schemaRef: %s\n", data)
// Output:
// schemaRef: {
// "properties": {
// "id": {
// "format": "uuid",
// "type": "string"
// }
// },
// "type": "object"
// }
}
Loading