Skip to content

Commit

Permalink
Merge pull request #1085 from willfaught/filecontent
Browse files Browse the repository at this point in the history
Swagger: Add support for File Content
  • Loading branch information
ReneWerner87 committed Apr 26, 2024
2 parents 22e51ae + 9ce8832 commit 75d4ee6
Show file tree
Hide file tree
Showing 3 changed files with 283 additions and 10 deletions.
21 changes: 20 additions & 1 deletion swagger/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ title: Swagger
![Security](https://github.com/gofiber/contrib/workflows/Security/badge.svg)
![Linter](https://github.com/gofiber/contrib/workflows/Linter/badge.svg)

Swagger middleware for [Fiber](https://github.com/gofiber/fiber). The middleware handles Swagger UI.
Swagger middleware for [Fiber](https://github.com/gofiber/fiber). The middleware handles Swagger UI.

**Note: Requires Go 1.18 and above**

Expand Down Expand Up @@ -63,6 +63,19 @@ cfg := swagger.Config{
app.Use(swagger.New(cfg))
```

Use program data for Swagger content:
```go
cfg := swagger.Config{
BasePath: "/",
FilePath: "./docs/swagger.json",
FileContent: mySwaggerByteSlice,
Path: "swagger",
Title: "Swagger API Docs",
}

app.Use(swagger.New(cfg))
```

Using multiple instances of Swagger:
```go
// Create Swagger middleware for v1
Expand Down Expand Up @@ -102,6 +115,12 @@ type Config struct {
// Optional. Default: ./swagger.json
FilePath string

// FileContent for the content of the swagger.json or swagger.yaml file.
// If provided, FilePath will not be read.
//
// Optional. Default: nil
FileContent []byte

// Path combines with BasePath for the full UI path
//
// Optional. Default: docs
Expand Down
31 changes: 22 additions & 9 deletions swagger/swagger.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,12 @@ type Config struct {
// Optional. Default: ./swagger.json
FilePath string

// FileContent for the content of the swagger.json or swagger.yaml file.
// If provided, FilePath will not be read.
//
// Optional. Default: nil
FileContent []byte

// Path combines with BasePath for the full UI path
//
// Optional. Default: docs
Expand Down Expand Up @@ -85,16 +91,20 @@ func New(config ...Config) fiber.Handler {
}
}

// Verify Swagger file exists
if _, err := os.Stat(cfg.FilePath); os.IsNotExist(err) {
panic(fmt.Errorf("%s file does not exist", cfg.FilePath))
}
rawSpec := cfg.FileContent
if len(rawSpec) == 0 {
// Verify Swagger file exists
_, err := os.Stat(cfg.FilePath)
if os.IsNotExist(err) {
panic(fmt.Errorf("%s file does not exist", cfg.FilePath))
}

// Read Swagger Spec into memory
rawSpec, err := os.ReadFile(cfg.FilePath)
if err != nil {
log.Fatalf("Failed to read provided Swagger file (%s): %v", cfg.FilePath, err.Error())
panic(err)
// Read Swagger Spec into memory
rawSpec, err = os.ReadFile(cfg.FilePath)
if err != nil {
log.Fatalf("Failed to read provided Swagger file (%s): %v", cfg.FilePath, err.Error())
panic(err)
}
}

// Validate we have valid JSON or YAML
Expand All @@ -105,6 +115,9 @@ func New(config ...Config) fiber.Handler {

if errJSON != nil && errYAML != nil {
log.Fatalf("Failed to parse the Swagger spec as JSON or YAML: JSON error: %s, YAML error: %s", errJSON, errYAML)
if len(cfg.FileContent) != 0 {
panic(fmt.Errorf("Invalid Swagger spec: %s", string(rawSpec)))
}
panic(fmt.Errorf("Invalid Swagger spec file: %s", cfg.FilePath))
}

Expand Down
241 changes: 241 additions & 0 deletions swagger/swagger_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package swagger

import (
_ "embed"
"io"
"net/http"
"net/http/httptest"
Expand All @@ -10,6 +11,12 @@ import (
"github.com/stretchr/testify/require"
)

//go:embed swagger.json
var swaggerJSON []byte

//go:embed swagger.yaml
var swaggerYAML []byte

func performRequest(method, target string, app *fiber.App) *http.Response {
r := httptest.NewRequest(method, target, nil)
resp, _ := app.Test(r, -1)
Expand Down Expand Up @@ -259,3 +266,237 @@ func TestNew(t *testing.T) {
require.Equal(t, "success", string(bodyBytes))
})
}

func TestNewWithFileContent(t *testing.T) {
t.Run("Endpoint check with only custom path", func(t *testing.T) {
app := fiber.New()

cfg := Config{
Path: "custompath",
FileContent: swaggerJSON,
FilePath: "doesnotexist-swagger.json",
}
app.Use(New(cfg))

w1 := performRequest("GET", "/custompath", app)
require.Equal(t, 200, w1.StatusCode)

w2 := performRequest("GET", "/doesnotexist-swagger.json", app)
require.Equal(t, 200, w2.StatusCode)

w3 := performRequest("GET", "/notfound", app)
require.Equal(t, 404, w3.StatusCode)
})

t.Run("Endpoint check with only custom basepath", func(t *testing.T) {
app := fiber.New()

cfg := Config{
BasePath: "/api/v1",
FileContent: swaggerJSON,
FilePath: "doesnotexist-swagger.json",
}
app.Use(New(cfg))

w1 := performRequest("GET", "/api/v1/docs", app)
require.Equal(t, 200, w1.StatusCode)

w2 := performRequest("GET", "/api/v1/doesnotexist-swagger.json", app)
require.Equal(t, 200, w2.StatusCode)

w3 := performRequest("GET", "/notfound", app)
require.Equal(t, 404, w3.StatusCode)
})

t.Run("Endpoint check with custom config", func(t *testing.T) {
app := fiber.New()

cfg := Config{
BasePath: "/",
FilePath: "doesnotexist-swagger.json",
FileContent: swaggerJSON,
}
app.Use(New(cfg))

w1 := performRequest("GET", "/docs", app)
require.Equal(t, 200, w1.StatusCode)

w2 := performRequest("GET", "/doesnotexist-swagger.json", app)
require.Equal(t, 200, w2.StatusCode)

w3 := performRequest("GET", "/notfound", app)
require.Equal(t, 404, w3.StatusCode)
})

t.Run("Endpoint check with custom path", func(t *testing.T) {
app := fiber.New()

cfg := Config{
BasePath: "/",
FilePath: "doesnotexist-swagger.json",
Path: "swagger",
FileContent: swaggerJSON,
}
app.Use(New(cfg))

w1 := performRequest("GET", "/swagger", app)
require.Equal(t, 200, w1.StatusCode)

w2 := performRequest("GET", "/doesnotexist-swagger.json", app)
require.Equal(t, 200, w2.StatusCode)

w3 := performRequest("GET", "/notfound", app)
require.Equal(t, 404, w3.StatusCode)
})

t.Run("Endpoint check with custom config and yaml spec", func(t *testing.T) {
app := fiber.New()

cfg := Config{
BasePath: "/",
FilePath: "./doesnotexist-swagger.yaml",
FileContent: swaggerYAML,
}
app.Use(New(cfg))

w1 := performRequest("GET", "/docs", app)
require.Equal(t, 200, w1.StatusCode)

w2 := performRequest("GET", "/doesnotexist-swagger.yaml", app)
require.Equal(t, 200, w2.StatusCode)

w3 := performRequest("GET", "/notfound", app)
require.Equal(t, 404, w3.StatusCode)
})

t.Run("Endpoint check with custom path and yaml spec", func(t *testing.T) {
app := fiber.New()

cfg := Config{
BasePath: "/",
FilePath: "doesnotexist-swagger.yaml",
Path: "swagger",
FileContent: swaggerYAML,
}
app.Use(New(cfg))

w1 := performRequest("GET", "/swagger", app)
require.Equal(t, 200, w1.StatusCode)

w2 := performRequest("GET", "/doesnotexist-swagger.yaml", app)
require.Equal(t, 200, w2.StatusCode)

w3 := performRequest("GET", "/notfound", app)
require.Equal(t, 404, w3.StatusCode)
})

t.Run("Endpoint check with empty custom config", func(t *testing.T) {
app := fiber.New()

cfg := Config{
FileContent: swaggerJSON,
FilePath: "doesnotexist-swagger.json",
}

app.Use(New(cfg))

w1 := performRequest("GET", "/docs", app)
require.Equal(t, 200, w1.StatusCode)

w2 := performRequest("GET", "/doesnotexist-swagger.json", app)
require.Equal(t, 200, w2.StatusCode)

w3 := performRequest("GET", "/notfound", app)
require.Equal(t, 404, w3.StatusCode)
})

t.Run("Swagger file content not specified", func(t *testing.T) {
app := fiber.New()

cfg := Config{
FilePath: "./docs/swagger.json",
}

require.Panics(t, func() {
app.Use(New(cfg))
}, "content not specified")
})

t.Run("Endpoint check with multiple Swagger instances", func(t *testing.T) {
app := fiber.New()

app.Use(New(Config{
BasePath: "/api/v1",
FileContent: swaggerJSON,
FilePath: "doesnotexist-swagger.json",
}))

app.Use(New(Config{
BasePath: "/api/v2",
FileContent: swaggerJSON,
FilePath: "doesnotexist-swagger.json",
}))

w1 := performRequest("GET", "/api/v1/docs", app)
require.Equal(t, 200, w1.StatusCode)

w2 := performRequest("GET", "/api/v1/doesnotexist-swagger.json", app)
require.Equal(t, 200, w2.StatusCode)

w3 := performRequest("GET", "/api/v2/docs", app)
require.Equal(t, 200, w3.StatusCode)

w4 := performRequest("GET", "/api/v2/doesnotexist-swagger.json", app)
require.Equal(t, 200, w4.StatusCode)

w5 := performRequest("GET", "/notfound", app)
require.Equal(t, 404, w5.StatusCode)
})

t.Run("Endpoint check with custom routes", func(t *testing.T) {
app := fiber.New()

app.Use(New(Config{
BasePath: "/api/v1",
FileContent: swaggerJSON,
FilePath: "doesnotexist-swagger.json",
}))

app.Get("/api/v1/tasks", func(c *fiber.Ctx) error {
return c.SendString("success")
})

app.Get("/api/v1", func(c *fiber.Ctx) error {
return c.SendString("success")
})

w1 := performRequest("GET", "/api/v1/docs", app)
require.Equal(t, 200, w1.StatusCode)

w2 := performRequest("GET", "/api/v1/doesnotexist-swagger.json", app)
require.Equal(t, 200, w2.StatusCode)

w3 := performRequest("GET", "/notfound", app)
require.Equal(t, 404, w3.StatusCode)

// Verify we can send request to handler with the same BasePath as the middleware
w4 := performRequest("GET", "/api/v1/tasks", app)
bodyBytes, err := io.ReadAll(w4.Body)
require.NoError(t, err)
require.Equal(t, 200, w4.StatusCode)
require.Equal(t, "success", string(bodyBytes))

// Verify handler in BasePath still works
w5 := performRequest("GET", "/api/v1", app)
bodyBytes, err = io.ReadAll(w5.Body)
require.NoError(t, err)
require.Equal(t, 200, w5.StatusCode)
require.Equal(t, "success", string(bodyBytes))

w6 := performRequest("GET", "/api/v1/", app)
bodyBytes, err = io.ReadAll(w6.Body)
require.NoError(t, err)
require.Equal(t, 200, w6.StatusCode)
require.Equal(t, "success", string(bodyBytes))
})
}

0 comments on commit 75d4ee6

Please sign in to comment.