go-fuego / fuego

Golang Fuego - web framework generating OpenAPI 3 spec from source code
https://go-fuego.github.io/fuego/
MIT License
924 stars 47 forks source link

ability to overwrite how a type is going to be generated in the spec #222

Open phenpessoa opened 2 weeks ago

phenpessoa commented 2 weeks ago

Is your feature request related to a problem? Please describe. Yes, currently there is no way to change how a type is going to be described in the OpenAPI spec, even though we might be using custom Marshalers/Unmarshalers.

Describe the solution you'd like I'd like to have an option to manually overwrite how a type will be described in the OpenAPI spec. I was thinking that maybe it could be an interface that the type implements and returns some sort of data of how it should be described. But open to suggestions.

Describe alternatives you've considered Currently the only alternative is editing the spec file manually.

Additional context To give a concrete example, I have this type:

type Foo struct {
    Bar Option[string] `json:"bar"`
}

type Option[T any] struct {
    Valid bool
    Val   T
}

And in the spec it will show up as

Foo:
  description: Foo schema
  properties:
    bar:
      properties:
        Val:
          type: string
        Valid:
          type: boolean
      type: object
  type: object

But, the option field there basically is just a way to make the field nullable, so I'd like it to be encoded as:

Foo:
  description: Foo schema
  properties:
    bar:
      type: [string, 'null']
  type: object
dylanhitt commented 2 weeks ago

This is a great idea!

We also appreciate your thoughtfulness when describing the feature. Im fairly certain you can just overwrite a route completely if needed, but I’m away from a computer at least til end of the week.

That being said, providing an idiomatic way to just overwrite an entires route spec is a great idea.

dylanhitt commented 1 week ago

I guess the question is do we want the input for this override to be a Go type or do we want to allow the user to construct the type (to be exact openapi3.Schema) themselves? Or maybe both?

@phenpessoa

To walk through your idea some so I understand. You're describing that in the case above Foo would implement say a function called FuegoAPIOverride() and the fuego route registration would call that somewhere?

phenpessoa commented 1 week ago

To walk through your idea some so I understand. You're describing that in the case above Foo would implement say a function called FuegoAPIOverride() and the fuego route registration would call that somewhere?

Yup! That was my original idea. And that method would return an object (maybe an object and an error?) which is an OpenAPI Schema/Object/Model. (Not sure if I'm using the right terminology here, sorry).

So it would be something like this:

// This type would be specified by Fuego, not by the user.
type OpenAPISchema struct {
    Type       []string            `yaml:"type"       json:"type"`
    Required   []string            `yaml:"required"   json:"required"`
    Properties map[string]Property `yaml:"properties" json:"properties"`
    // ... any other fields
}

type Property struct {
    Type    []string `yaml:"type"    json:"type"`
    Minimum int      `yaml:"minimum" json:"minimum"`
    Maximum int      `yaml:"maximum" json:"maximum"`
    // ... any other fields
}

// And then we could have an interface that types could implement
// This name is not a suggestion, just an example
type OpenAPISchemer interface {
    FuegoAPIOverride() OpenAPISchema
}

And then users would implement this interface for any types they see fit.

dylanhitt commented 5 days ago

Just a thought for now. I think we'd want to continue our options pattern. Maybe a OptionCustomSchema(whatever the schema input is) In this case we'd want the caller to construct the entire schema. They could call our helpers if need be

phenpessoa commented 5 days ago

Just a thought for now. I think we'd want to continue our options pattern. Maybe a OptionCustomSchema(whatever the schema input is) In this case we'd want the caller to construct the entire schema. They could call our helpers if need be

I’d prefer a more granular, per type approach. One of the selling points of fuego for me is the ability to automatically generate an OpenAPI schema from types. That would defeat that (unless I’m misunderstanding) and require the user build the entire OpenAPI schema themselves.

dylanhitt commented 5 days ago

🤔

OH. I see now. I didn’t really look at your example close enough. I still think the option pattern can be used how you described, but I’ll look at each implementation style sometime this week.

Thank you for walking me through that. I appreciate it