Skip to content

openapi3gen: Allow client with custom type to supply their own type(s) #919

@taufik-rama

Description

@taufik-rama

Hello 👋

I'm currently trying stuff out using openapi3gen package & I noticed a functionality to generates OpenAPI's types based on the struct fields

switch t.Kind() {
case reflect.Func, reflect.Chan:
return nil, nil // ignore
case reflect.Bool:
schema.Type = &openapi3.Types{"boolean"}
case reflect.Int:
schema.Type = &openapi3.Types{"integer"}

I've an issue where the resulting object type is "unknown" because I uses newtype/custom type for my type

package mypkg

type ID uuid.UUID

type T struct {
    ID ID `json:"id"`
}

So when I generates T, it will result in something like

# ...
paths:
    /my-api:
        delete:
            requestBody:
                content:
                    application/json:
                        schema:
                            properties:
                                id: {}
# ...

The expectation is that the type should instead refer to the "actual" type, or other possibility would be from the user to also generate custom schema definitions

# ...
paths:
    /my-api:
        delete:
            requestBody:
                content:
                    application/json:
                        schema:
                            properties:
                                id:
                                    type: string
# ...

My current solution

I made some changes to above type mapping & provides a SetSchemar interface, which would supply the openapi3.Schema to the user, which they then can set themselves based on their requirement

diff --git a/openapi3gen/openapi3gen.go b/openapi3gen/openapi3gen.go
index 95805ee..a301337 100644
--- a/openapi3gen/openapi3gen.go
+++ b/openapi3gen/openapi3gen.go
@@ -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
@@ -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 {

So now I can implement the interface, and it will generate correctly

func (_ *ID) SetSchema(schema *openapi3.Schema) {
    schema.Type = "string"
    schema.Format = "uuid"
}

Result:

# ...
paths:
    /my-api:
        delete:
            requestBody:
                content:
                    application/json:
                        schema:
                            properties:
                                id:
                                    format: uuid
                                    type: string
# ...

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions