wI2L / fizz

:lemon: Gin wrapper with OpenAPI 3 spec generation
https://pkg.go.dev/github.com/wI2L/fizz
MIT License
214 stars 52 forks source link

'Any' type with interface{} #51

Closed rw-access closed 3 years ago

rw-access commented 3 years ago

I have an API that has one field that can return an arbitrary data type. Normally, I would avoid that scenario as much as possible, but in my case it does make sense to have this behavior.

On my output struct, I have a field result interface{}, but when generating the OpenAPI 3.0 schema, it's completely dropped. I think ideally, this would generate a jsonschema of "type":["number","string","boolean","object","array", "null"]

Is there to express this? If not, do you mind if I make a PR to add this behavior?

rw-access commented 3 years ago

FWIW I can make "type": "any" work but that's not valid JSON schema.

var ifaceptr *interface{}
fizz.Generator().OverrideTypeName(reflect.TypeOf(ifaceptr).Elem(), "any")
"result": {
    "type": "any",
    "description": "The result object which can be any valid JSON type",
    "format": "any"
},
wI2L commented 3 years ago

@rw-access

Yes, as you can see here, when a data type is not supported, it is skipped and an error is added to the generator's internal list. You should see a TypeError if you print the content of fizz.Errors.

That's clearly a limitation that could be lifted. The DataTypeFromType function could return a TypeAny value to represent the interface{} Go type, which could then generate an OpenAPI Schema that represents all JSON values.

However, your mention of the jsonschema "type":["number","string","boolean","object","array", "null"] is invalid according to the OpenAPI scpec. See https://swagger.io/specification/#openapi-object, it specifies:

type - Value MUST be a string. Multiple types via an array are not supported.

I'm thinking that producing a schema that contains a oneOf property validating one of number, string, boolean, object or array type could do the trick.

rw-access commented 3 years ago

Ah good catch with oneOf, that makes more sense. I was looking at the generator code and it looks like it shouldn't be too bad to add. Mind if I give it a shot?

wI2L commented 3 years ago

@rw-access

Nevermind, I just found that there already is a support for "any types" in the OpenAPI spec: https://swagger.io/docs/specification/data-models/data-types/ (end of page, section Any Type).

So the above would stand, but instead of messing with oneOf, we could just create a constant schema value like so:

anyValueSchema = &Schema{
  Nullable: true,
  Description: "Can be any value, including null",
}

and use that schema, either inlined in the spec, or by using a reference to avoid duplication with SchemaOrRef. Note: the schema would have to be registered in g.api.Components.Schemas (when the generator is initialized), with the name used in the SchemaOrRef object.

EDIT: Yes, go ahead if you want to open a PR ;)