swaggest / rest

Web services with OpenAPI and JSON Schema done quick in Go
https://pkg.go.dev/github.com/swaggest/rest
MIT License
376 stars 17 forks source link

Validation Middleware panics with *uuid.UUID #186

Closed chriskolenko closed 8 months ago

chriskolenko commented 10 months ago

Describe the bug

When using a *uuid.UUID type in a body request the Validator Middleware is panicing. (See stack trace below)

It only happens with a pointer, when using uuid.UUID it works fine.

This bug was introduced between: github.com/swaggest/openapi-go v0.2.44 => v0.2.43 ^^ found this after creating the bug might need to move this to the other repo?

To Reproduce Add a pointer to a uuid.UUID In my models I have

type NewThing struct {
    ParentId      *uuid.UUID              `json:"parent_id" required:"false"`
}

Expected behavior It shouldn't panic, and the field should not be validated.

Additional context

Exception has occurred: panic
"reflect: Zero(nil)"
Stack:
     2  0x0000000000525ba5 in reflect.Zero
         at /usr/local/go/src/reflect/value.go:3222
     3  0x00000000012c5a90 in github.com/swaggest/openapi-go/internal.ReflectRequestBody.func3
         at /home/chris/go/pkg/mod/github.com/swaggest/openapi-go@v0.2.44/internal/json_schema.go:127
     4  0x00000000012a3a95 in github.com/swaggest/jsonschema-go.InterceptNullability.func1.1
         at /home/chris/go/pkg/mod/github.com/swaggest/jsonschema-go@v0.3.64/context.go:106
     5  0x00000000012bc0b5 in github.com/swaggest/jsonschema-go.checkNullability.func1
         at /home/chris/go/pkg/mod/github.com/swaggest/jsonschema-go@v0.3.64/reflect.go:1183
     7  0x00000000012bbfb3 in github.com/swaggest/jsonschema-go.checkNullability
         at /home/chris/go/pkg/mod/github.com/swaggest/jsonschema-go@v0.3.64/reflect.go:1233
     8  0x00000000012b9abd in github.com/swaggest/jsonschema-go.(*Reflector).walkProperties
         at /home/chris/go/pkg/mod/github.com/swaggest/jsonschema-go@v0.3.64/reflect.go:1007
     9  0x00000000012b7065 in github.com/swaggest/jsonschema-go.(*Reflector).kindSwitch
         at /home/chris/go/pkg/mod/github.com/swaggest/jsonschema-go@v0.3.64/reflect.go:721
    10  0x00000000012b31d6 in github.com/swaggest/jsonschema-go.(*Reflector).reflect
         at /home/chris/go/pkg/mod/github.com/swaggest/jsonschema-go@v0.3.64/reflect.go:510
    11  0x00000000012b0446 in github.com/swaggest/jsonschema-go.(*Reflector).Reflect
         at /home/chris/go/pkg/mod/github.com/swaggest/jsonschema-go@v0.3.64/reflect.go:284
    12  0x00000000012c2fb3 in github.com/swaggest/openapi-go/internal.ReflectRequestBody
         at /home/chris/go/pkg/mod/github.com/swaggest/openapi-go@v0.2.44/internal/json_schema.go:172
    13  0x000000000134cbcc in github.com/swaggest/openapi-go/openapi3.(*Reflector).parseRequestBody
         at /home/chris/go/pkg/mod/github.com/swaggest/openapi-go@v0.2.44/openapi3/reflect.go:323
    14  0x000000000134bc5b in github.com/swaggest/openapi-go/openapi3.(*Reflector).setupRequest
         at /home/chris/go/pkg/mod/github.com/swaggest/openapi-go@v0.2.44/openapi3/reflect.go:246
    15  0x000000000135209d in github.com/swaggest/openapi-go/openapi3.(*Reflector).WalkRequestJSONSchemas
         at /home/chris/go/pkg/mod/github.com/swaggest/openapi-go@v0.2.44/openapi3/walk_schema.go:108
    16  0x00000000013afd84 in github.com/swaggest/rest/openapi.(*Collector).ProvideRequestJSONSchemas
         at /home/chris/go/pkg/mod/github.com/swaggest/rest@v0.2.61/openapi/collector.go:516
    17  0x00000000013d8f97 in github.com/swaggest/rest/jsonschema.Factory.MakeRequestValidator
         at /home/chris/go/pkg/mod/github.com/swaggest/rest@v0.2.61/jsonschema/validator.go:58
    18  0x00000000013db2e9 in github.com/swaggest/rest/jsonschema.(*Factory).MakeRequestValidator
         at <autogenerated>:1
    19  0x00000000013f6417 in github.com/swaggest/rest/request.ValidatorMiddleware.func1
         at /home/chris/go/pkg/mod/github.com/swaggest/rest@v0.2.61/request/middleware.go:87
    20  0x00000000013b3347 in github.com/swaggest/rest/nethttp.WrapHandler
         at /home/chris/go/pkg/mod/github.com/swaggest/rest@v0.2.61/nethttp/wrap.go:16
    21  0x00000000013b6a4c in github.com/swaggest/rest/chirouter.(*Wrapper).prepareHandler
         at /home/chris/go/pkg/mod/github.com/swaggest/rest@v0.2.61/chirouter/wrapper.go:223
    22  0x00000000013b5fff in github.com/swaggest/rest/chirouter.(*Wrapper).Method
         at /home/chris/go/pkg/mod/github.com/swaggest/rest@v0.2.61/chirouter/wrapper.go:147
    23  0x00000000013fc716 in github.com/swaggest/rest/web.(*Service).Post
         at /home/chris/go/pkg/mod/github.com/swaggest/rest@v0.2.61/web/service.go:152
    24  0x000000000140637e in function/handler.NewHandler
         at /home/chris/workspace/getnoops/dev/config/handler/handler.go:111
    25  0x0000000001724f8e in main.main
         at /home/chris/workspace/getnoops/dev/config/main.go:19
vearutop commented 10 months ago

I tried to reproduce, but couldn't. Can you update the test below with more details, so that it panics?

package main_test

import (
    "bytes"
    "context"
    "net/http"
    "net/http/httptest"
    "testing"

    "github.com/google/uuid"
    "github.com/stretchr/testify/assert"
    "github.com/stretchr/testify/require"
    "github.com/swaggest/jsonschema-go"
    "github.com/swaggest/openapi-go/openapi3"
    "github.com/swaggest/rest/web"
    "github.com/swaggest/usecase"
)

func TestFoo(t *testing.T) {
    type NewThing struct {
        ParentId *uuid.UUID `json:"parent_id" required:"false"`
    }

    s := web.NewService(openapi3.NewReflector())

    // Create custom schema mapping for 3rd party type.
    uuidDef := jsonschema.Schema{}
    uuidDef.AddType(jsonschema.String)
    uuidDef.WithFormat("uuid")
    uuidDef.WithExamples("248df4b7-aa70-47b8-a036-33ac447e668d")
    s.OpenAPICollector.Reflector().AddTypeMapping(uuid.UUID{}, uuidDef)

    s.Post("/foo", usecase.NewInteractor(func(ctx context.Context, input NewThing, output *string) error {
        if input.ParentId != nil {
            *output = input.ParentId.String()
        } else {
            *output = "nil"
        }

        return nil
    }))

    req, err := http.NewRequest(http.MethodPost, "/foo", bytes.NewReader([]byte(`{"parent_id": "348df4b7-aa70-47b8-a036-33ac447e668d"}`)))
    require.NoError(t, err)
    req.Header.Set("Content-Type", "application/json")
    rw := httptest.NewRecorder()

    s.ServeHTTP(rw, req)
    assert.Equal(t, `"348df4b7-aa70-47b8-a036-33ac447e668d"
`, rw.Body.String())
}
vearutop commented 10 months ago

I've added a check in openapi-go v0.2.45, but still would be interested to reproduce the bug.

vearutop commented 8 months ago

Please feel free to reopen if the issue happens again.