go-fuego / fuego

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

How to set `operationId`? Get ugly name on generated OpenAPI schema. #128

Closed nikitavoloboev closed 4 months ago

nikitavoloboev commented 4 months ago

To Reproduce

Have go server with fuego and example for POST from readme: https://github.com/kuskusapp/kuskus/blob/main/go/main.go

package main

import "github.com/go-fuego/fuego"

type MyInput struct {
    Name string `json:"name" validate:"required"`
}

type MyOutput struct {
    Message string `json:"message"`
}

func main() {
    s := fuego.NewServer()

    // Automatically generates OpenAPI documentation for this route
    fuego.Post(s, "/post", func(c *fuego.ContextWithBody[MyInput]) (MyOutput, error) {
        body, err := c.Body()
        if err != nil {
            return MyOutput{}, err
        }

        return MyOutput{
            Message: "Hello, " + body.Name,
        }, nil
    })

    s.Run()
}

After I run it, it generates this schema:

{"components":{"requestBodies":{"MyInput":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MyInput"}}},"description":"Request body for main.MyInput","required":true}},"schemas":{"MyInput":{"properties":{"name":{"type":"string"}},"type":"object"},"MyOutput":{"properties":{"message":{"type":"string"}},"type":"object"}}},"info":{"description":"OpenAPI","title":"OpenAPI","version":"0.0.1"},"openapi":"3.0.3","paths":{"/post":{"post":{"description":"controller: main.main.func1","operationId":"POST /post:func1","requestBody":{"$ref":"#/components/requestBodies/MyInput"},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MyOutput"}}},"description":"OK"},"default":{"description":""}},"summary":"func1","tags":["MyOutput"]}}}}

When I try use it with https://grafbase.com I see this:

image

Why is the name postPostFunc1? How I change it?

I talked with Grafbase devs in here: https://discord.com/channels/890534438151274507/1240603818606202981

And they said:

image

How can I set a pretty name for the generated OpenAPI? ♥️

nikitavoloboev commented 4 months ago

Ok seems I have to use https://pkg.go.dev/github.com/go-fuego/fuego#Route.OperationID

Is there example how I can use it?

EwenQuim commented 4 months ago

The reason why you have an ugly name is because you used an anonymous function!

If you separate the controller in a named method func myController you'll have a more explicit name :)

EwenQuim commented 4 months ago

If you really want to override the operationID, just use the (Route).OperationID(string) method.

// The operationID will be set to 'myCustomOperationID' instead of 'myController')
fuego.Get(views, "/notes/{author}/", myController).OperationID("myCustomOperationID")
nikitavoloboev commented 4 months ago

So I tried with this:

package main

import "github.com/go-fuego/fuego"

type MyInput struct {
    Name string `json:"name" validate:"required"`
}

type MyOutput struct {
    Message string `json:"message"`
}

func main() {
    s := fuego.NewServer()

    fuego.Post(s, "/post", sayHello)

    s.Run()
}

func sayHello(c *fuego.ContextWithBody[MyInput]) (MyOutput, error) {
    body, err := c.Body()
    if err != nil {
        return MyOutput{}, err
    }

    return MyOutput{
        Message: "Hello, " + body.Name,
    }, nil
}

Grafbase is now saying:

image
cat go/doc/openapi.json
{"components":{"requestBodies":{"MyInput":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MyInput"}}},"description":"Request body for main.MyInput","required":true}},"schemas":{"MyInput":{"properties":{"name":{"type":"string"}},"type":"object"},"MyOutput":{"properties":{"message":{"type":"string"}},"type":"object"}}},"info":{"description":"OpenAPI","title":"OpenAPI","version":"0.0.1"},"openapi":"3.0.3","paths":{"/post":{"post":{"description":"controller: main.sayHello","operationId":"POST /post:sayHello","requestBody":{"$ref":"#/components/requestBodies/MyInput"},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MyOutput"}}},"description":"OK"},"default":{"description":""}},"summary":"sayHello","tags":["MyOutput"]}}}}⏎                                                 

Not sure what is going wrong, perhaps my go code is wrong that it generates invalid openapi or its grafbase fault?

Thanks.

EwenQuim commented 4 months ago

Your Go code generates valid OpenAPI schema.

I think that grafbase is looking for a "query root" but i don't know what it is.

nikitavoloboev commented 4 months ago
package main

import "github.com/go-fuego/fuego"

type MyInput struct {
    Name string `json:"name" validate:"required"`
}

type MyOutput struct {
    Message string `json:"message"`
}

func main() {
    s := fuego.NewServer()

    fuego.Post(s, "/post", sayHello)
    fuego.Get(s, "/get", getName)

    s.Run()
}

func getName(c fuego.ContextNoBody) (string, error) {
    return "Nikita", nil
}

func sayHello(c *fuego.ContextWithBody[MyInput]) (MyOutput, error) {
    body, err := c.Body()
    if err != nil {
        return MyOutput{}, err
    }

    return MyOutput{
        Message: "Hello, " + body.Name,
    }, nil
}
image

Fixed it. Thaanks @EwenQuim