Skip to content

Commit

Permalink
New more explicit OverrideDescription option
Browse files Browse the repository at this point in the history
  • Loading branch information
EwenQuim committed Dec 13, 2024
1 parent 56f38e2 commit 7ea6109
Show file tree
Hide file tree
Showing 9 changed files with 91 additions and 44 deletions.
4 changes: 3 additions & 1 deletion examples/full-app-gourmet/views/views.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ func (rs Resource) Routes(s *fuego.Server) {
fuego.Get(adminRoutes, "/ingredients/create", rs.adminIngredientCreationPage)
fuego.All(adminRoutes, "/ingredients/{id}", rs.adminOneIngredient)

fuego.Post(adminRoutes, "/ingredients/new", rs.adminCreateIngredient)
fuego.Post(adminRoutes, "/ingredients/new", rs.adminCreateIngredient,
option.Description("Create a new ingredient"),
)
fuego.Get(adminRoutes, "/users", rs.adminRecipes)
}
8 changes: 4 additions & 4 deletions examples/petstore/controllers/pets.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,23 +36,23 @@ func (rs PetsResources) Routes(s *fuego.Server) {
optionPagination,
option.Query("name", "Filter by name", param.Example("cat name", "felix"), param.Nullable()),
option.QueryInt("younger_than", "Only get pets younger than given age in years", param.Default(3)),
option.AddDescription("Filter pets"),
option.Description("Filter pets"),
)

fuego.Get(petsGroup, "/all", rs.getAllPets,
optionPagination,
option.Tags("my-tag"),
option.AddDescription("Get all pets"),
option.Description("Get all pets"),
)

fuego.Get(petsGroup, "/by-age", rs.getAllPetsByAge, option.AddDescription("Returns an array of pets grouped by age"))
fuego.Get(petsGroup, "/by-age", rs.getAllPetsByAge, option.Description("Returns an array of pets grouped by age"))
fuego.Post(petsGroup, "/", rs.postPets,
option.DefaultStatusCode(201),
option.AddError(409, "Conflict: Pet with the same name already exists", PetsError{}),
)

fuego.Get(petsGroup, "/{id}", rs.getPets,
option.Description("Replace description with this sentence."),
option.OverrideDescription("Replace description with this sentence."),
option.OperationID("getPet"),
option.Path("id", "Pet ID", param.Example("example", "123")),
)
Expand Down
16 changes: 8 additions & 8 deletions examples/petstore/lib/testdata/doc/openapi.golden.json
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@
"paths": {
"/pets/": {
"get": {
"description": "controller: `github.com/go-fuego/fuego/examples/petstore/controllers.PetsResources.filterPets`\n\n---\n\nFilter pets",
"description": "#### Controller: \n\n`github.com/go-fuego/fuego/examples/petstore/controllers.PetsResources.filterPets`\n\n---\n\nFilter pets",
"operationId": "GET_/pets/",
"parameters": [
{
Expand Down Expand Up @@ -302,7 +302,7 @@
]
},
"post": {
"description": "controller: `github.com/go-fuego/fuego/examples/petstore/controllers.PetsResources.postPets`\n\n---\n\n",
"description": "#### Controller: \n\n`github.com/go-fuego/fuego/examples/petstore/controllers.PetsResources.postPets`\n\n---\n\n",
"operationId": "POST_/pets/",
"parameters": [
{
Expand Down Expand Up @@ -390,7 +390,7 @@
},
"/pets/all": {
"get": {
"description": "controller: `github.com/go-fuego/fuego/examples/petstore/controllers.PetsResources.getAllPets`\n\n---\n\nGet all pets",
"description": "#### Controller: \n\n`github.com/go-fuego/fuego/examples/petstore/controllers.PetsResources.getAllPets`\n\n---\n\nGet all pets",
"operationId": "GET_/pets/all",
"parameters": [
{
Expand Down Expand Up @@ -520,7 +520,7 @@
},
"/pets/by-age": {
"get": {
"description": "controller: `github.com/go-fuego/fuego/examples/petstore/controllers.PetsResources.getAllPetsByAge`\n\n---\n\nReturns an array of pets grouped by age",
"description": "#### Controller: \n\n`github.com/go-fuego/fuego/examples/petstore/controllers.PetsResources.getAllPetsByAge`\n\n---\n\nReturns an array of pets grouped by age",
"operationId": "GET_/pets/by-age",
"parameters": [
{
Expand Down Expand Up @@ -599,7 +599,7 @@
},
"/pets/by-name/{name...}": {
"get": {
"description": "controller: `github.com/go-fuego/fuego/examples/petstore/controllers.PetsResources.getPetByName`\n\n---\n\n",
"description": "#### Controller: \n\n`github.com/go-fuego/fuego/examples/petstore/controllers.PetsResources.getPetByName`\n\n---\n\n",
"operationId": "GET_/pets/by-name/:name...",
"parameters": [
{
Expand Down Expand Up @@ -675,7 +675,7 @@
},
"/pets/{id}": {
"delete": {
"description": "controller: `github.com/go-fuego/fuego/examples/petstore/controllers.PetsResources.deletePets`\n\n---\n\n",
"description": "#### Controller: \n\n`github.com/go-fuego/fuego/examples/petstore/controllers.PetsResources.deletePets`\n\n---\n\n",
"operationId": "DELETE_/pets/:id",
"parameters": [
{
Expand Down Expand Up @@ -827,7 +827,7 @@
]
},
"put": {
"description": "controller: `github.com/go-fuego/fuego/examples/petstore/controllers.PetsResources.putPets`\n\n---\n\n",
"description": "#### Controller: \n\n`github.com/go-fuego/fuego/examples/petstore/controllers.PetsResources.putPets`\n\n---\n\n",
"operationId": "PUT_/pets/:id",
"parameters": [
{
Expand Down Expand Up @@ -913,7 +913,7 @@
},
"/pets/{id}/json": {
"put": {
"description": "controller: `github.com/go-fuego/fuego/examples/petstore/controllers.PetsResources.putPets`\n\n---\n\n",
"description": "#### Controller: \n\n`github.com/go-fuego/fuego/examples/petstore/controllers.PetsResources.putPets`\n\n---\n\n",
"operationId": "PUT_/pets/:id/json",
"parameters": [
{
Expand Down
63 changes: 46 additions & 17 deletions mux.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,18 @@ type BaseRoute struct {
Middlewares []func(http.Handler) http.Handler
AcceptedContentTypes []string // Content types accepted for the request body. If nil, all content types (*/*) are accepted.
Hidden bool // If true, the route will not be documented in the OpenAPI spec
OverrideDescription bool // Override the default description
DefaultStatusCode int // Default status code for the response
OpenAPI *OpenAPI // Ref to the whole OpenAPI spec
}

func (r *BaseRoute) GenerateDefaultDescription(otherMiddlewares ...func(http.Handler) http.Handler) {
if r.OverrideDescription {
return
}
r.Operation.Description = DefaultDescription(r.FullName, append(r.Middlewares, otherMiddlewares...)) + r.Operation.Description
}

// Capture all methods (GET, POST, PUT, PATCH, DELETE) and register a controller.
func All[ReturnType, Body any, Contexted ctx[Body]](s *Server, path string, controller func(Contexted) (ReturnType, error), options ...func(*BaseRoute)) *Route[ReturnType, Body] {
return registerFuegoController(s, "", path, controller, options...)
Expand Down Expand Up @@ -161,14 +169,12 @@ func PatchStd(s *Server, path string, controller func(http.ResponseWriter, *http

func registerFuegoController[T, B any, Contexted ctx[B]](s *Server, method, path string, controller func(Contexted) (T, error), options ...func(*BaseRoute)) *Route[T, B] {
route := BaseRoute{
Method: method,
Path: path,
Params: make(map[string]OpenAPIParam),
FullName: FuncName(controller),
Operation: &openapi3.Operation{
Description: "controller: `" + FuncName(controller) + "`\n\n---\n\n",
},
OpenAPI: s.OpenAPI,
Method: method,
Path: path,
Params: make(map[string]OpenAPIParam),
FullName: FuncName(controller),
Operation: &openapi3.Operation{},
OpenAPI: s.OpenAPI,
}

acceptHeaderParameter := openapi3.NewHeaderParameter("Accept")
Expand All @@ -179,26 +185,28 @@ func registerFuegoController[T, B any, Contexted ctx[B]](s *Server, method, path
o(&route)
}

route.GenerateDefaultDescription(s.middlewares...)

return Register(s, Route[T, B]{BaseRoute: route}, HTTPHandler(s, controller, &route))
}

func registerStdController(s *Server, method, path string, controller func(http.ResponseWriter, *http.Request), options ...func(*BaseRoute)) *Route[any, any] {
route := BaseRoute{
Method: method,
Path: path,
Params: make(map[string]OpenAPIParam),
FullName: FuncName(controller),
Operation: &openapi3.Operation{
Description: "controller: `" + FuncName(controller) + "`\n\n---\n\n",
},
Handler: http.HandlerFunc(controller),
OpenAPI: s.OpenAPI,
Method: method,
Path: path,
Params: make(map[string]OpenAPIParam),
FullName: FuncName(controller),
Operation: &openapi3.Operation{},
Handler: http.HandlerFunc(controller),
OpenAPI: s.OpenAPI,
}

for _, o := range append(s.routeOptions, options...) {
o(&route)
}

route.GenerateDefaultDescription(s.middlewares...)

return Register(s, Route[any, any]{BaseRoute: route}, http.HandlerFunc(controller))
}

Expand Down Expand Up @@ -244,3 +252,24 @@ func camelToHuman(s string) string {
}
return result.String()
}

// DefaultDescription returns a default .md description for a controller
func DefaultDescription[T any](handler string, middlewares []T) string {
description := "#### Controller: \n\n`" +
handler + "`"

if len(middlewares) > 0 {
description += "\n\n#### Middlewares:\n"

for i, fn := range middlewares {
description += "\n- `" + FuncName(fn) + "`"

if i == 6 {
description += "\n- more middleware..."
break
}
}
}

return description + "\n\n---\n\n"
}
4 changes: 2 additions & 2 deletions mux_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -409,14 +409,14 @@ func TestRegister(t *testing.T) {
}, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}),
OptionOperationID("new-operation-id"),
OptionSummary("new-summary"),
OptionAddDescription("new-description"),
OptionDescription("new-description"),
OptionTags("new-tag"),
)

require.NotNil(t, route)
require.Equal(t, []string{"my-tag", "new-tag"}, route.Operation.Tags)
require.Equal(t, "new-summary", route.Operation.Summary)
require.Equal(t, "my-description\nnew-description", route.Operation.Description)
require.Equal(t, "new-description", route.Operation.Description)
require.Equal(t, "new-operation-id", route.Operation.OperationID)
})
}
Expand Down
2 changes: 1 addition & 1 deletion openapi_operations_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ func TestTags(t *testing.T) {
)

require.Equal(t, []string{"my-tag"}, route.Operation.Tags)
require.Equal(t, "my description", route.Operation.Description)
require.Equal(t, "#### Controller: \n\n`github.com/go-fuego/fuego.testController`\n\n---\n\nmy description", route.Operation.Description)
require.Equal(t, "my summary", route.Operation.Summary)
require.Equal(t, true, route.Operation.Deprecated)
}
Expand Down
14 changes: 12 additions & 2 deletions option.go
Original file line number Diff line number Diff line change
Expand Up @@ -258,10 +258,10 @@ func OptionSummary(summary string) func(*BaseRoute) {
}
}

// Description sets the description to the route.
// Description adds a description to the route.
// By default, the description is set by Fuego with some info,
// like the controller function name and the package name.
// If you want to add a description, please use [AddDescription] instead.
// If you want to override Fuego's description, please use [OptionOverrideDescription] instead.
func OptionDescription(description string) func(*BaseRoute) {
return func(r *BaseRoute) {
r.Operation.Description = description
Expand All @@ -277,6 +277,16 @@ func OptionAddDescription(description string) func(*BaseRoute) {
}
}

// Description appends a description to the route.
// By default, the description is set by Fuego with some info,
// like the controller function name and the package name.
func OptionOverrideDescription(description string) func(*BaseRoute) {
return func(r *BaseRoute) {
r.OverrideDescription = true
r.Operation.Description = description
}
}

// OperationID adds an operation ID to the route.
func OptionOperationID(operationID string) func(*BaseRoute) {
return func(r *BaseRoute) {
Expand Down
7 changes: 7 additions & 0 deletions option/option.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,15 @@ var Description = fuego.OptionDescription
// AddDescription adds a description to the route.
// By default, the description is set by Fuego with some info,
// like the controller function name and the package name.
//
// Deprecated: Use [Description] instead.
var AddDescription = fuego.OptionAddDescription

// OverrideDescription overrides the default description set by Fuego.
// By default, the description is set by Fuego with some info,
// like the controller function name and the package name.
var OverrideDescription = fuego.OptionOverrideDescription

// Security configures security requirements to the route.
//
// Single Scheme (AND Logic):
Expand Down
17 changes: 8 additions & 9 deletions option_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -233,14 +233,14 @@ func TestOpenAPI(t *testing.T) {

route := fuego.Get(s, "/test", helloWorld,
fuego.OptionSummary("test summary"),
fuego.OptionAddDescription("test description"),
fuego.OptionDescription("test description"),
fuego.OptionTags("first-tag", "second-tag"),
fuego.OptionDeprecated(),
fuego.OptionOperationID("test-operation-id"),
)

require.Equal(t, "test summary", route.Operation.Summary)
require.Equal(t, "controller: `github.com/go-fuego/fuego_test.helloWorld`\n\n---\n\ntest description", route.Operation.Description)
require.Equal(t, "#### Controller: \n\n`github.com/go-fuego/fuego_test.helloWorld`\n\n---\n\ntest description", route.Operation.Description)
require.Equal(t, []string{"first-tag", "second-tag"}, route.Operation.Tags)
require.True(t, route.Operation.Deprecated)
})
Expand Down Expand Up @@ -710,26 +710,25 @@ func TestSecurity(t *testing.T) {
})
}

func TestOptionAddDescription(t *testing.T) {
func TestOptionDescription(t *testing.T) {
t.Run("Declare a description for the route", func(t *testing.T) {
s := fuego.NewServer()

route := fuego.Get(s, "/test", helloWorld,
option.AddDescription("test description"),
option.Description("test description"),
)

require.Equal(t, "controller: `github.com/go-fuego/fuego_test.helloWorld`\n\n---\n\ntest description", route.Operation.Description)
require.Equal(t, "#### Controller: \n\n`github.com/go-fuego/fuego_test.helloWorld`\n\n---\n\ntest description", route.Operation.Description)
})

t.Run("Declare a description for the route with multiple descriptions", func(t *testing.T) {
t.Run("Override Fuego's description for the route", func(t *testing.T) {
s := fuego.NewServer()

route := fuego.Get(s, "/test", helloWorld,
option.Description("test description\n\n"),
option.AddDescription("another description"),
option.OverrideDescription("another description"),
)

require.Equal(t, "test description\n\nanother description", route.Operation.Description)
require.Equal(t, "another description", route.Operation.Description)
})
}

Expand Down

0 comments on commit 7ea6109

Please sign in to comment.