Skip to content

Commit 0f1b8f1

Browse files
authored
Merge pull request #9 from retr0h/issue/8
Ability to go embed schema
2 parents e84fc2f + 5575164 commit 0f1b8f1

File tree

2 files changed

+123
-3
lines changed

2 files changed

+123
-3
lines changed

openapi.go

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,15 @@ type Config struct {
2525
// Required.
2626
Schema string
2727

28+
// SchemaBytes allows loading the OpenAPI specification directly
29+
// from a byte slice ([]byte). This is useful for embedding the
30+
// OpenAPI spec in the binary using Go's embed package, or if the
31+
// spec is obtained dynamically at runtime.
32+
// Required unless Schema is provided.
33+
//
34+
// If both Schema and SchemaBytes are provided, SchemaBytes takes precedence.
35+
SchemaBytes []byte
36+
2837
// ContextKey defines the key that will be used to store the validator
2938
// on the echo.Context when the request is successfully validated.
3039
// Optional. Defaults to "validator".
@@ -46,13 +55,19 @@ func OpenAPI(file string) echo.MiddlewareFunc {
4655
return OpenAPIWithConfig(c)
4756
}
4857

58+
func OpenAPIFromBytes(schemaBytes []byte) echo.MiddlewareFunc {
59+
c := DefaultConfig
60+
c.SchemaBytes = schemaBytes
61+
return OpenAPIWithConfig(c)
62+
}
63+
4964
func OpenAPIWithConfig(config Config) echo.MiddlewareFunc {
5065
if config.Skipper == nil {
5166
config.Skipper = DefaultConfig.Skipper
5267
}
5368

54-
if config.Schema == "" {
55-
panic("schema is required")
69+
if config.Schema == "" && len(config.SchemaBytes) == 0 {
70+
panic("either schema or schemaBytes is required")
5671
}
5772

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

6277
ctx := context.Background()
6378
loader := &openapi3.Loader{Context: ctx, IsExternalRefsAllowed: true}
64-
schema, err := loader.LoadFromFile(config.Schema)
79+
80+
var schema *openapi3.T
81+
var err error
82+
83+
if len(config.SchemaBytes) > 0 {
84+
schema, err = loader.LoadFromData(config.SchemaBytes)
85+
} else {
86+
schema, err = loader.LoadFromFile(config.Schema)
87+
}
88+
6589
if err != nil {
6690
panic(fmt.Sprintf("failed loading schema file: %v", err))
6791
}

openapi_test.go

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,3 +217,99 @@ func TestOpenAPIWithConfig_Request_Validation(t *testing.T) {
217217
})
218218
}
219219
}
220+
221+
func TestOpenAPIFromBytes(t *testing.T) {
222+
sampleOpenAPISpec := []byte(`
223+
openapi: 3.0.0
224+
info:
225+
title: Sample API
226+
version: "1.0"
227+
paths:
228+
/:
229+
get:
230+
responses:
231+
'200':
232+
description: OK
233+
/validation:
234+
post:
235+
requestBody:
236+
required: true
237+
content:
238+
application/json:
239+
schema:
240+
type: object
241+
properties:
242+
username:
243+
type: string
244+
minLength: 2
245+
responses:
246+
'200':
247+
description: OK
248+
`)
249+
250+
testCases := []struct {
251+
name string
252+
method string
253+
path string
254+
body *bytes.Buffer
255+
statusCode int
256+
contentType string
257+
}{
258+
{
259+
name: "valid GET request to /",
260+
method: http.MethodGet,
261+
path: "/",
262+
body: nil, // No body for GET requests
263+
statusCode: http.StatusOK,
264+
},
265+
{
266+
name: "invalid POST request to /validation with empty body",
267+
method: http.MethodPost,
268+
path: "/validation",
269+
body: bytes.NewBuffer([]byte(``)),
270+
statusCode: http.StatusBadRequest,
271+
contentType: echo.MIMEApplicationJSON,
272+
},
273+
{
274+
name: "valid POST request to /validation",
275+
method: http.MethodPost,
276+
path: "/validation",
277+
body: bytes.NewBuffer([]byte(`{"username": "test"}`)),
278+
statusCode: http.StatusOK,
279+
contentType: echo.MIMEApplicationJSON,
280+
},
281+
}
282+
283+
e := echo.New()
284+
285+
e.Any("/", func(c echo.Context) error {
286+
return c.JSON(http.StatusOK, "ok")
287+
})
288+
289+
e.Any("/validation", func(c echo.Context) error {
290+
return c.JSON(http.StatusOK, "ok")
291+
})
292+
293+
e.Use(OpenAPIFromBytes(sampleOpenAPISpec))
294+
295+
for _, tc := range testCases {
296+
t.Run(tc.name, func(t *testing.T) {
297+
var req *http.Request
298+
299+
if tc.body == nil {
300+
req = httptest.NewRequest(tc.method, tc.path, http.NoBody)
301+
} else {
302+
req = httptest.NewRequest(tc.method, tc.path, tc.body)
303+
}
304+
305+
if tc.contentType != "" {
306+
req.Header.Set("Content-Type", tc.contentType)
307+
}
308+
309+
resp := httptest.NewRecorder()
310+
e.ServeHTTP(resp, req)
311+
312+
assert.Equal(t, tc.statusCode, resp.Code)
313+
})
314+
}
315+
}

0 commit comments

Comments
 (0)