From 251494460566476eb217f9d9efd7d356ea594fdb Mon Sep 17 00:00:00 2001 From: Jacob Hochstetler Date: Tue, 13 Sep 2022 14:32:40 -0500 Subject: [PATCH] Linting (gofmt) Updated deprecated calls Updated golangci rules Updated to Go1.18 `any` Added Hooks around UseCase Interactor execution --- .golangci.yml | 2 + README.md | 4 +- _examples/advanced-generic/error_response.go | 4 +- .../advanced-generic/output_headers_test.go | 3 +- _examples/advanced-generic/output_writer.go | 2 +- .../request_response_mapping_test.go | 3 +- _examples/advanced-generic/router.go | 2 +- _examples/advanced/dummy.go | 2 +- _examples/advanced/error_response.go | 6 +- _examples/advanced/file_multi_upload.go | 2 +- _examples/advanced/file_upload.go | 2 +- _examples/advanced/gzip_pass_through.go | 2 +- _examples/advanced/json_body.go | 2 +- _examples/advanced/json_body_validation.go | 2 +- _examples/advanced/json_map_body.go | 2 +- _examples/advanced/json_param.go | 2 +- _examples/advanced/json_slice_body.go | 2 +- _examples/advanced/no_validation.go | 2 +- _examples/advanced/output_headers.go | 2 +- _examples/advanced/output_writer.go | 2 +- _examples/advanced/query_object.go | 2 +- .../advanced/request_response_mapping.go | 2 +- .../advanced/request_response_mapping_test.go | 3 +- _examples/advanced/router.go | 2 +- _examples/advanced/validation.go | 2 +- _examples/basic/main.go | 2 +- .../task-api/internal/infra/log/usecase.go | 2 +- .../internal/infra/repository/task.go | 2 +- .../task-api/internal/usecase/create_task.go | 2 +- .../task-api/internal/usecase/find_task.go | 2 +- .../task-api/internal/usecase/find_tasks.go | 2 +- .../task-api/internal/usecase/finish_task.go | 2 +- .../task-api/internal/usecase/update_task.go | 2 +- chirouter/wrapper_test.go | 2 +- error.go | 10 +- error_test.go | 4 +- go.mod | 2 +- gzip/container.go | 14 +- gzip/container_test.go | 8 +- jsonschema/validator.go | 12 +- jsonschema/validator_test.go | 32 ++-- nethttp/example_test.go | 2 +- nethttp/handler.go | 55 ++++-- nethttp/handler_test.go | 166 ++++++++++++++++-- nethttp/openapi.go | 4 +- nethttp/openapi_test.go | 2 +- nethttp/options.go | 4 +- nethttp/usecase.go | 2 +- nethttp/wrap.go | 2 +- openapi/collector.go | 8 +- openapi/collector_test.go | 6 +- request.go | 4 +- request/decoder.go | 10 +- request/factory.go | 18 +- request/file.go | 2 +- request/file_test.go | 10 +- request/jsonbody.go | 8 +- request/jsonbody_test.go | 4 +- request/middleware.go | 6 +- request/reflect.go | 2 +- request_test.go | 2 +- response/encoder.go | 26 +-- response/gzip/middleware_test.go | 4 +- response/middleware_test.go | 2 +- response/validator_test.go | 2 +- resttest/client_test.go | 4 +- resttest/server_test.go | 21 ++- trait.go | 4 +- validator.go | 20 +-- web/example_test.go | 3 +- web/service_test.go | 6 +- 71 files changed, 371 insertions(+), 199 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index 3be2189..c18032f 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -12,6 +12,8 @@ linters-settings: threshold: 100 misspell: locale: US + nlreturn: + block-size: 2 unused: check-exported: false unparam: diff --git a/README.md b/README.md index 04b9345..b0aaece 100644 --- a/README.md +++ b/README.md @@ -185,7 +185,7 @@ It can provide information about itself that will be exposed in generated docume ```go // Create use case interactor with references to input/output types and interaction function. -u := usecase.NewIOI(new(helloInput), new(helloOutput), func(ctx context.Context, input, output interface{}) error { +u := usecase.NewIOI(new(helloInput), new(helloOutput), func(ctx context.Context, input, output any) error { var ( in = input.(*helloInput) out = output.(*helloOutput) @@ -220,7 +220,7 @@ u.SetTitle("Greeter") u.SetDescription("Greeter greets you.") u.Input = new(helloInput) u.Output = new(helloOutput) -u.Interactor = usecase.Interact(func(ctx context.Context, input, output interface{}) error { +u.Interactor = usecase.Interact(func(ctx context.Context, input, output any) error { // Do something about input to prepare output. return nil }) diff --git a/_examples/advanced-generic/error_response.go b/_examples/advanced-generic/error_response.go index b98213d..2e41b55 100644 --- a/_examples/advanced-generic/error_response.go +++ b/_examples/advanced-generic/error_response.go @@ -12,8 +12,8 @@ import ( ) type customErr struct { - Message string `json:"msg"` - Details map[string]interface{} `json:"details,omitempty"` + Message string `json:"msg"` + Details map[string]any `json:"details,omitempty"` } func errorResponse() usecase.Interactor { diff --git a/_examples/advanced-generic/output_headers_test.go b/_examples/advanced-generic/output_headers_test.go index 1736619..e37dffe 100644 --- a/_examples/advanced-generic/output_headers_test.go +++ b/_examples/advanced-generic/output_headers_test.go @@ -3,6 +3,7 @@ package main import ( + "io" "io/ioutil" "net/http" "net/http/httptest" @@ -41,7 +42,7 @@ func Test_outputHeaders(t *testing.T) { assert.Equal(t, resp.StatusCode, http.StatusOK) - body, err := ioutil.ReadAll(resp.Body) + body, err := io.ReadAll(resp.Body) assert.NoError(t, err) assert.NoError(t, resp.Body.Close()) diff --git a/_examples/advanced-generic/output_writer.go b/_examples/advanced-generic/output_writer.go index 4fbc981..e2880ae 100644 --- a/_examples/advanced-generic/output_writer.go +++ b/_examples/advanced-generic/output_writer.go @@ -16,7 +16,7 @@ func outputCSVWriter() usecase.Interactor { usecase.OutputWithEmbeddedWriter } - u := usecase.NewInteractor(func(ctx context.Context, _ interface{}, out *writerOutput) (err error) { + u := usecase.NewInteractor(func(ctx context.Context, _ any, out *writerOutput) (err error) { out.Header = "abc" c := csv.NewWriter(out) diff --git a/_examples/advanced-generic/request_response_mapping_test.go b/_examples/advanced-generic/request_response_mapping_test.go index 0c85192..b8aaa39 100644 --- a/_examples/advanced-generic/request_response_mapping_test.go +++ b/_examples/advanced-generic/request_response_mapping_test.go @@ -4,6 +4,7 @@ package main import ( "bytes" + "io" "io/ioutil" "net/http" "net/http/httptest" @@ -32,7 +33,7 @@ func Test_requestResponseMapping(t *testing.T) { assert.Equal(t, http.StatusNoContent, resp.StatusCode) - body, err := ioutil.ReadAll(resp.Body) + body, err := io.ReadAll(resp.Body) assert.NoError(t, err) assert.NoError(t, resp.Body.Close()) assert.Equal(t, "", string(body)) diff --git a/_examples/advanced-generic/router.go b/_examples/advanced-generic/router.go index 6b8ba2c..5062f29 100644 --- a/_examples/advanced-generic/router.go +++ b/_examples/advanced-generic/router.go @@ -71,7 +71,7 @@ func NewRouter() http.Handler { func(handler http.Handler) http.Handler { var h *nethttp.Handler if nethttp.HandlerAs(handler, &h) { - h.MakeErrResp = func(ctx context.Context, err error) (int, interface{}) { + h.MakeErrResp = func(ctx context.Context, err error) (int, any) { code, er := rest.Err(err) var ae anotherErr diff --git a/_examples/advanced/dummy.go b/_examples/advanced/dummy.go index da56516..a48c897 100644 --- a/_examples/advanced/dummy.go +++ b/_examples/advanced/dummy.go @@ -7,7 +7,7 @@ import ( ) func dummy() usecase.Interactor { - return usecase.NewIOI(nil, nil, func(ctx context.Context, input, output interface{}) error { + return usecase.NewIOI(nil, nil, func(ctx context.Context, input, output any) error { return nil }) } diff --git a/_examples/advanced/error_response.go b/_examples/advanced/error_response.go index f872d39..a3f1221 100644 --- a/_examples/advanced/error_response.go +++ b/_examples/advanced/error_response.go @@ -10,8 +10,8 @@ import ( ) type customErr struct { - Message string `json:"msg"` - Details map[string]interface{} `json:"details,omitempty"` + Message string `json:"msg"` + Details map[string]any `json:"details,omitempty"` } func errorResponse() usecase.Interactor { @@ -23,7 +23,7 @@ func errorResponse() usecase.Interactor { Status string `json:"status"` } - u := usecase.NewIOI(new(errType), new(okResp), func(ctx context.Context, input, output interface{}) (err error) { + u := usecase.NewIOI(new(errType), new(okResp), func(ctx context.Context, input, output any) (err error) { var ( in = input.(*errType) out = output.(*okResp) diff --git a/_examples/advanced/file_multi_upload.go b/_examples/advanced/file_multi_upload.go index ff572cf..7b8cb06 100644 --- a/_examples/advanced/file_multi_upload.go +++ b/_examples/advanced/file_multi_upload.go @@ -26,7 +26,7 @@ func fileMultiUploader() usecase.Interactor { Query int `json:"inQuery"` } - u := usecase.NewIOI(new(upload), new(info), func(ctx context.Context, input, output interface{}) (err error) { + u := usecase.NewIOI(new(upload), new(info), func(ctx context.Context, input, output any) (err error) { var ( in = input.(*upload) out = output.(*info) diff --git a/_examples/advanced/file_upload.go b/_examples/advanced/file_upload.go index 4ec2d2e..0e8f881 100644 --- a/_examples/advanced/file_upload.go +++ b/_examples/advanced/file_upload.go @@ -26,7 +26,7 @@ func fileUploader() usecase.Interactor { Query int `json:"inQuery"` } - u := usecase.NewIOI(new(upload), new(info), func(ctx context.Context, input, output interface{}) (err error) { + u := usecase.NewIOI(new(upload), new(info), func(ctx context.Context, input, output any) (err error) { var ( in = input.(*upload) out = output.(*info) diff --git a/_examples/advanced/gzip_pass_through.go b/_examples/advanced/gzip_pass_through.go index 4b597ef..de5c3b5 100644 --- a/_examples/advanced/gzip_pass_through.go +++ b/_examples/advanced/gzip_pass_through.go @@ -66,7 +66,7 @@ func directGzip() usecase.Interactor { } u := usecase.NewIOI(new(gzipPassThroughInput), new(gzipPassThroughOutput), - func(ctx context.Context, input, output interface{}) error { + func(ctx context.Context, input, output any) error { var ( in = input.(*gzipPassThroughInput) out = output.(*gzipPassThroughOutput) diff --git a/_examples/advanced/json_body.go b/_examples/advanced/json_body.go index f26968e..b01251b 100644 --- a/_examples/advanced/json_body.go +++ b/_examples/advanced/json_body.go @@ -29,7 +29,7 @@ func jsonBody() usecase.Interactor { } u := usecase.NewIOI(new(inputWithJSON), new(outputWithJSON), - func(ctx context.Context, input, output interface{}) (err error) { + func(ctx context.Context, input, output any) (err error) { var ( in = input.(*inputWithJSON) out = output.(*outputWithJSON) diff --git a/_examples/advanced/json_body_validation.go b/_examples/advanced/json_body_validation.go index ccffcb7..b3d5f09 100644 --- a/_examples/advanced/json_body_validation.go +++ b/_examples/advanced/json_body_validation.go @@ -34,7 +34,7 @@ func jsonBodyValidation() usecase.Interactor { u.Input = new(inputWithJSON) u.Output = new(outputWithJSON) - u.Interactor = usecase.Interact(func(ctx context.Context, input, output interface{}) (err error) { + u.Interactor = usecase.Interact(func(ctx context.Context, input, output any) (err error) { var ( in = input.(*inputWithJSON) out = output.(*outputWithJSON) diff --git a/_examples/advanced/json_map_body.go b/_examples/advanced/json_map_body.go index 5e80d7e..f8a0472 100644 --- a/_examples/advanced/json_map_body.go +++ b/_examples/advanced/json_map_body.go @@ -26,7 +26,7 @@ func jsonMapBody() usecase.Interactor { Data JSONMapPayload `json:"data"` } - u := usecase.NewIOI(new(jsonMapReq), new(jsonOutput), func(ctx context.Context, input, output interface{}) (err error) { + u := usecase.NewIOI(new(jsonMapReq), new(jsonOutput), func(ctx context.Context, input, output any) (err error) { var ( in = input.(*jsonMapReq) out = output.(*jsonOutput) diff --git a/_examples/advanced/json_param.go b/_examples/advanced/json_param.go index 193bf5e..625b4fb 100644 --- a/_examples/advanced/json_param.go +++ b/_examples/advanced/json_param.go @@ -28,7 +28,7 @@ func jsonParam() usecase.Interactor { JSONPayload } - u := usecase.NewIOI(new(inputWithJSON), new(outputWithJSON), func(ctx context.Context, input, output interface{}) (err error) { + u := usecase.NewIOI(new(inputWithJSON), new(outputWithJSON), func(ctx context.Context, input, output any) (err error) { var ( in = input.(*inputWithJSON) out = output.(*outputWithJSON) diff --git a/_examples/advanced/json_slice_body.go b/_examples/advanced/json_slice_body.go index d85b0b7..7e346f4 100644 --- a/_examples/advanced/json_slice_body.go +++ b/_examples/advanced/json_slice_body.go @@ -26,7 +26,7 @@ func jsonSliceBody() usecase.Interactor { Data JSONSlicePayload `json:"data"` } - u := usecase.NewIOI(new(jsonSliceReq), new(jsonOutput), func(ctx context.Context, input, output interface{}) (err error) { + u := usecase.NewIOI(new(jsonSliceReq), new(jsonOutput), func(ctx context.Context, input, output any) (err error) { var ( in = input.(*jsonSliceReq) out = output.(*jsonOutput) diff --git a/_examples/advanced/no_validation.go b/_examples/advanced/no_validation.go index 53e7385..0b9a76f 100644 --- a/_examples/advanced/no_validation.go +++ b/_examples/advanced/no_validation.go @@ -23,7 +23,7 @@ func noValidation() usecase.Interactor { } `json:"data"` } - u := usecase.NewIOI(new(inputPort), new(outputPort), func(ctx context.Context, input, output interface{}) (err error) { + u := usecase.NewIOI(new(inputPort), new(outputPort), func(ctx context.Context, input, output any) (err error) { in := input.(*inputPort) out := output.(*outputPort) diff --git a/_examples/advanced/output_headers.go b/_examples/advanced/output_headers.go index 61586f4..d4eff72 100644 --- a/_examples/advanced/output_headers.go +++ b/_examples/advanced/output_headers.go @@ -23,7 +23,7 @@ func outputHeaders() usecase.Interactor { u.Output = new(headerOutput) - u.Interactor = usecase.Interact(func(ctx context.Context, input, output interface{}) (err error) { + u.Interactor = usecase.Interact(func(ctx context.Context, input, output any) (err error) { out := output.(*headerOutput) out.Header = "abc" diff --git a/_examples/advanced/output_writer.go b/_examples/advanced/output_writer.go index 14eb1f2..9c301a5 100644 --- a/_examples/advanced/output_writer.go +++ b/_examples/advanced/output_writer.go @@ -26,7 +26,7 @@ func outputCSVWriter() usecase.Interactor { u.Output = new(writerOutput) - u.Interactor = usecase.Interact(func(ctx context.Context, input, output interface{}) (err error) { + u.Interactor = usecase.Interact(func(ctx context.Context, input, output any) (err error) { out := output.(*writerOutput) out.Header = "abc" diff --git a/_examples/advanced/query_object.go b/_examples/advanced/query_object.go index 3cc91d2..a93e187 100644 --- a/_examples/advanced/query_object.go +++ b/_examples/advanced/query_object.go @@ -16,7 +16,7 @@ func queryObject() usecase.Interactor { } u := usecase.NewIOI(new(inputQueryObject), new(outputQueryObject), - func(ctx context.Context, input, output interface{}) (err error) { + func(ctx context.Context, input, output any) (err error) { var ( in = input.(*inputQueryObject) out = output.(*outputQueryObject) diff --git a/_examples/advanced/request_response_mapping.go b/_examples/advanced/request_response_mapping.go index 3b159cf..83e6dbc 100644 --- a/_examples/advanced/request_response_mapping.go +++ b/_examples/advanced/request_response_mapping.go @@ -17,7 +17,7 @@ func reqRespMapping() usecase.Interactor { Val2 int `json:"-" description:"Simple scalar value with sample validation." required:"true" minimum:"3"` } - u := usecase.NewIOI(new(inputPort), new(outputPort), func(ctx context.Context, input, output interface{}) (err error) { + u := usecase.NewIOI(new(inputPort), new(outputPort), func(ctx context.Context, input, output any) (err error) { var ( in = input.(*inputPort) out = output.(*outputPort) diff --git a/_examples/advanced/request_response_mapping_test.go b/_examples/advanced/request_response_mapping_test.go index 5db99a5..3d03d6f 100644 --- a/_examples/advanced/request_response_mapping_test.go +++ b/_examples/advanced/request_response_mapping_test.go @@ -2,6 +2,7 @@ package main import ( "bytes" + "io" "io/ioutil" "net/http" "net/http/httptest" @@ -30,7 +31,7 @@ func Test_requestResponseMapping(t *testing.T) { assert.Equal(t, http.StatusNoContent, resp.StatusCode) - body, err := ioutil.ReadAll(resp.Body) + body, err := io.ReadAll(resp.Body) assert.NoError(t, err) assert.NoError(t, resp.Body.Close()) assert.Equal(t, "", string(body)) diff --git a/_examples/advanced/router.go b/_examples/advanced/router.go index c282689..d22a849 100644 --- a/_examples/advanced/router.go +++ b/_examples/advanced/router.go @@ -69,7 +69,7 @@ func NewRouter() http.Handler { func(handler http.Handler) http.Handler { var h *nethttp.Handler if nethttp.HandlerAs(handler, &h) { - h.MakeErrResp = func(ctx context.Context, err error) (int, interface{}) { + h.MakeErrResp = func(ctx context.Context, err error) (int, any) { code, er := rest.Err(err) var ae anotherErr diff --git a/_examples/advanced/validation.go b/_examples/advanced/validation.go index be47e1b..a083b88 100644 --- a/_examples/advanced/validation.go +++ b/_examples/advanced/validation.go @@ -23,7 +23,7 @@ func validation() usecase.Interactor { } `json:"data" required:"true"` } - u := usecase.NewIOI(new(inputPort), new(outputPort), func(ctx context.Context, input, output interface{}) (err error) { + u := usecase.NewIOI(new(inputPort), new(outputPort), func(ctx context.Context, input, output any) (err error) { in := input.(*inputPort) out := output.(*outputPort) diff --git a/_examples/basic/main.go b/_examples/basic/main.go index 95683ba..3be07ce 100644 --- a/_examples/basic/main.go +++ b/_examples/basic/main.go @@ -46,7 +46,7 @@ func main() { } // Create use case interactor with references to input/output types and interaction function. - u := usecase.NewIOI(new(helloInput), new(helloOutput), func(ctx context.Context, input, output interface{}) error { + u := usecase.NewIOI(new(helloInput), new(helloOutput), func(ctx context.Context, input, output any) error { var ( in = input.(*helloInput) out = output.(*helloOutput) diff --git a/_examples/task-api/internal/infra/log/usecase.go b/_examples/task-api/internal/infra/log/usecase.go index 2df9566..450d4fd 100644 --- a/_examples/task-api/internal/infra/log/usecase.go +++ b/_examples/task-api/internal/infra/log/usecase.go @@ -25,7 +25,7 @@ func UseCaseMiddleware() usecase.Middleware { name = hasName.Name() } - return usecase.Interact(func(ctx context.Context, input, output interface{}) error { + return usecase.Interact(func(ctx context.Context, input, output any) error { err := next.Interact(ctx, input, output) if err != nil { log.Printf("usecase %s request (%v) failed: %v", name, input, err) diff --git a/_examples/task-api/internal/infra/repository/task.go b/_examples/task-api/internal/infra/repository/task.go index a58bcb3..0b9f593 100644 --- a/_examples/task-api/internal/infra/repository/task.go +++ b/_examples/task-api/internal/infra/repository/task.go @@ -130,7 +130,7 @@ func (tr *Task) Create(ctx context.Context, value task.Value) (task.Entity, erro if t.Value.Goal == value.Goal { return task.Entity{}, usecase.Error{ StatusCode: status.AlreadyExists, - Context: map[string]interface{}{ + Context: map[string]any{ "task": t, }, Value: errors.New("task with same goal already exists"), diff --git a/_examples/task-api/internal/usecase/create_task.go b/_examples/task-api/internal/usecase/create_task.go index 6e183ae..ae49e2e 100644 --- a/_examples/task-api/internal/usecase/create_task.go +++ b/_examples/task-api/internal/usecase/create_task.go @@ -15,7 +15,7 @@ func CreateTask( TaskCreator() task.Creator }, ) usecase.IOInteractor { - u := usecase.NewIOI(new(task.Value), new(task.Entity), func(ctx context.Context, input, output interface{}) error { + u := usecase.NewIOI(new(task.Value), new(task.Entity), func(ctx context.Context, input, output any) error { var ( in = input.(*task.Value) out = output.(*task.Entity) diff --git a/_examples/task-api/internal/usecase/find_task.go b/_examples/task-api/internal/usecase/find_task.go index af908e2..d40b4fd 100644 --- a/_examples/task-api/internal/usecase/find_task.go +++ b/_examples/task-api/internal/usecase/find_task.go @@ -15,7 +15,7 @@ func FindTask( }, ) usecase.IOInteractor { u := usecase.NewIOI(new(task.Identity), new(task.Entity), - func(ctx context.Context, input, output interface{}) error { + func(ctx context.Context, input, output any) error { var ( in = input.(*task.Identity) out = output.(*task.Entity) diff --git a/_examples/task-api/internal/usecase/find_tasks.go b/_examples/task-api/internal/usecase/find_tasks.go index 1c37739..404ed9d 100644 --- a/_examples/task-api/internal/usecase/find_tasks.go +++ b/_examples/task-api/internal/usecase/find_tasks.go @@ -15,7 +15,7 @@ func FindTasks( TaskFinder() task.Finder }, ) usecase.IOInteractor { - u := usecase.NewIOI(nil, new([]task.Entity), func(ctx context.Context, input, output interface{}) error { + u := usecase.NewIOI(nil, new([]task.Entity), func(ctx context.Context, input, output any) error { out, ok := output.(*[]task.Entity) if !ok { return fmt.Errorf("%w: unexpected output type %T", status.Unimplemented, output) diff --git a/_examples/task-api/internal/usecase/finish_task.go b/_examples/task-api/internal/usecase/finish_task.go index 6ddb41a..e70cd6e 100644 --- a/_examples/task-api/internal/usecase/finish_task.go +++ b/_examples/task-api/internal/usecase/finish_task.go @@ -14,7 +14,7 @@ type finishTaskDeps interface { // FinishTask creates usecase interactor. func FinishTask(deps finishTaskDeps) usecase.IOInteractor { - u := usecase.NewIOI(new(task.Identity), nil, func(ctx context.Context, input, _ interface{}) error { + u := usecase.NewIOI(new(task.Identity), nil, func(ctx context.Context, input, _ any) error { var ( in = input.(*task.Identity) err error diff --git a/_examples/task-api/internal/usecase/update_task.go b/_examples/task-api/internal/usecase/update_task.go index 30f18e4..92d0738 100644 --- a/_examples/task-api/internal/usecase/update_task.go +++ b/_examples/task-api/internal/usecase/update_task.go @@ -19,7 +19,7 @@ func UpdateTask( TaskUpdater() task.Updater }, ) usecase.Interactor { - u := usecase.NewIOI(new(updateTask), nil, func(ctx context.Context, input, _ interface{}) error { + u := usecase.NewIOI(new(updateTask), nil, func(ctx context.Context, input, _ any) error { var ( in = input.(*updateTask) err error diff --git a/chirouter/wrapper_test.go b/chirouter/wrapper_test.go index d36ccd4..1cb42f0 100644 --- a/chirouter/wrapper_test.go +++ b/chirouter/wrapper_test.go @@ -272,7 +272,7 @@ func TestWrapper_Mount(t *testing.T) { nethttp.HTTPBasicSecurityMiddleware(service.OpenAPICollector, "Admin", "Admin access"), ) - apiV1.Post("/sum", usecase.NewIOI(new([]int), new(int), func(ctx context.Context, input, output interface{}) error { + apiV1.Post("/sum", usecase.NewIOI(new([]int), new(int), func(ctx context.Context, input, output any) error { return errors.New("oops") })) diff --git a/error.go b/error.go index 90623e0..d70d3df 100644 --- a/error.go +++ b/error.go @@ -16,7 +16,7 @@ type ErrWithHTTPStatus interface { // ErrWithFields exposes structured context of error. type ErrWithFields interface { error - Fields() map[string]interface{} + Fields() map[string]any } // ErrWithAppCode exposes application error code. @@ -81,10 +81,10 @@ func Err(err error) (int, ErrResponse) { // ErrResponse is HTTP error response body. type ErrResponse struct { - StatusText string `json:"status,omitempty" description:"Status text."` - AppCode int `json:"code,omitempty" description:"Application-specific error code."` - ErrorText string `json:"error,omitempty" description:"Error message."` - Context map[string]interface{} `json:"context,omitempty" description:"Application context."` + StatusText string `json:"status,omitempty" description:"Status text."` + AppCode int `json:"code,omitempty" description:"Application-specific error code."` + ErrorText string `json:"error,omitempty" description:"Error message."` + Context map[string]any `json:"context,omitempty" description:"Application context."` err error // Original error. httpStatusCode int // HTTP response status code. diff --git a/error_test.go b/error_test.go index 512667d..273ab0d 100644 --- a/error_test.go +++ b/error_test.go @@ -34,12 +34,12 @@ func TestErr(t *testing.T) { err := usecase.Error{ StatusCode: status.InvalidArgument, Value: errors.New("failed"), - Context: map[string]interface{}{"hello": "world"}, + Context: map[string]any{"hello": "world"}, } code, er := rest.Err(err) assert.Equal(t, http.StatusBadRequest, code) - assert.Equal(t, map[string]interface{}{"hello": "world"}, er.Context) + assert.Equal(t, map[string]any{"hello": "world"}, er.Context) assert.Equal(t, "invalid argument: failed", er.Error()) assert.Equal(t, "INVALID_ARGUMENT", er.StatusText) assert.Equal(t, 0, er.AppCode) diff --git a/go.mod b/go.mod index d8cc2c4..91d498c 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/swaggest/rest -go 1.17 +go 1.18 require ( github.com/bool64/dev v0.2.22 diff --git a/gzip/container.go b/gzip/container.go index 7369bd1..b5aba29 100644 --- a/gzip/container.go +++ b/gzip/container.go @@ -5,7 +5,6 @@ import ( "compress/gzip" "encoding/json" "io" - "io/ioutil" "strconv" "github.com/cespare/xxhash/v2" @@ -39,18 +38,19 @@ func WriteCompressedBytes(compressed []byte, w io.Writer) (int, error) { return 0, err } - n, err := io.Copy(w, gr) //nolint:gosec // The origin of compressed data supposed to be app itself, safe to copy. + // The origin of compressed data supposed to be app itself, safe to copy. + n, err := io.Copy(w, gr) //nolint:gosec return int(n), err } // UnpackJSON unmarshals data from JSON container into a Go value. -func (jc JSONContainer) UnpackJSON(v interface{}) error { +func (jc JSONContainer) UnpackJSON(v any) error { return UnmarshalJSON(jc.gz, v) } // PackJSON puts Go value in JSON container. -func (jc *JSONContainer) PackJSON(v interface{}) error { +func (jc *JSONContainer) PackJSON(v any) error { res, err := MarshalJSON(v) if err != nil { return err @@ -83,7 +83,7 @@ func (jc JSONContainer) MarshalJSON() (j []byte, err error) { } }() - return ioutil.ReadAll(r) + return io.ReadAll(r) } // ETag returns hash of compressed bytes. @@ -92,7 +92,7 @@ func (jc JSONContainer) ETag() string { } // MarshalJSON encodes Go value as JSON and compresses result with gzip. -func MarshalJSON(v interface{}) ([]byte, error) { +func MarshalJSON(v any) ([]byte, error) { b := bytes.Buffer{} w := gzip.NewWriter(&b) @@ -116,7 +116,7 @@ func MarshalJSON(v interface{}) ([]byte, error) { } // UnmarshalJSON decodes compressed JSON bytes into a Go value. -func UnmarshalJSON(data []byte, v interface{}) error { +func UnmarshalJSON(data []byte, v any) error { b := bytes.NewReader(data) r, err := gzip.NewReader(b) diff --git a/gzip/container_test.go b/gzip/container_test.go index 89fa113..1674279 100644 --- a/gzip/container_test.go +++ b/gzip/container_test.go @@ -46,11 +46,11 @@ func TestWriteJSON(t *testing.T) { usecase.WithOutput }{} - var ur interface{} = cont + var ur any = cont - u.Output = new(interface{}) - u.Interactor = usecase.Interact(func(ctx context.Context, input, output interface{}) error { - *output.(*interface{}) = ur + u.Output = new(any) + u.Interactor = usecase.Interact(func(ctx context.Context, input, output any) error { + *output.(*any) = ur return nil }) diff --git a/jsonschema/validator.go b/jsonschema/validator.go index e9a0d9b..ad9209a 100644 --- a/jsonschema/validator.go +++ b/jsonschema/validator.go @@ -15,7 +15,7 @@ var _ rest.Validator = &Validator{} // Validator is a JSON Schema based validator. type Validator struct { // JSONMarshal controls custom marshaler, nil value enables "encoding/json". - JSONMarshal func(interface{}) ([]byte, error) + JSONMarshal func(any) ([]byte, error) inNamedSchemas map[rest.ParamIn]map[string]*jsonschema.Schema inRequired map[rest.ParamIn][]string @@ -38,7 +38,7 @@ func NewFactory( // Please use NewFactory to create an instance. type Factory struct { // JSONMarshal controls custom marshaler, nil value enables "encoding/json". - JSONMarshal func(interface{}) ([]byte, error) + JSONMarshal func(any) ([]byte, error) requestSchemas rest.RequestJSONSchemaProvider responseSchemas rest.ResponseJSONSchemaProvider @@ -47,7 +47,7 @@ type Factory struct { // MakeRequestValidator creates request validator for HTTP method and input structure. func (f Factory) MakeRequestValidator( method string, - input interface{}, + input any, mapping rest.RequestMapping, ) rest.Validator { v := Validator{ @@ -68,7 +68,7 @@ func (f Factory) MakeRequestValidator( func (f Factory) MakeResponseValidator( statusCode int, contentType string, - output interface{}, + output any, headerMapping map[string]string, ) rest.Validator { v := Validator{ @@ -139,7 +139,7 @@ func (v *Validator) AddSchema(in rest.ParamIn, name string, jsonSchema []byte, r return nil } -func (v *Validator) checkRequired(in rest.ParamIn, namedData map[string]interface{}) []string { +func (v *Validator) checkRequired(in rest.ParamIn, namedData map[string]any) []string { required := v.inRequired[in] if len(required) == 0 { @@ -189,7 +189,7 @@ func (v *Validator) HasConstraints(in rest.ParamIn) bool { } // ValidateData performs validation of a mapped request data. -func (v *Validator) ValidateData(in rest.ParamIn, namedData map[string]interface{}) error { +func (v *Validator) ValidateData(in rest.ParamIn, namedData map[string]any) error { var errs rest.ValidationErrors missing := v.checkRequired(in, namedData) diff --git a/jsonschema/validator_test.go b/jsonschema/validator_test.go index 992a057..079cbce 100644 --- a/jsonschema/validator_test.go +++ b/jsonschema/validator_test.go @@ -24,7 +24,7 @@ func BenchmarkRequestValidator_ValidateRequestData(b *testing.B) { b.ResetTimer() b.ReportAllocs() - value := map[string]interface{}{ + value := map[string]any{ "in_cookie": "abc", } @@ -47,27 +47,27 @@ func TestRequestValidator_ValidateData(t *testing.T) { rest.ParamInFormData: map[string]string{"FormData": "inFormData"}, }) - err := validator.ValidateData(rest.ParamInCookie, map[string]interface{}{"in_cookie": 123}) + err := validator.ValidateData(rest.ParamInCookie, map[string]any{"in_cookie": 123}) assert.Equal(t, err, rest.ValidationErrors{"cookie:in_cookie": []string{"#: expected string, but got number"}}) - err = validator.ValidateData(rest.ParamInCookie, map[string]interface{}{}) + err = validator.ValidateData(rest.ParamInCookie, map[string]any{}) assert.Equal(t, err, rest.ValidationErrors{"cookie:in_cookie": []string{"missing value"}}) - err = validator.ValidateData(rest.ParamInQuery, map[string]interface{}{"in_query": 123}) + err = validator.ValidateData(rest.ParamInQuery, map[string]any{"in_query": 123}) assert.Equal(t, err, rest.ValidationErrors{"query:in_query": []string{"#: expected string, but got number"}}) - err = validator.ValidateData(rest.ParamInQuery, map[string]interface{}{"in_query": "ab"}) + err = validator.ValidateData(rest.ParamInQuery, map[string]any{"in_query": "ab"}) assert.Equal(t, err, rest.ValidationErrors{"query:in_query": []string{"#: length must be >= 3, but got 2"}}) - assert.NoError(t, validator.ValidateData(rest.ParamInQuery, map[string]interface{}{})) - assert.NoError(t, validator.ValidateData(rest.ParamInQuery, map[string]interface{}{"unknown": 123})) - assert.NoError(t, validator.ValidateData(rest.ParamInQuery, map[string]interface{}{"in_query_ignored": 123})) - assert.NoError(t, validator.ValidateData("unknown", map[string]interface{}{})) - assert.NoError(t, validator.ValidateData(rest.ParamInCookie, map[string]interface{}{"in_cookie": "abc"})) + assert.NoError(t, validator.ValidateData(rest.ParamInQuery, map[string]any{})) + assert.NoError(t, validator.ValidateData(rest.ParamInQuery, map[string]any{"unknown": 123})) + assert.NoError(t, validator.ValidateData(rest.ParamInQuery, map[string]any{"in_query_ignored": 123})) + assert.NoError(t, validator.ValidateData("unknown", map[string]any{})) + assert.NoError(t, validator.ValidateData(rest.ParamInCookie, map[string]any{"in_cookie": "abc"})) - assert.NoError(t, validator.ValidateData(rest.ParamInFormData, map[string]interface{}{"inFormData": "abc"})) + assert.NoError(t, validator.ValidateData(rest.ParamInFormData, map[string]any{"inFormData": "abc"})) - err = validator.ValidateData(rest.ParamInFormData, map[string]interface{}{"inFormData": "ab"}) + err = validator.ValidateData(rest.ParamInFormData, map[string]any{"inFormData": "ab"}) assert.Equal(t, err, rest.ValidationErrors{"formData:inFormData": []string{"#: length must be >= 3, but got 2"}}) } @@ -82,21 +82,21 @@ func TestFactory_MakeResponseValidator(t *testing.T) { assert.NoError(t, validator.ValidateJSONBody([]byte(`{"name":"John"}`))) assert.Error(t, validator.ValidateJSONBody([]byte(`{"name":""}`))) // minLength:"1" violated. - assert.NoError(t, validator.ValidateData(rest.ParamInHeader, map[string]interface{}{ + assert.NoError(t, validator.ValidateData(rest.ParamInHeader, map[string]any{ "X-Trace": "abc", })) - assert.Error(t, validator.ValidateData(rest.ParamInHeader, map[string]interface{}{ + assert.Error(t, validator.ValidateData(rest.ParamInHeader, map[string]any{ "X-Trace": "abcd", // maxLength:"3" violated. })) } func TestNullableTime(t *testing.T) { - type request struct { + type req struct { ExpiryDate *time.Time `json:"expiryDate"` } validator := jsonschema.NewFactory(&openapi.Collector{}, &openapi.Collector{}). - MakeRequestValidator(http.MethodPost, new(request), nil) + MakeRequestValidator(http.MethodPost, new(req), nil) err := validator.ValidateJSONBody([]byte(`{"expiryDate":null}`)) assert.NoError(t, err, "%+v", err) diff --git a/nethttp/example_test.go b/nethttp/example_test.go index 6d8db9f..f34bfeb 100644 --- a/nethttp/example_test.go +++ b/nethttp/example_test.go @@ -47,7 +47,7 @@ func ExampleSecurityMiddleware() { }, }) - u := usecase.NewIOI(nil, nil, func(ctx context.Context, input, output interface{}) error { + u := usecase.NewIOI(nil, nil, func(ctx context.Context, input, output any) error { // Do something. return nil }) diff --git a/nethttp/handler.go b/nethttp/handler.go index 41af5ef..9021446 100644 --- a/nethttp/handler.go +++ b/nethttp/handler.go @@ -64,6 +64,9 @@ type Handler struct { useCase usecase.Interactor + // useCaseInteractorHooks run before and after the Interactor is executed. + useCaseInteractorHooks []UseCaseHook + inputBufferType reflect.Type inputIsPtr bool @@ -82,7 +85,7 @@ func (h *Handler) SetRequestDecoder(requestDecoder RequestDecoder) { h.requestDecoder = requestDecoder } -func (h *Handler) decodeRequest(r *http.Request) (interface{}, error) { +func (h *Handler) decodeRequest(r *http.Request) (any, error) { if h.requestDecoder == nil { panic("request decoder is not initialized, please use SetRequestDecoder") } @@ -100,7 +103,7 @@ func (h *Handler) decodeRequest(r *http.Request) (interface{}, error) { // ServeHTTP serves http inputPort with use case interactor. func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { var ( - input, output interface{} + input, output any err error ) @@ -119,26 +122,44 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { if err != nil { h.handleDecodeError(w, r, err, input, output) - return } } - err = h.useCase.Interact(r.Context(), input, output) + // pre interactor hooks + if err := h.executeHooks(r.Context(), input, output, true); err != nil { + h.handleErrResponse(w, r, err) + return + } - if err != nil { + if err = h.useCase.Interact(r.Context(), input, output); err != nil { h.handleErrResponse(w, r, err) + return + } + // post interactor hooks + if err := h.executeHooks(r.Context(), input, output, false); err != nil { + h.handleErrResponse(w, r, err) return } h.responseEncoder.WriteSuccessfulResponse(w, r, output, h.HandlerTrait) } +func (h *Handler) executeHooks(ctx context.Context, input, output any, beforeInteract bool) error { + for _, hook := range h.useCaseInteractorHooks { + if err := hook(ctx, input, output, beforeInteract); err != nil { + return err + } + } + + return nil +} + func (h *Handler) handleErrResponseDefault(w http.ResponseWriter, r *http.Request, err error) { var ( code int - er interface{} + er any ) if h.MakeErrResp != nil { @@ -168,7 +189,7 @@ func closeMultipartForm(r *http.Request) { type decodeErrCtxKey struct{} -func (h *Handler) handleDecodeError(w http.ResponseWriter, r *http.Request, err error, input, output interface{}) { +func (h *Handler) handleDecodeError(w http.ResponseWriter, r *http.Request, err error, input, output any) { err = status.Wrap(err, status.InvalidArgument) if h.failingUseCase != nil { @@ -198,7 +219,7 @@ func (h *Handler) setupInputBuffer() { func (h *Handler) setupOutputBuffer() { var ( withOutput usecase.HasOutputPort - output interface{} + output any ) if usecase.As(h.useCase, &withOutput) && reflect.TypeOf(withOutput.OutputPort()) != nil { @@ -243,18 +264,26 @@ func HandlerWithRouteMiddleware(method, pathPattern string) func(http.Handler) h // RequestDecoder maps data from http.Request into structured Go input value. type RequestDecoder interface { - Decode(r *http.Request, input interface{}, validator rest.Validator) error + Decode(r *http.Request, input any, validator rest.Validator) error } // ResponseEncoder writes data from use case output/error into http.ResponseWriter. type ResponseEncoder interface { - WriteErrResponse(w http.ResponseWriter, r *http.Request, statusCode int, response interface{}) + WriteErrResponse(w http.ResponseWriter, r *http.Request, statusCode int, response any) WriteSuccessfulResponse( w http.ResponseWriter, r *http.Request, - output interface{}, + output any, ht rest.HandlerTrait, ) - SetupOutput(output interface{}, ht *rest.HandlerTrait) - MakeOutput(w http.ResponseWriter, ht rest.HandlerTrait) interface{} + SetupOutput(output any, ht *rest.HandlerTrait) + MakeOutput(w http.ResponseWriter, ht rest.HandlerTrait) any +} + +// UseCaseHook is a function that runs around the usecase interact execution. +type UseCaseHook func(ctx context.Context, input, output any, beforeInteract bool) error + +// SetUseCaseInteractorHooks sets usecase interactor hooks. +func (h *Handler) SetUseCaseInteractorHooks(hooks ...UseCaseHook) { + h.useCaseInteractorHooks = hooks } diff --git a/nethttp/handler_test.go b/nethttp/handler_test.go index 6e0d224..759fed9 100644 --- a/nethttp/handler_test.go +++ b/nethttp/handler_test.go @@ -3,6 +3,7 @@ package nethttp_test import ( "context" "errors" + "io" "net/http" "net/http/httptest" "strings" @@ -35,7 +36,7 @@ func TestHandler_ServeHTTP(t *testing.T) { u.Input = new(Input) u.Output = new(Output) - u.Interactor = usecase.Interact(func(ctx context.Context, input, output interface{}) error { + u.Interactor = usecase.Interact(func(ctx context.Context, input, output any) error { in, ok := input.(*Input) require.True(t, ok) require.NotNil(t, in) @@ -56,7 +57,7 @@ func TestHandler_ServeHTTP(t *testing.T) { validatorCalled := false h := nethttp.NewHandler(u, func(h *nethttp.Handler) { - h.ReqValidator = rest.ValidatorFunc(func(in rest.ParamIn, namedData map[string]interface{}) error { + h.ReqValidator = rest.ValidatorFunc(func(in rest.ParamIn, namedData map[string]any) error { validatorCalled = true return nil @@ -69,7 +70,7 @@ func TestHandler_ServeHTTP(t *testing.T) { h.SetResponseEncoder(&response.Encoder{}) h.SetRequestDecoder(request.DecoderFunc( - func(r *http.Request, input interface{}, validator rest.Validator) error { + func(r *http.Request, input any, validator rest.Validator) error { assert.Equal(t, req, r) in, ok := input.(*Input) require.True(t, ok) @@ -87,7 +88,7 @@ func TestHandler_ServeHTTP(t *testing.T) { umwCalled := false w := nethttp.UseCaseMiddlewares(usecase.MiddlewareFunc(func(next usecase.Interactor) usecase.Interactor { - return usecase.Interact(func(ctx context.Context, input, output interface{}) error { + return usecase.Interact(func(ctx context.Context, input, output any) error { umwCalled = true return next.Interact(ctx, input, output) @@ -116,7 +117,7 @@ func TestHandler_ServeHTTP_decodeErr(t *testing.T) { u.Input = new(Input) u.Output = new(Output) - u.Interactor = usecase.Interact(func(ctx context.Context, input, output interface{}) error { + u.Interactor = usecase.Interact(func(ctx context.Context, input, output any) error { assert.Fail(t, "should not be called") return nil @@ -127,7 +128,7 @@ func TestHandler_ServeHTTP_decodeErr(t *testing.T) { uh := nethttp.NewHandler(u) uh.SetRequestDecoder(request.DecoderFunc( - func(r *http.Request, input interface{}, validator rest.Validator) error { + func(r *http.Request, input any, validator rest.Validator) error { return errors.New("failed to decode request") }, )) @@ -135,7 +136,7 @@ func TestHandler_ServeHTTP_decodeErr(t *testing.T) { umwCalled := false h := nethttp.UseCaseMiddlewares(usecase.MiddlewareFunc(func(next usecase.Interactor) usecase.Interactor { - return usecase.Interact(func(ctx context.Context, input, output interface{}) error { + return usecase.Interact(func(ctx context.Context, input, output any) error { umwCalled = true return next.Interact(ctx, input, output) @@ -156,7 +157,7 @@ func TestHandler_ServeHTTP_emptyPorts(t *testing.T) { usecase.Interactor }{} - u.Interactor = usecase.Interact(func(ctx context.Context, input, output interface{}) error { + u.Interactor = usecase.Interact(func(ctx context.Context, input, output any) error { assert.Nil(t, input) assert.Nil(t, output) @@ -182,7 +183,7 @@ func TestHandler_ServeHTTP_customErrResp(t *testing.T) { usecase.OutputWithNoContent }{} - u.Interactor = usecase.Interact(func(ctx context.Context, input, output interface{}) error { + u.Interactor = usecase.Interact(func(ctx context.Context, input, output any) error { assert.Nil(t, input) assert.Nil(t, output) @@ -190,7 +191,7 @@ func TestHandler_ServeHTTP_customErrResp(t *testing.T) { }) h := nethttp.NewHandler(u) - h.MakeErrResp = func(ctx context.Context, err error) (int, interface{}) { + h.MakeErrResp = func(ctx context.Context, err error) (int, any) { return http.StatusExpectationFailed, struct { Custom string `json:"custom"` }{ @@ -242,7 +243,7 @@ func TestHandler_ServeHTTP_getWithBody(t *testing.T) { u.Input = new(reqWithBody) - u.Interactor = usecase.Interact(func(ctx context.Context, input, output interface{}) error { + u.Interactor = usecase.Interact(func(ctx context.Context, input, output any) error { in, ok := input.(*reqWithBody) assert.True(t, ok) assert.Equal(t, 123, in.ID) @@ -273,7 +274,7 @@ func TestHandler_ServeHTTP_customMapping(t *testing.T) { }{} u.Input = new(Input) - u.Interactor = usecase.Interact(func(ctx context.Context, input, output interface{}) error { + u.Interactor = usecase.Interact(func(ctx context.Context, input, output any) error { in, ok := input.(*Input) assert.True(t, ok) assert.Equal(t, 123, in.ID) @@ -309,11 +310,11 @@ func TestHandler_ServeHTTP_customMapping(t *testing.T) { } func TestOptionsMiddleware(t *testing.T) { - u := usecase.NewIOI(nil, nil, func(ctx context.Context, input, output interface{}) error { + u := usecase.NewIOI(nil, nil, func(ctx context.Context, input, output any) error { return errors.New("failed") }) h := nethttp.NewHandler(u, func(h *nethttp.Handler) { - h.MakeErrResp = func(ctx context.Context, err error) (int, interface{}) { + h.MakeErrResp = func(ctx context.Context, err error) (int, any) { return http.StatusExpectationFailed, struct { Foo string `json:"foo"` }{Foo: err.Error()} @@ -341,3 +342,140 @@ func TestOptionsMiddleware(t *testing.T) { assert.EqualError(t, loggedErr, "failed") assert.Equal(t, `{"foo":"failed"}`+"\n", rw.Body.String()) } + +type inputWithQuery struct { + ID int `query:"id"` +} + +func TestHandler_ServeHTTP_interactHooks(t *testing.T) { + t.Parallel() + + assertInput := func(input any) { + in, ok := input.(*inputWithQuery) + assert.True(t, ok) + assert.Equal(t, 321, in.ID) + } + + tests := []struct { + name string + hooks func(count *int) []nethttp.UseCaseHook + wantCode int + wantBody string + wantHookCount int + }{ + { + name: "no error", + hooks: func(hookCalls *int) []nethttp.UseCaseHook { + return []nethttp.UseCaseHook{ + func(ctx context.Context, input, output any, beforeInteract bool) error { + assertInput(input) + *hookCalls++ + + return nil + }, + } + }, + wantCode: 204, + wantBody: "", + wantHookCount: 2, + }, + { + name: "beforehook error", + hooks: func(hookCalls *int) []nethttp.UseCaseHook { + return []nethttp.UseCaseHook{ + func(ctx context.Context, input, output any, beforeInteract bool) error { + if beforeInteract { + assertInput(input) + *hookCalls++ + } + + return io.EOF + }, + } + }, + wantCode: 500, + wantBody: `{"error":"EOF"}` + "\n", + wantHookCount: 1, + }, + { + name: "afterhook error", + hooks: func(hookCalls *int) []nethttp.UseCaseHook { + return []nethttp.UseCaseHook{ + func(ctx context.Context, input, output any, beforeInteract bool) error { + *hookCalls++ + if beforeInteract { + assertInput(input) + return nil + } + + return io.EOF + }, + } + }, + wantCode: 500, + wantBody: `{"error":"EOF"}` + "\n", + wantHookCount: 2, + }, + { + name: "multiple hook error", + hooks: func(hookCalls *int) []nethttp.UseCaseHook { + return []nethttp.UseCaseHook{ + func(ctx context.Context, input, output any, beforeInteract bool) error { + *hookCalls++ + return nil + }, + func(ctx context.Context, input, output any, beforeInteract bool) error { + *hookCalls++ + if beforeInteract { + assertInput(input) + return nil + } + + return io.EOF + }, + } + }, + wantCode: 500, + wantBody: `{"error":"EOF"}` + "\n", + wantHookCount: 4, + }, + } + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + // setup + u := &struct { + usecase.Interactor + usecase.WithInput + usecase.OutputWithNoContent + }{} + u.Input = new(inputWithQuery) + u.Interactor = usecase.Interact(func(ctx context.Context, input, output any) error { + assertInput(input) + return nil + }) + + uh := nethttp.NewHandler(u) + hookCalls := 0 + uh.SetUseCaseInteractorHooks(tt.hooks(&hookCalls)...) + h := nethttp.WrapHandler(uh, + request.DecoderMiddleware(request.NewDecoderFactory()), + nethttp.HandlerWithRouteMiddleware(http.MethodGet, "/test"), + response.EncoderMiddleware, + ) + + req, err := http.NewRequest(http.MethodGet, "/test?id=321", nil) + require.NoError(t, err) + + rw := httptest.NewRecorder() + h.ServeHTTP(rw, req) + + // assertions + assert.Equal(t, tt.wantCode, rw.Code) + assert.Equal(t, tt.wantBody, rw.Body.String()) + assert.Equal(t, tt.wantHookCount, hookCalls) + }) + } +} diff --git a/nethttp/openapi.go b/nethttp/openapi.go index db9651a..4202e06 100644 --- a/nethttp/openapi.go +++ b/nethttp/openapi.go @@ -129,7 +129,7 @@ func AnnotateOpenAPI( } // SecurityResponse is a security middleware option to customize response structure and status. -func SecurityResponse(structure interface{}, httpStatus int) func(config *MiddlewareConfig) { +func SecurityResponse(structure any, httpStatus int) func(config *MiddlewareConfig) { return func(config *MiddlewareConfig) { config.ResponseStructure = structure config.ResponseStatus = httpStatus @@ -139,7 +139,7 @@ func SecurityResponse(structure interface{}, httpStatus int) func(config *Middle // MiddlewareConfig defines security middleware options. type MiddlewareConfig struct { // ResponseStructure declares structure that is used for unauthorized message, default rest.ErrResponse{}. - ResponseStructure interface{} + ResponseStructure any // ResponseStatus declares HTTP status code that is used for unauthorized message, default http.StatusUnauthorized. ResponseStatus int diff --git a/nethttp/openapi_test.go b/nethttp/openapi_test.go index 4055ea5..84b812c 100644 --- a/nethttp/openapi_test.go +++ b/nethttp/openapi_test.go @@ -26,7 +26,7 @@ func TestOpenAPIMiddleware(t *testing.T) { Value string `json:"val"` Header int }) - u.Interactor = usecase.Interact(func(ctx context.Context, input, output interface{}) error { + u.Interactor = usecase.Interact(func(ctx context.Context, input, output any) error { in, ok := input.(*Input) assert.True(t, ok) assert.Equal(t, 123, in.ID) diff --git a/nethttp/options.go b/nethttp/options.go index f2fcf2c..7727262 100644 --- a/nethttp/options.go +++ b/nethttp/options.go @@ -50,7 +50,7 @@ func SuccessStatus(status int) func(h *Handler) { // RequestMapping creates rest.RequestMapping from struct tags. // // This can be used to decouple mapping from usecase input with additional struct. -func RequestMapping(v interface{}) func(h *Handler) { +func RequestMapping(v any) func(h *Handler) { return func(h *Handler) { m := make(rest.RequestMapping) @@ -81,7 +81,7 @@ func RequestMapping(v interface{}) func(h *Handler) { // ResponseHeaderMapping creates headers mapping from struct tags. // // This can be used to decouple mapping from usecase input with additional struct. -func ResponseHeaderMapping(v interface{}) func(h *Handler) { +func ResponseHeaderMapping(v any) func(h *Handler) { return func(h *Handler) { if mm, ok := v.(map[string]string); ok { h.RespHeaderMapping = mm diff --git a/nethttp/usecase.go b/nethttp/usecase.go index e3d1433..5f33c8b 100644 --- a/nethttp/usecase.go +++ b/nethttp/usecase.go @@ -21,7 +21,7 @@ func UseCaseMiddlewares(mw ...usecase.Middleware) func(http.Handler) http.Handle u := uh.UseCase() fu := usecase.Wrap(u, usecase.MiddlewareFunc(func(next usecase.Interactor) usecase.Interactor { - return usecase.Interact(func(ctx context.Context, input, output interface{}) error { + return usecase.Interact(func(ctx context.Context, input, output any) error { return ctx.Value(decodeErrCtxKey{}).(error) }) })) diff --git a/nethttp/wrap.go b/nethttp/wrap.go index 9772260..6e3dd98 100644 --- a/nethttp/wrap.go +++ b/nethttp/wrap.go @@ -36,7 +36,7 @@ func WrapHandler(h http.Handler, mw ...func(http.Handler) http.Handler) http.Han // // HandlerAs will panic if target is not a non-nil pointer to either a type that implements // http.Handler, or to any interface type. -func HandlerAs(handler http.Handler, target interface{}) bool { +func HandlerAs(handler http.Handler, target any) bool { if target == nil { panic("target cannot be nil") } diff --git a/openapi/collector.go b/openapi/collector.go index 6b0574e..8c35fbb 100644 --- a/openapi/collector.go +++ b/openapi/collector.go @@ -249,7 +249,7 @@ func (c *Collector) processUseCase(op *openapi3.Operation, u usecase.Interactor, func (c *Collector) processExpectedErrors(op *openapi3.Operation, u usecase.Interactor, h rest.HandlerTrait) error { var ( - errsByCode = map[int][]interface{}{} + errsByCode = map[int][]any{} statusCodes []int hasExpectedErrors usecase.HasExpectedErrors ) @@ -260,7 +260,7 @@ func (c *Collector) processExpectedErrors(op *openapi3.Operation, u usecase.Inte for _, e := range hasExpectedErrors.ExpectedErrors() { var ( - errResp interface{} + errResp any statusCode int ) @@ -362,7 +362,7 @@ func (c *Collector) provideParametersJSONSchemas(op openapi3.Operation, validato // ProvideRequestJSONSchemas provides JSON Schemas for request structure. func (c *Collector) ProvideRequestJSONSchemas( method string, - input interface{}, + input any, mapping rest.RequestMapping, validator rest.JSONSchemaValidator, ) error { @@ -490,7 +490,7 @@ func (c *Collector) provideHeaderSchemas(resp *openapi3.Response, validator rest func (c *Collector) ProvideResponseJSONSchemas( statusCode int, contentType string, - output interface{}, + output any, headerMapping map[string]string, validator rest.JSONSchemaValidator, ) error { diff --git a/openapi/collector_test.go b/openapi/collector_test.go index 7d5ff3b..f79d3f4 100644 --- a/openapi/collector_test.go +++ b/openapi/collector_test.go @@ -25,13 +25,13 @@ import ( var _ rest.JSONSchemaValidator = validatorMock{} type validatorMock struct { - ValidateDataFunc func(in rest.ParamIn, namedData map[string]interface{}) error + ValidateDataFunc func(in rest.ParamIn, namedData map[string]any) error ValidateJSONBodyFunc func(jsonBody []byte) error HasConstraintsFunc func(in rest.ParamIn) bool AddSchemaFunc func(in rest.ParamIn, name string, schemaData []byte, required bool) error } -func (v validatorMock) ValidateData(in rest.ParamIn, namedData map[string]interface{}) error { +func (v validatorMock) ValidateData(in rest.ParamIn, namedData map[string]any) error { return v.ValidateDataFunc(in, namedData) } @@ -250,7 +250,7 @@ func TestCollector_Collect_CombineErrors(t *testing.T) { u.SetExpectedErrors(status.InvalidArgument, anotherErr{}, status.FailedPrecondition, status.AlreadyExists) h := rest.HandlerTrait{} - h.MakeErrResp = func(ctx context.Context, err error) (int, interface{}) { + h.MakeErrResp = func(ctx context.Context, err error) (int, any) { code, er := rest.Err(err) var ae anotherErr diff --git a/request.go b/request.go index da6de43..9602e94 100644 --- a/request.go +++ b/request.go @@ -45,8 +45,8 @@ func (re RequestErrors) Error() string { } // Fields returns request errors by field location and name. -func (re RequestErrors) Fields() map[string]interface{} { - res := make(map[string]interface{}, len(re)) +func (re RequestErrors) Fields() map[string]any { + res := make(map[string]any, len(re)) for k, v := range re { res[k] = v diff --git a/request/decoder.go b/request/decoder.go index 1c3cfa0..7acb69a 100644 --- a/request/decoder.go +++ b/request/decoder.go @@ -19,11 +19,11 @@ type ( } decoderFunc func(r *http.Request) (url.Values, error) - valueDecoderFunc func(r *http.Request, v interface{}, validator rest.Validator) error + valueDecoderFunc func(r *http.Request, v any, validator rest.Validator) error ) -func decodeValidate(d *form.Decoder, v interface{}, p url.Values, in rest.ParamIn, val rest.Validator) error { - goValues := make(map[string]interface{}, len(p)) +func decodeValidate(d *form.Decoder, v any, p url.Values, in rest.ParamIn, val rest.Validator) error { + goValues := make(map[string]any, len(p)) err := d.Decode(v, p, goValues) if err != nil { @@ -50,7 +50,7 @@ func decodeValidate(d *form.Decoder, v interface{}, p url.Values, in rest.ParamI } func makeDecoder(in rest.ParamIn, formDecoder *form.Decoder, decoderFunc decoderFunc) valueDecoderFunc { - return func(r *http.Request, v interface{}, validator rest.Validator) error { + return func(r *http.Request, v any, validator rest.Validator) error { values, err := decoderFunc(r) if err != nil { return err @@ -73,7 +73,7 @@ type decoder struct { var _ nethttp.RequestDecoder = &decoder{} // Decode populates and validates input with data from http request. -func (d *decoder) Decode(r *http.Request, input interface{}, validator rest.Validator) error { +func (d *decoder) Decode(r *http.Request, input any, validator rest.Validator) error { if i, ok := input.(Loader); ok { return i.LoadFromHTTPRequest(r) } diff --git a/request/factory.go b/request/factory.go index ca5d6bc..e9d3903 100644 --- a/request/factory.go +++ b/request/factory.go @@ -36,7 +36,7 @@ type DecoderFactory struct { // JSONReader allows custom JSON decoder for request body. // If not set encoding/json.Decoder is used. - JSONReader func(rd io.Reader, v interface{}) error + JSONReader func(rd io.Reader, v any) error formDecoders map[rest.ParamIn]*form.Decoder decoderFunctions map[rest.ParamIn]decoderFunc @@ -45,7 +45,7 @@ type DecoderFactory struct { } type customDecoder struct { - types []interface{} + types []any fn form.DecodeFunc } @@ -92,7 +92,7 @@ func (df *DecoderFactory) SetDecoderFunc(tagName rest.ParamIn, d func(r *http.Re // CustomMapping can be nil, otherwise it is used instead of field tags to match decoded fields with struct. func (df *DecoderFactory) MakeDecoder( method string, - input interface{}, + input any, customMapping rest.RequestMapping, ) nethttp.RequestDecoder { m := decoder{ @@ -152,7 +152,7 @@ func (df *DecoderFactory) MakeDecoder( return &m } -func (df *DecoderFactory) prepareCustomMapping(input interface{}, customMapping rest.RequestMapping) rest.RequestMapping { +func (df *DecoderFactory) prepareCustomMapping(input any, customMapping rest.RequestMapping) rest.RequestMapping { // Copy custom mapping to avoid mutability issues on original map. cm := make(rest.RequestMapping, len(customMapping)) for k, v := range customMapping { @@ -200,7 +200,7 @@ func (df *DecoderFactory) prepareCustomMapping(input interface{}, customMapping } // jsonParams configures custom decoding for parameters with JSON struct values. -func (df *DecoderFactory) jsonParams(formDecoder *form.Decoder, in rest.ParamIn, input interface{}) { +func (df *DecoderFactory) jsonParams(formDecoder *form.Decoder, in rest.ParamIn, input any) { // Check fields for struct values with json tags. E.g. query parameter with json value. refl.WalkTaggedFields(reflect.ValueOf(input), func(v reflect.Value, sf reflect.StructField, tag string) { // Skip unexported fields. @@ -213,7 +213,7 @@ func (df *DecoderFactory) jsonParams(formDecoder *form.Decoder, in rest.ParamIn, if refl.HasTaggedFields(fieldVal, jsonTag) { // If value is a struct with `json` tags, custom decoder unmarshals json // from a string value into a struct. - formDecoder.RegisterFunc(func(s string) (interface{}, error) { + formDecoder.RegisterFunc(func(s string) (any, error) { var err error f := reflect.New(sf.Type) if df.JSONReader != nil { @@ -232,7 +232,7 @@ func (df *DecoderFactory) jsonParams(formDecoder *form.Decoder, in rest.ParamIn, }, string(in)) } -func (df *DecoderFactory) makeDefaultDecoder(input interface{}, m *decoder) { +func (df *DecoderFactory) makeDefaultDecoder(input any, m *decoder) { defaults := url.Values{} refl.WalkTaggedFields(reflect.ValueOf(input), func(v reflect.Value, sf reflect.StructField, tag string) { @@ -241,7 +241,7 @@ func (df *DecoderFactory) makeDefaultDecoder(input interface{}, m *decoder) { dec := df.defaultValDecoder - m.decoders = append(m.decoders, func(r *http.Request, v interface{}, validator rest.Validator) error { + m.decoders = append(m.decoders, func(r *http.Request, v any, validator rest.Validator) error { return dec.Decode(v, defaults) }) m.in = append(m.in, defaultTag) @@ -277,7 +277,7 @@ func (df *DecoderFactory) makeCustomMappingDecoder(customMapping rest.RequestMap } // RegisterFunc adds custom type handling. -func (df *DecoderFactory) RegisterFunc(fn form.DecodeFunc, types ...interface{}) { +func (df *DecoderFactory) RegisterFunc(fn form.DecodeFunc, types ...any) { for _, fd := range df.formDecoders { fd.RegisterFunc(fn, types...) } diff --git a/request/file.go b/request/file.go index 0bd2e38..f6a1134 100644 --- a/request/file.go +++ b/request/file.go @@ -17,7 +17,7 @@ var ( multipartFileHeadersType = reflect.TypeOf(([]*multipart.FileHeader)(nil)) ) -func decodeFiles(r *http.Request, input interface{}, _ rest.Validator) error { +func decodeFiles(r *http.Request, input any, _ rest.Validator) error { v := reflect.ValueOf(input) return decodeFilesInStruct(r, v) diff --git a/request/file_test.go b/request/file_test.go index 10c7cfb..86142b9 100644 --- a/request/file_test.go +++ b/request/file_test.go @@ -3,7 +3,7 @@ package request_test import ( "bytes" "context" - "io/ioutil" + "io" "mime/multipart" "net/http" "net/http/httptest" @@ -60,14 +60,14 @@ func TestMapper_Decode_fileUploadTag(t *testing.T) { }{} u.Input = new(fileReqTest) - u.Interactor = usecase.Interact(func(ctx context.Context, input, output interface{}) error { + u.Interactor = usecase.Interact(func(ctx context.Context, input, output any) error { in, ok := input.(*fileReqTest) assert.True(t, ok) assert.NotNil(t, in.Upload) assert.NotNil(t, in.UploadHeader) assert.Equal(t, "my.csv", in.UploadHeader.Filename) assert.Equal(t, int64(6), in.UploadHeader.Size) - content, err := ioutil.ReadAll(in.Upload) + content, err := io.ReadAll(in.Upload) assert.NoError(t, err) assert.NoError(t, in.Upload.Close()) assert.Equal(t, "Hello!", string(content)) @@ -79,12 +79,12 @@ func TestMapper_Decode_fileUploadTag(t *testing.T) { assert.Equal(t, "my2.csv", in.UploadsHeaders[1].Filename) assert.Equal(t, int64(7), in.UploadsHeaders[1].Size) - content, err = ioutil.ReadAll(in.Uploads[0]) + content, err = io.ReadAll(in.Uploads[0]) assert.NoError(t, err) assert.NoError(t, in.Uploads[0].Close()) assert.Equal(t, "Hello1!", string(content)) - content, err = ioutil.ReadAll(in.Uploads[1]) + content, err = io.ReadAll(in.Uploads[1]) assert.NoError(t, err) assert.NoError(t, in.Uploads[1].Close()) assert.Equal(t, "Hello2!", string(content)) diff --git a/request/jsonbody.go b/request/jsonbody.go index cf44c11..8a679d0 100644 --- a/request/jsonbody.go +++ b/request/jsonbody.go @@ -12,19 +12,19 @@ import ( ) var bufPool = sync.Pool{ - New: func() interface{} { + New: func() any { return bytes.NewBuffer(nil) }, } -func readJSON(rd io.Reader, v interface{}) error { +func readJSON(rd io.Reader, v any) error { d := json.NewDecoder(rd) return d.Decode(v) } -func decodeJSONBody(readJSON func(rd io.Reader, v interface{}) error, tolerateFormData bool) valueDecoderFunc { - return func(r *http.Request, input interface{}, validator rest.Validator) error { +func decodeJSONBody(readJSON func(rd io.Reader, v any) error, tolerateFormData bool) valueDecoderFunc { + return func(r *http.Request, input any, validator rest.Validator) error { if r.ContentLength == 0 { return ErrMissingRequestBody } diff --git a/request/jsonbody_test.go b/request/jsonbody_test.go index c8f7aab..d528a03 100644 --- a/request/jsonbody_test.go +++ b/request/jsonbody_test.go @@ -30,7 +30,7 @@ func Test_decodeJSONBody(t *testing.T) { assert.Equal(t, "248df4b7-aa70-47b8-a036-33ac447e668d", i.CustomerID) assert.Equal(t, "withdraw", i.Type) - vl := rest.ValidatorFunc(func(in rest.ParamIn, namedData map[string]interface{}) error { + vl := rest.ValidatorFunc(func(in rest.ParamIn, namedData map[string]any) error { return nil }) @@ -90,7 +90,7 @@ func Test_decodeJSONBody_validateFailed(t *testing.T) { var i []int - vl := rest.ValidatorFunc(func(in rest.ParamIn, namedData map[string]interface{}) error { + vl := rest.ValidatorFunc(func(in rest.ParamIn, namedData map[string]any) error { return errors.New("failed") }) diff --git a/request/middleware.go b/request/middleware.go index 5c82407..0f801bd 100644 --- a/request/middleware.go +++ b/request/middleware.go @@ -91,14 +91,14 @@ func ValidatorMiddleware(factory rest.RequestValidatorFactory) func(http.Handler var _ nethttp.RequestDecoder = DecoderFunc(nil) // DecoderFunc implements RequestDecoder with a func. -type DecoderFunc func(r *http.Request, input interface{}, validator rest.Validator) error +type DecoderFunc func(r *http.Request, input any, validator rest.Validator) error // Decode implements RequestDecoder. -func (df DecoderFunc) Decode(r *http.Request, input interface{}, validator rest.Validator) error { +func (df DecoderFunc) Decode(r *http.Request, input any, validator rest.Validator) error { return df(r, input, validator) } // DecoderMaker creates request decoder for particular structured Go input value. type DecoderMaker interface { - MakeDecoder(method string, input interface{}, customMapping rest.RequestMapping) nethttp.RequestDecoder + MakeDecoder(method string, input any, customMapping rest.RequestMapping) nethttp.RequestDecoder } diff --git a/request/reflect.go b/request/reflect.go index 428dc9d..3a866a4 100644 --- a/request/reflect.go +++ b/request/reflect.go @@ -7,7 +7,7 @@ import ( ) // HasFileFields checks if the structure has fields to receive uploaded files. -func hasFileFields(i interface{}, tagname string) bool { +func hasFileFields(i any, tagname string) bool { found := false refl.WalkTaggedFields(reflect.ValueOf(i), func(v reflect.Value, sf reflect.StructField, tag string) { diff --git a/request_test.go b/request_test.go index bc7e9e8..d960664 100644 --- a/request_test.go +++ b/request_test.go @@ -13,5 +13,5 @@ func TestRequestErrors_Error(t *testing.T) { } assert.EqualError(t, err, "bad request") - assert.Equal(t, map[string]interface{}{"foo": []string{"bar"}}, err.Fields()) + assert.Equal(t, map[string]any{"foo": []string{"bar"}}, err.Fields()) } diff --git a/response/encoder.go b/response/encoder.go index ecc32fd..85789a3 100644 --- a/response/encoder.go +++ b/response/encoder.go @@ -18,7 +18,7 @@ import ( // Encoder prepares and writes http response. type Encoder struct { - JSONWriter func(v interface{}) + JSONWriter func(v any) outputBufferType reflect.Type outputHeadersEncoder *form.Encoder @@ -32,7 +32,7 @@ type noContent interface { } // addressable makes a pointer from a non-pointer values. -func addressable(output interface{}) interface{} { +func addressable(output any) any { if reflect.ValueOf(output).Kind() != reflect.Ptr { o := reflect.New(reflect.TypeOf(output)) o.Elem().Set(reflect.ValueOf(output)) @@ -43,7 +43,7 @@ func addressable(output interface{}) interface{} { return output } -func (h *Encoder) setupHeadersEncoder(output interface{}, ht *rest.HandlerTrait) { +func (h *Encoder) setupHeadersEncoder(output any, ht *rest.HandlerTrait) { // Enable dynamic headers check in interface mode. if h.unwrapInterface = reflect.ValueOf(output).Elem().Kind() == reflect.Interface; h.unwrapInterface { enc := form.NewEncoder() @@ -78,7 +78,7 @@ func (h *Encoder) setupHeadersEncoder(output interface{}, ht *rest.HandlerTrait) } // SetupOutput configures encoder with and instance of use case output. -func (h *Encoder) SetupOutput(output interface{}, ht *rest.HandlerTrait) { +func (h *Encoder) SetupOutput(output any, ht *rest.HandlerTrait) { h.outputBufferType = reflect.TypeOf(output) h.outputHeadersEncoder = nil h.skipRendering = true @@ -121,7 +121,7 @@ type jsonEncoder struct { } var jsonEncoderPool = sync.Pool{ - New: func() interface{} { + New: func() any { buf := bytes.NewBuffer(nil) enc := json.NewEncoder(buf) enc.SetEscapeHTML(false) @@ -136,7 +136,7 @@ var jsonEncoderPool = sync.Pool{ func (h *Encoder) writeJSONResponse( w http.ResponseWriter, r *http.Request, - v interface{}, + v any, ht rest.HandlerTrait, ) { if ht.SuccessContentType == "" { @@ -195,7 +195,7 @@ func (h *Encoder) writeJSONResponse( } // WriteErrResponse encodes and writes error to response. -func (h *Encoder) WriteErrResponse(w http.ResponseWriter, r *http.Request, statusCode int, response interface{}) { +func (h *Encoder) WriteErrResponse(w http.ResponseWriter, r *http.Request, statusCode int, response any) { contentType := "application/json; charset=utf-8" e := jsonEncoderPool.Get().(*jsonEncoder) //nolint:errcheck @@ -230,7 +230,7 @@ func (h *Encoder) WriteErrResponse(w http.ResponseWriter, r *http.Request, statu func (h *Encoder) WriteSuccessfulResponse( w http.ResponseWriter, r *http.Request, - output interface{}, + output any, ht rest.HandlerTrait, ) { if h.unwrapInterface { @@ -273,10 +273,10 @@ func (h *Encoder) WriteSuccessfulResponse( h.writeJSONResponse(w, r, output, ht) } -func (h *Encoder) whiteHeader(w http.ResponseWriter, r *http.Request, output interface{}, ht rest.HandlerTrait) bool { - var headerValues map[string]interface{} +func (h *Encoder) whiteHeader(w http.ResponseWriter, r *http.Request, output any, ht rest.HandlerTrait) bool { + var headerValues map[string]any if ht.RespValidator != nil { - headerValues = make(map[string]interface{}) + headerValues = make(map[string]any) } headers, err := h.outputHeadersEncoder.Encode(output, headerValues) @@ -307,7 +307,7 @@ func (h *Encoder) whiteHeader(w http.ResponseWriter, r *http.Request, output int } // MakeOutput instantiates a value for use case output port. -func (h *Encoder) MakeOutput(w http.ResponseWriter, ht rest.HandlerTrait) interface{} { +func (h *Encoder) MakeOutput(w http.ResponseWriter, ht rest.HandlerTrait) any { if h.outputBufferType == nil { return nil } @@ -337,7 +337,7 @@ type writerWithHeaders struct { responseWriter *Encoder trait rest.HandlerTrait - output interface{} + output any headersSet bool } diff --git a/response/gzip/middleware_test.go b/response/gzip/middleware_test.go index ffd99d2..c3fcaca 100644 --- a/response/gzip/middleware_test.go +++ b/response/gzip/middleware_test.go @@ -3,7 +3,7 @@ package gzip_test import ( "bytes" gz "compress/gzip" - "io/ioutil" + "io" "net/http" "net/http/httptest" "strings" @@ -242,7 +242,7 @@ func gzipDecode(t *testing.T, data []byte) []byte { r, err := gz.NewReader(b) require.NoError(t, err) - j, err := ioutil.ReadAll(r) + j, err := io.ReadAll(r) require.NoError(t, err) require.NoError(t, r.Close()) diff --git a/response/middleware_test.go b/response/middleware_test.go index bb68ded..fbb38b2 100644 --- a/response/middleware_test.go +++ b/response/middleware_test.go @@ -25,7 +25,7 @@ func TestEncoderMiddleware(t *testing.T) { } u.Output = new(outputPort) - u.Interactor = usecase.Interact(func(ctx context.Context, input, output interface{}) error { + u.Interactor = usecase.Interact(func(ctx context.Context, input, output any) error { output.(*outputPort).Name = "Jane" output.(*outputPort).Items = []string{"one", "two", "three"} diff --git a/response/validator_test.go b/response/validator_test.go index 0fdf48d..bc59692 100644 --- a/response/validator_test.go +++ b/response/validator_test.go @@ -32,7 +32,7 @@ func TestValidatorMiddleware(t *testing.T) { } u.Output = new(outputPort) - u.Interactor = usecase.Interact(func(ctx context.Context, input, output interface{}) error { + u.Interactor = usecase.Interact(func(ctx context.Context, input, output any) error { out, ok := output.(*outputPort) require.True(t, ok) diff --git a/resttest/client_test.go b/resttest/client_test.go index 1bc6443..add9c83 100644 --- a/resttest/client_test.go +++ b/resttest/client_test.go @@ -1,7 +1,7 @@ package resttest_test import ( - "io/ioutil" + "io" "net/http" "net/http/httptest" "sync/atomic" @@ -16,7 +16,7 @@ func TestNewClient(t *testing.T) { cnt := int64(0) srv := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { assert.Equal(t, "/foo?q=1", r.URL.String()) - b, err := ioutil.ReadAll(r.Body) + b, err := io.ReadAll(r.Body) assert.NoError(t, err) assert.Equal(t, `{"foo":"bar"}`, string(b)) assert.Equal(t, "application/json", r.Header.Get("Content-Type")) diff --git a/resttest/server_test.go b/resttest/server_test.go index 3d4d6aa..e1ccb9a 100644 --- a/resttest/server_test.go +++ b/resttest/server_test.go @@ -3,7 +3,6 @@ package resttest_test import ( "bytes" "io" - "io/ioutil" "net/http" "strings" "sync" @@ -38,7 +37,7 @@ func assertRoundTrip(t *testing.T, baseURL string, expectation httpmock.Expectat resp, err := http.DefaultTransport.RoundTrip(req) require.NoError(t, err) - body, err := ioutil.ReadAll(resp.Body) + body, err := io.ReadAll(resp.Body) require.NoError(t, resp.Body.Close()) require.NoError(t, err) @@ -161,7 +160,7 @@ func TestServerMock_ServeHTTP_error(t *testing.T) { resp, err := http.DefaultTransport.RoundTrip(req) require.NoError(t, err) - respBody, err := ioutil.ReadAll(resp.Body) + respBody, err := io.ReadAll(resp.Body) require.NoError(t, resp.Body.Close()) require.NoError(t, err) @@ -176,7 +175,7 @@ func TestServerMock_ServeHTTP_error(t *testing.T) { resp, err = http.DefaultTransport.RoundTrip(req) require.NoError(t, err) - respBody, err = ioutil.ReadAll(resp.Body) + respBody, err = io.ReadAll(resp.Body) require.NoError(t, resp.Body.Close()) require.NoError(t, err) @@ -191,7 +190,7 @@ func TestServerMock_ServeHTTP_error(t *testing.T) { resp, err = http.DefaultTransport.RoundTrip(req) require.NoError(t, err) - respBody, err = ioutil.ReadAll(resp.Body) + respBody, err = io.ReadAll(resp.Body) require.NoError(t, resp.Body.Close()) require.NoError(t, err) @@ -206,7 +205,7 @@ func TestServerMock_ServeHTTP_error(t *testing.T) { resp, err = http.DefaultTransport.RoundTrip(req) require.NoError(t, err) - respBody, err = ioutil.ReadAll(resp.Body) + respBody, err = io.ReadAll(resp.Body) require.NoError(t, resp.Body.Close()) require.NoError(t, err) @@ -250,7 +249,7 @@ func TestServerMock_ServeHTTP_concurrency(t *testing.T) { resp, err := http.DefaultTransport.RoundTrip(req) require.NoError(t, err) - respBody, err := ioutil.ReadAll(resp.Body) + respBody, err := io.ReadAll(resp.Body) require.NoError(t, resp.Body.Close()) require.NoError(t, err) @@ -301,7 +300,7 @@ func TestServerMock_vars(t *testing.T) { resp, err := http.DefaultTransport.RoundTrip(req) require.NoError(t, err) - body, err := ioutil.ReadAll(resp.Body) + body, err := io.ReadAll(resp.Body) require.NoError(t, err) require.NoError(t, resp.Body.Close()) @@ -340,7 +339,7 @@ func TestServerMock_ExpectAsync(t *testing.T) { resp, err := http.DefaultTransport.RoundTrip(req) require.NoError(t, err) - body, err := ioutil.ReadAll(resp.Body) + body, err := io.ReadAll(resp.Body) require.NoError(t, err) require.NoError(t, resp.Body.Close()) @@ -357,7 +356,7 @@ func TestServerMock_ExpectAsync(t *testing.T) { resp, err := http.DefaultTransport.RoundTrip(req) require.NoError(t, err) - body, err := ioutil.ReadAll(resp.Body) + body, err := io.ReadAll(resp.Body) require.NoError(t, err) require.NoError(t, resp.Body.Close()) @@ -371,7 +370,7 @@ func TestServerMock_ExpectAsync(t *testing.T) { resp, err := http.DefaultTransport.RoundTrip(req) require.NoError(t, err) - body, err := ioutil.ReadAll(resp.Body) + body, err := io.ReadAll(resp.Body) require.NoError(t, err) require.NoError(t, resp.Body.Close()) diff --git a/trait.go b/trait.go index c197605..1e49066 100644 --- a/trait.go +++ b/trait.go @@ -22,7 +22,7 @@ type HandlerTrait struct { // MakeErrResp overrides error response builder instead of default Err, // returned values are HTTP status code and error structure to be marshaled. - MakeErrResp func(ctx context.Context, err error) (int, interface{}) + MakeErrResp func(ctx context.Context, err error) (int, any) // ReqMapping controls request decoding into use case input. // Optional, if not set field tags are used as mapping. @@ -51,7 +51,7 @@ func (h *HandlerTrait) RequestMapping() RequestMapping { } // OutputHasNoContent indicates if output does not seem to have any content body to render in response. -func OutputHasNoContent(output interface{}) bool { +func OutputHasNoContent(output any) bool { if output == nil { return true } diff --git a/validator.go b/validator.go index 713bfff..90aba46 100644 --- a/validator.go +++ b/validator.go @@ -5,7 +5,7 @@ import "encoding/json" // Validator validates a map of decoded data. type Validator interface { // ValidateData validates decoded request/response data and returns error in case of invalid data. - ValidateData(in ParamIn, namedData map[string]interface{}) error + ValidateData(in ParamIn, namedData map[string]any) error // ValidateJSONBody validates JSON encoded body and returns error in case of invalid data. ValidateJSONBody(jsonBody []byte) error @@ -15,10 +15,10 @@ type Validator interface { } // ValidatorFunc implements Validator with a func. -type ValidatorFunc func(in ParamIn, namedData map[string]interface{}) error +type ValidatorFunc func(in ParamIn, namedData map[string]any) error // ValidateData implements Validator. -func (v ValidatorFunc) ValidateData(in ParamIn, namedData map[string]interface{}) error { +func (v ValidatorFunc) ValidateData(in ParamIn, namedData map[string]any) error { return v(in, namedData) } @@ -29,14 +29,14 @@ func (v ValidatorFunc) HasConstraints(_ ParamIn) bool { // ValidateJSONBody implements Validator. func (v ValidatorFunc) ValidateJSONBody(body []byte) error { - return v(ParamInBody, map[string]interface{}{"body": json.RawMessage(body)}) + return v(ParamInBody, map[string]any{"body": json.RawMessage(body)}) } // RequestJSONSchemaProvider provides request JSON Schemas. type RequestJSONSchemaProvider interface { ProvideRequestJSONSchemas( method string, - input interface{}, + input any, mapping RequestMapping, validator JSONSchemaValidator, ) error @@ -47,7 +47,7 @@ type ResponseJSONSchemaProvider interface { ProvideResponseJSONSchemas( statusCode int, contentType string, - output interface{}, + output any, headerMapping map[string]string, validator JSONSchemaValidator, ) error @@ -63,7 +63,7 @@ type JSONSchemaValidator interface { // RequestValidatorFactory creates request validator for particular structured Go input value. type RequestValidatorFactory interface { - MakeRequestValidator(method string, input interface{}, mapping RequestMapping) Validator + MakeRequestValidator(method string, input any, mapping RequestMapping) Validator } // ResponseValidatorFactory creates response validator for particular structured Go output value. @@ -71,7 +71,7 @@ type ResponseValidatorFactory interface { MakeResponseValidator( statusCode int, contentType string, - output interface{}, + output any, headerMapping map[string]string, ) Validator } @@ -87,8 +87,8 @@ func (re ValidationErrors) Error() string { } // Fields returns request errors by field location and name. -func (re ValidationErrors) Fields() map[string]interface{} { - res := make(map[string]interface{}, len(re)) +func (re ValidationErrors) Fields() map[string]any { + res := make(map[string]any, len(re)) for k, v := range re { res[k] = v diff --git a/web/example_test.go b/web/example_test.go index b102801..83346a3 100644 --- a/web/example_test.go +++ b/web/example_test.go @@ -21,7 +21,7 @@ type album struct { } func postAlbums() usecase.Interactor { - u := usecase.NewIOI(new(album), new(album), func(ctx context.Context, input, output interface{}) error { + u := usecase.NewIOI(new(album), new(album), func(ctx context.Context, input, output any) error { log.Println("Creating album") return nil @@ -54,6 +54,7 @@ func ExampleDefaultService() { log.Println("Starting service at http://localhost:8080") + //#nosec:G114 if err := http.ListenAndServe("localhost:8080", service); err != nil { log.Fatal(err) } diff --git a/web/service_test.go b/web/service_test.go index d8ad725..7504c0f 100644 --- a/web/service_test.go +++ b/web/service_test.go @@ -3,9 +3,9 @@ package web_test import ( "context" "fmt" - "io/ioutil" "net/http" "net/http/httptest" + "os" "testing" "github.com/stretchr/testify/assert" @@ -22,7 +22,7 @@ type albumID struct { } func albumByID() usecase.Interactor { - u := usecase.NewIOI(new(albumID), new(album), func(ctx context.Context, input, output interface{}) error { + u := usecase.NewIOI(new(albumID), new(album), func(ctx context.Context, input, output any) error { return nil }) u.SetTags("Album") @@ -67,7 +67,7 @@ func TestDefaultService(t *testing.T) { assert.Equal(t, http.StatusOK, rw.Code) assertjson.EqualMarshal(t, rw.Body.Bytes(), service.OpenAPI) - expected, err := ioutil.ReadFile("_testdata/openapi.json") + expected, err := os.ReadFile("_testdata/openapi.json") require.NoError(t, err) assertjson.EqualMarshal(t, expected, service.OpenAPI)