Skip to content

Ability to go embed schema #9

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Oct 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 27 additions & 3 deletions openapi.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,15 @@ type Config struct {
// Required.
Schema string

// SchemaBytes allows loading the OpenAPI specification directly
// from a byte slice ([]byte). This is useful for embedding the
// OpenAPI spec in the binary using Go's embed package, or if the
// spec is obtained dynamically at runtime.
// Required unless Schema is provided.
//
// If both Schema and SchemaBytes are provided, SchemaBytes takes precedence.
SchemaBytes []byte

// ContextKey defines the key that will be used to store the validator
// on the echo.Context when the request is successfully validated.
// Optional. Defaults to "validator".
Expand All @@ -46,13 +55,19 @@ func OpenAPI(file string) echo.MiddlewareFunc {
return OpenAPIWithConfig(c)
}

func OpenAPIFromBytes(schemaBytes []byte) echo.MiddlewareFunc {
c := DefaultConfig
c.SchemaBytes = schemaBytes
return OpenAPIWithConfig(c)
}

func OpenAPIWithConfig(config Config) echo.MiddlewareFunc {
if config.Skipper == nil {
config.Skipper = DefaultConfig.Skipper
}

if config.Schema == "" {
panic("schema is required")
if config.Schema == "" && len(config.SchemaBytes) == 0 {
panic("either schema or schemaBytes is required")
}

if config.ContextKey == "" {
Expand All @@ -61,7 +76,16 @@ func OpenAPIWithConfig(config Config) echo.MiddlewareFunc {

ctx := context.Background()
loader := &openapi3.Loader{Context: ctx, IsExternalRefsAllowed: true}
schema, err := loader.LoadFromFile(config.Schema)

var schema *openapi3.T
var err error

if len(config.SchemaBytes) > 0 {
schema, err = loader.LoadFromData(config.SchemaBytes)
} else {
schema, err = loader.LoadFromFile(config.Schema)
}

if err != nil {
panic(fmt.Sprintf("failed loading schema file: %v", err))
}
Expand Down
96 changes: 96 additions & 0 deletions openapi_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -217,3 +217,99 @@ func TestOpenAPIWithConfig_Request_Validation(t *testing.T) {
})
}
}

func TestOpenAPIFromBytes(t *testing.T) {
sampleOpenAPISpec := []byte(`
openapi: 3.0.0
info:
title: Sample API
version: "1.0"
paths:
/:
get:
responses:
'200':
description: OK
/validation:
post:
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
username:
type: string
minLength: 2
responses:
'200':
description: OK
`)

testCases := []struct {
name string
method string
path string
body *bytes.Buffer
statusCode int
contentType string
}{
{
name: "valid GET request to /",
method: http.MethodGet,
path: "/",
body: nil, // No body for GET requests
statusCode: http.StatusOK,
},
{
name: "invalid POST request to /validation with empty body",
method: http.MethodPost,
path: "/validation",
body: bytes.NewBuffer([]byte(``)),
statusCode: http.StatusBadRequest,
contentType: echo.MIMEApplicationJSON,
},
{
name: "valid POST request to /validation",
method: http.MethodPost,
path: "/validation",
body: bytes.NewBuffer([]byte(`{"username": "test"}`)),
statusCode: http.StatusOK,
contentType: echo.MIMEApplicationJSON,
},
}

e := echo.New()

e.Any("/", func(c echo.Context) error {
return c.JSON(http.StatusOK, "ok")
})

e.Any("/validation", func(c echo.Context) error {
return c.JSON(http.StatusOK, "ok")
})

e.Use(OpenAPIFromBytes(sampleOpenAPISpec))

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
var req *http.Request

if tc.body == nil {
req = httptest.NewRequest(tc.method, tc.path, http.NoBody)
} else {
req = httptest.NewRequest(tc.method, tc.path, tc.body)
}

if tc.contentType != "" {
req.Header.Set("Content-Type", tc.contentType)
}

resp := httptest.NewRecorder()
e.ServeHTTP(resp, req)

assert.Equal(t, tc.statusCode, resp.Code)
})
}
}
Loading