diff --git a/README.md b/README.md index d645ba6..528b952 100644 --- a/README.md +++ b/README.md @@ -87,3 +87,46 @@ e.Use(middleware.GzipWithConfig(middleware.GzipConfig{ }, })) ``` + +### Change UI Theme to redocly: + +You can use [redocly open sourceapi theme](https://redocly.com/docs/redoc/deployment/html/) to style up your user interface, to use this pass the theme name when you create your route + +```go +package main + +import ( + "github.com/labstack/echo/v4" + "github.com/swaggo/echo-swagger" + + _ "github.com/swaggo/echo-swagger/example/docs" // docs is generated by Swag CLI, you have to import it. +) + +// @title Swagger Example API +// @version 1.0 +// @description This is a sample server Petstore server. +// @termsOfService http://swagger.io/terms/ + +// @contact.name API Support +// @contact.url http://www.swagger.io/support +// @contact.email support@swagger.io + +// @license.name Apache 2.0 +// @license.url http://www.apache.org/licenses/LICENSE-2.0.html + +// @host petstore.swagger.io +// @BasePath /v2 +func main() { + e := echo.New() + + // pass theme name as redocly + e.GET("/swagger/*", echoSwagger.EchoWrapHandler(echoSwagger.ThemeName("redocly"))) + + e.Logger.Fatal(e.Start(":1323")) +} + +``` + +5. Run it, and browser to http://localhost:1323/swagger/index.html, you can see Swagger 2.0 Api documents. + +![swagger_index.html](https://raw.githubusercontent.com/mudphilo/echo-swagger/90daa4cf6fbde73190e7b6e88851ca467355cedb/Screenshot%202023-12-15%20at%2002.46.10.png) diff --git a/example/docs/docs.go b/example/docs/docs.go index b47edb2..c67d9bf 100644 --- a/example/docs/docs.go +++ b/example/docs/docs.go @@ -5,7 +5,7 @@ package docs import "github.com/swaggo/swag" const docTemplate_swagger = `{ - "schemes": {{ marshal .Schemes }}, +"schemes": {{ marshal .Schemes }}, "swagger": "2.0", "info": { "description": "{{escape .Description}}", @@ -24,7 +24,212 @@ const docTemplate_swagger = `{ }, "host": "{{.Host}}", "basePath": "{{.BasePath}}", - "paths": {} + "paths": { + "/file/upload": { + "post": { + "description": "Upload file", + "consumes": [ + "multipart/form-data" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Upload File" + ], + "summary": "Upload file", + "operationId": "file.upload", + "parameters": [ + { + "type": "file", + "description": "this is a test file", + "name": "file", + "in": "formData", + "required": true + } + ], + "responses": { + "200": { + "description": "ok", + "schema": { + "type": "string" + } + }, + "400": { + "description": "We need ID!!", + "schema": { + "type": "object", + "$ref": "#/definitions/web.APIError" + } + }, + "404": { + "description": "Can not find ID", + "schema": { + "type": "object", + "$ref": "#/definitions/web.APIError" + } + } + } + } + }, + "/testapi/get-string-by-int/{some_id}": { + "get": { + "description": "get string by ID", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Upload File" + ], + "summary": "Add a new pet to the store", + "operationId": "get-string-by-int", + "parameters": [ + { + "type": "int", + "description": "Some ID", + "name": "some_id", + "in": "path", + "required": true + }, + { + "description": "Some ID", + "name": "some_id", + "in": "body", + "required": true, + "schema": { + "type": "object", + "$ref": "#/definitions/web.Pet" + } + } + ], + "responses": { + "200": { + "description": "ok", + "schema": { + "type": "string" + } + }, + "400": { + "description": "We need ID!!", + "schema": { + "type": "object", + "$ref": "#/definitions/web.APIError" + } + }, + "404": { + "description": "Can not find ID", + "schema": { + "type": "object", + "$ref": "#/definitions/web.APIError" + } + } + } + } + }, + "/testapi/get-struct-array-by-string/{some_id}": { + "get": { + "description": "get struct array by ID", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Upload File" + ], + "operationId": "get-struct-array-by-string", + "parameters": [ + { + "type": "string", + "description": "Some ID", + "name": "some_id", + "in": "path", + "required": true + }, + { + "type": "int", + "description": "Offset", + "name": "offset", + "in": "query", + "required": true + }, + { + "type": "int", + "description": "Offset", + "name": "limit", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "ok", + "schema": { + "type": "string" + } + }, + "400": { + "description": "We need ID!!", + "schema": { + "type": "object", + "$ref": "#/definitions/web.APIError" + } + }, + "404": { + "description": "Can not find ID", + "schema": { + "type": "object", + "$ref": "#/definitions/web.APIError" + } + } + } + } + } + }, + "definitions": { + "web.APIError": { + "type": "object", + "properties": { + "CreatedAt": { + "type": "string", + "format": "date-time" + }, + "ErrorCode": { + "type": "integer" + }, + "ErrorMessage": { + "type": "string" + } + } + }, + "web.Pet": { + "type": "object", + "properties": { + "Category": { + "type": "object" + }, + "ID": { + "type": "integer" + }, + "Name": { + "type": "string" + }, + "PhotoUrls": { + "type": "array" + }, + "Status": { + "type": "string" + }, + "Tags": { + "type": "array" + } + } + } + } }` // SwaggerInfo_swagger holds exported Swagger Info so clients can modify it diff --git a/example/main.go b/example/main.go index 8586acf..dea970e 100644 --- a/example/main.go +++ b/example/main.go @@ -24,6 +24,7 @@ func main() { e := echo.New() e.GET("/swagger/*", echoSwagger.WrapHandler) + e.GET("/swagger/redocly/*", echoSwagger.EchoWrapHandler(echoSwagger.ThemeName("redocly"))) /* Or can use EchoWrapHandler func with configurations. diff --git a/redocly-theme-screenshot.png b/redocly-theme-screenshot.png new file mode 100644 index 0000000..8fa3e68 Binary files /dev/null and b/redocly-theme-screenshot.png differ diff --git a/swagger.go b/swagger.go index b8dc937..edd2c0e 100644 --- a/swagger.go +++ b/swagger.go @@ -25,6 +25,9 @@ type Config struct { // The information for OAuth2 integration, if any. OAuth *OAuthConfig + + // Information abouth theme, default theme is swagger-ui + Theme string } // OAuthConfig stores configuration for Swagger UI OAuth2 integration. See @@ -82,6 +85,13 @@ func InstanceName(instanceName string) func(*Config) { } } +// ThemeName specified swag theme name. +func ThemeName(themeName string) func(*Config) { + return func(c *Config) { + c.Theme = themeName + } +} + // PersistAuthorization Persist authorization information over browser close/refresh. // Defaults to false. func PersistAuthorization(persistAuthorization bool) func(*Config) { @@ -115,6 +125,11 @@ func newConfig(configFns ...func(*Config)) *Config { config.InstanceName = swag.Name } + // if theme is black set default theme to swagger ui + if config.Theme == "" { + config.Theme = "swagger-ui" + } + return &config } @@ -126,8 +141,19 @@ func EchoWrapHandler(options ...func(*Config)) echo.HandlerFunc { config := newConfig(options...) // create a template with name - index, _ := template.New("swagger_index.html").Parse(indexTemplate) + var index *template.Template + + // create a template with name + if config.Theme == "redocly" { + + index, _ = template.New("swagger_index.html").Parse(redoclyIndexTemplate) + + } else { + + index, _ = template.New("swagger_index.html").Parse(indexTemplate) + + } var re = regexp.MustCompile(`^(.*/)([^?].*)?[?|.]*$`) return func(c echo.Context) error { @@ -309,3 +335,37 @@ window.onload = function() { ` + +const redoclyIndexTemplate = ` + + + + API Documentation + + + + + + + + + + + + + + + + + +` diff --git a/swagger_test.go b/swagger_test.go index 4a088cf..e839a42 100644 --- a/swagger_test.go +++ b/swagger_test.go @@ -277,6 +277,19 @@ func TestWrapHandler(t *testing.T) { } +func TestWrapHandlerWithRedoclyTheme(t *testing.T) { + router := echo.New() + + router.Any("/*", EchoWrapHandler(DocExpansion("none"), DomID("swagger-ui"), ThemeName("redocly"))) + + w1 := performRequest(http.MethodGet, "/index.html", router) + assert.Equal(t, http.StatusOK, w1.Code) + assert.Equal(t, w1.Header()["Content-Type"][0], "text/html; charset=utf-8") + + assert.Equal(t, http.StatusInternalServerError, performRequest(http.MethodGet, "/doc.json", router).Code) + +} + func TestConfig(t *testing.T) { router := echo.New()