From 31bd12c562da1dfab4915d9d9ca7adff653895ab Mon Sep 17 00:00:00 2001 From: Eser Ozvataf Date: Fri, 23 Aug 2024 00:51:29 +0300 Subject: [PATCH] refactor(httpfx): replace Response with ResponseResult for consistency - Updated all instances of `Response` to `ResponseResult` across the HTTP middleware and handler functions. - Disabled all `revive` linter rules in `.golangci.yaml`. - Consolidated Makefile and Go-related configurations in `.editorconfig`. - Replaced hand-written assertion checks with `assert` from `testify`. - Fixed `StringsTrim*` functions to handle empty inputs correctly. - Added utility function `SerializeSlogAttrs` in `lib/slog.go`. - Introduced structured error and result handling in `results` package. --- .editorconfig | 2 +- .golangci.yaml | 86 +++++++++---------- pkg/app/mod.go | 4 +- pkg/bliss/httpfx/context.go | 2 +- .../httpfx/middlewares/auth-middleware.go | 2 +- .../middlewares/auth-middleware_test.go | 15 ++-- .../middlewares/correlation-id-middleware.go | 2 +- .../httpfx/middlewares/cors-middleware.go | 2 +- .../middlewares/error-handler-middleware.go | 2 +- .../middlewares/resolve-address-middleware.go | 2 +- .../middlewares/response-time-middleware.go | 2 +- pkg/bliss/httpfx/modules/healthcheck/mod.go | 2 +- pkg/bliss/httpfx/modules/openapi/mod.go | 2 +- pkg/bliss/httpfx/primitives.go | 2 +- pkg/bliss/httpfx/results.go | 50 ++++++----- pkg/bliss/httpfx/uris/clean-path_test.go | 5 +- pkg/bliss/httpfx/uris/common-path_test.go | 5 +- pkg/bliss/httpfx/uris/difference-path_test.go | 15 ++-- pkg/bliss/lib/arrays_test.go | 7 +- pkg/bliss/lib/ids_test.go | 8 +- pkg/bliss/lib/paths_test.go | 15 +--- pkg/bliss/lib/slog.go | 22 +++++ pkg/bliss/lib/strings.go | 16 ++++ pkg/bliss/lib/strings_test.go | 21 ++--- pkg/bliss/logfx/fx-adapter.go | 2 +- pkg/bliss/logfx/fx-adapter_test.go | 8 +- pkg/bliss/logfx/replacer_test.go | 3 + pkg/bliss/results/error-result.go | 24 ++++++ pkg/bliss/results/result-def.go | 61 +++++++++++++ pkg/bliss/results/result-def_test.go | 31 +++++++ pkg/bliss/results/result-occurrence.go | 52 +++++++++++ pkg/bliss/results/result.go | 14 +++ 32 files changed, 339 insertions(+), 147 deletions(-) create mode 100644 pkg/bliss/lib/slog.go create mode 100644 pkg/bliss/results/error-result.go create mode 100644 pkg/bliss/results/result-def.go create mode 100644 pkg/bliss/results/result-def_test.go create mode 100644 pkg/bliss/results/result-occurrence.go create mode 100644 pkg/bliss/results/result.go diff --git a/.editorconfig b/.editorconfig index 3ed06f8..923f694 100644 --- a/.editorconfig +++ b/.editorconfig @@ -16,5 +16,5 @@ max_line_length = off [{*.md,*.mdx,*.mdoc}] trim_trailing_whitespace = false -[Makefile] +[{Makefile,go.mod,go.sum,*.go,.gitmodules}] indent_style = tab diff --git a/.golangci.yaml b/.golangci.yaml index 8412a05..af95baa 100644 --- a/.golangci.yaml +++ b/.golangci.yaml @@ -36,55 +36,55 @@ linters-settings: - github.com/getkin/kin-openapi/openapi3 - github.com/golang-jwt/jwt/v5 revive: - enable-all-rules: true + # enable-all-rules: true ignore-generated-header: true severity: warning rules: - name: var-naming disabled: true arguments: [] - - name: exported - severity: warning - - name: error-return - severity: warning - - name: error-naming - severity: warning - - name: if-return - severity: warning - - name: var-naming - severity: warning - - name: var-declaration - severity: warning - - name: receiver-naming - severity: warning - - name: errorf - severity: warning - - name: empty-block - severity: warning - - name: unused-parameter - severity: warning - - name: unreachable-code - severity: warning - - name: redefines-builtin-id - severity: warning - - name: superfluous-else - severity: warning - - name: unexported-return - severity: warning - - name: indent-error-flow - severity: warning - - name: blank-imports - severity: warning - - name: range - severity: warning - - name: time-naming - severity: warning - - name: context-as-argument - severity: warning - - name: context-keys-type - severity: warning - - name: indent-error-flow - severity: warning + # - name: exported + # severity: warning + # - name: error-return + # severity: warning + # - name: error-naming + # severity: warning + # - name: if-return + # severity: warning + # - name: var-naming + # severity: warning + # - name: var-declaration + # severity: warning + # - name: receiver-naming + # severity: warning + # - name: errorf + # severity: warning + # - name: empty-block + # severity: warning + # - name: unused-parameter + # severity: warning + # - name: unreachable-code + # severity: warning + # - name: redefines-builtin-id + # severity: warning + # - name: superfluous-else + # severity: warning + # - name: unexported-return + # severity: warning + # - name: indent-error-flow + # severity: warning + # - name: blank-imports + # severity: warning + # - name: range + # severity: warning + # - name: time-naming + # severity: warning + # - name: context-as-argument + # severity: warning + # - name: context-keys-type + # severity: warning + # - name: indent-error-flow + # severity: warning issues: fix: true diff --git a/pkg/app/mod.go b/pkg/app/mod.go index 272965d..c7528ae 100644 --- a/pkg/app/mod.go +++ b/pkg/app/mod.go @@ -50,7 +50,7 @@ func RegisterRoutes(routes httpfx.Router, appConfig *AppConfig) { routes.Use(middlewares.CorsMiddleware()) routes. - Route("GET /", func(ctx *httpfx.Context) httpfx.Response { + Route("GET /", func(ctx *httpfx.Context) httpfx.ResponseResult { message := fmt.Sprintf( "Hello %s (%s) from %s!", ctx.Request.Context().Value(middlewares.ClientAddr), @@ -65,7 +65,7 @@ func RegisterRoutes(routes httpfx.Router, appConfig *AppConfig) { HasResponse(http.StatusOK) routes. - Route("GET /protected", middlewares.AuthMiddleware(), func(ctx *httpfx.Context) httpfx.Response { + Route("GET /protected", middlewares.AuthMiddleware(), func(ctx *httpfx.Context) httpfx.ResponseResult { message := fmt.Sprintf("Hello from %s! this endpoint is protected!", appConfig.AppName) return ctx.Results.PlainText(message) diff --git a/pkg/bliss/httpfx/context.go b/pkg/bliss/httpfx/context.go index 66b6a20..b732161 100644 --- a/pkg/bliss/httpfx/context.go +++ b/pkg/bliss/httpfx/context.go @@ -23,7 +23,7 @@ type Context struct { // isAborted bool } -func (c *Context) Next() Response { +func (c *Context) Next() ResponseResult { c.index++ for c.index < len(c.handlers) { diff --git a/pkg/bliss/httpfx/middlewares/auth-middleware.go b/pkg/bliss/httpfx/middlewares/auth-middleware.go index b37b7a1..bebce58 100644 --- a/pkg/bliss/httpfx/middlewares/auth-middleware.go +++ b/pkg/bliss/httpfx/middlewares/auth-middleware.go @@ -17,7 +17,7 @@ const ( var ErrInvalidSigningMethod = errors.New("Invalid signing method") func AuthMiddleware() httpfx.Handler { - return func(ctx *httpfx.Context) httpfx.Response { + return func(ctx *httpfx.Context) httpfx.ResponseResult { tokenString, hasToken := getBearerToken(ctx) if !hasToken { diff --git a/pkg/bliss/httpfx/middlewares/auth-middleware_test.go b/pkg/bliss/httpfx/middlewares/auth-middleware_test.go index 03a40bc..63ae4ca 100644 --- a/pkg/bliss/httpfx/middlewares/auth-middleware_test.go +++ b/pkg/bliss/httpfx/middlewares/auth-middleware_test.go @@ -10,6 +10,7 @@ import ( "github.com/eser/go-service/pkg/bliss/httpfx" "github.com/eser/go-service/pkg/bliss/httpfx/middlewares" "github.com/golang-jwt/jwt/v5" + "github.com/stretchr/testify/assert" ) func createToken(secret string, exp time.Time) string { @@ -78,24 +79,18 @@ func TestAuthMiddleware(t *testing.T) { result := middleware(&httpCtx) if result.StatusCode != tt.expectedStatusCode { - t.Errorf("Expected status code %d, got %d", tt.expectedStatusCode, result.StatusCode) + assert.Equal(t, tt.expectedStatusCode, result.StatusCode) } if tt.expectedStatusCode == http.StatusOK || tt.expectedStatusCode == http.StatusNoContent { claims, claimsOk := httpCtx.Request.Context().Value(middlewares.AuthClaims).(jwt.MapClaims) - if !claimsOk { - t.Error("Claims are missing in context") - } + assert.True(t, claimsOk, "Claims are missing in context") - if claims["exp"] == nil { - t.Error("exp claim is missing") - } + assert.NotNil(t, claims["exp"], "exp claim is missing") if exp, ok := claims["exp"].(float64); ok { - if time.Unix(int64(exp), 0).Before(time.Now()) { - t.Error("exp claim is not valid") - } + assert.False(t, time.Unix(int64(exp), 0).Before(time.Now()), "exp claim is not valid") } } }) diff --git a/pkg/bliss/httpfx/middlewares/correlation-id-middleware.go b/pkg/bliss/httpfx/middlewares/correlation-id-middleware.go index d3bac53..6b2ee59 100644 --- a/pkg/bliss/httpfx/middlewares/correlation-id-middleware.go +++ b/pkg/bliss/httpfx/middlewares/correlation-id-middleware.go @@ -8,7 +8,7 @@ import ( const CorrelationIdHeader = "X-Correlation-Id" func CorrelationIdMiddleware() httpfx.Handler { - return func(ctx *httpfx.Context) httpfx.Response { + return func(ctx *httpfx.Context) httpfx.ResponseResult { // FIXME(@eser): no need to check if the header is specified correlationId := ctx.Request.Header.Get(CorrelationIdHeader) if correlationId == "" { diff --git a/pkg/bliss/httpfx/middlewares/cors-middleware.go b/pkg/bliss/httpfx/middlewares/cors-middleware.go index 49c54ea..dc3a926 100644 --- a/pkg/bliss/httpfx/middlewares/cors-middleware.go +++ b/pkg/bliss/httpfx/middlewares/cors-middleware.go @@ -7,7 +7,7 @@ import ( const AccessControlAllowOriginHeader = "Access-Control-Allow-Origin" func CorsMiddleware() httpfx.Handler { - return func(ctx *httpfx.Context) httpfx.Response { + return func(ctx *httpfx.Context) httpfx.ResponseResult { result := ctx.Next() ctx.ResponseWriter.Header().Set(AccessControlAllowOriginHeader, "*") diff --git a/pkg/bliss/httpfx/middlewares/error-handler-middleware.go b/pkg/bliss/httpfx/middlewares/error-handler-middleware.go index b83dd2d..fc11dba 100644 --- a/pkg/bliss/httpfx/middlewares/error-handler-middleware.go +++ b/pkg/bliss/httpfx/middlewares/error-handler-middleware.go @@ -3,7 +3,7 @@ package middlewares import "github.com/eser/go-service/pkg/bliss/httpfx" func ErrorHandlerMiddleware() httpfx.Handler { - return func(ctx *httpfx.Context) httpfx.Response { + return func(ctx *httpfx.Context) httpfx.ResponseResult { result := ctx.Next() return result diff --git a/pkg/bliss/httpfx/middlewares/resolve-address-middleware.go b/pkg/bliss/httpfx/middlewares/resolve-address-middleware.go index 3ceb75c..cc05cce 100644 --- a/pkg/bliss/httpfx/middlewares/resolve-address-middleware.go +++ b/pkg/bliss/httpfx/middlewares/resolve-address-middleware.go @@ -17,7 +17,7 @@ const ( ) func ResolveAddressMiddleware() httpfx.Handler { - return func(ctx *httpfx.Context) httpfx.Response { + return func(ctx *httpfx.Context) httpfx.ResponseResult { addr := GetClientAddrs(ctx.Request) newContext := context.WithValue( diff --git a/pkg/bliss/httpfx/middlewares/response-time-middleware.go b/pkg/bliss/httpfx/middlewares/response-time-middleware.go index a934e12..c244884 100644 --- a/pkg/bliss/httpfx/middlewares/response-time-middleware.go +++ b/pkg/bliss/httpfx/middlewares/response-time-middleware.go @@ -10,7 +10,7 @@ import ( const ResponseTimeHeader = "X-Request-Time" func ResponseTimeMiddleware() httpfx.Handler { - return func(ctx *httpfx.Context) httpfx.Response { + return func(ctx *httpfx.Context) httpfx.ResponseResult { startTime := time.Now() result := ctx.Next() diff --git a/pkg/bliss/httpfx/modules/healthcheck/mod.go b/pkg/bliss/httpfx/modules/healthcheck/mod.go index 9321aa4..93cc245 100644 --- a/pkg/bliss/httpfx/modules/healthcheck/mod.go +++ b/pkg/bliss/httpfx/modules/healthcheck/mod.go @@ -16,7 +16,7 @@ var Module = fx.Module( //nolint:gochecknoglobals func RegisterRoutes(routes httpfx.Router) { routes. - Route("GET /health-check", func(ctx *httpfx.Context) httpfx.Response { + Route("GET /health-check", func(ctx *httpfx.Context) httpfx.ResponseResult { return ctx.Results.Ok() }). HasSummary("Health Check"). diff --git a/pkg/bliss/httpfx/modules/openapi/mod.go b/pkg/bliss/httpfx/modules/openapi/mod.go index a109f06..6e00a81 100644 --- a/pkg/bliss/httpfx/modules/openapi/mod.go +++ b/pkg/bliss/httpfx/modules/openapi/mod.go @@ -19,7 +19,7 @@ var Module = fx.Module( //nolint:gochecknoglobals func RegisterRoutes(routes httpfx.Router) { routes. - Route("GET /openapi.json", func(ctx *httpfx.Context) httpfx.Response { + Route("GET /openapi.json", func(ctx *httpfx.Context) httpfx.ResponseResult { spec := &ApiIdentity{ name: "golang-service", version: "0.0.0", diff --git a/pkg/bliss/httpfx/primitives.go b/pkg/bliss/httpfx/primitives.go index a44fb33..805b206 100644 --- a/pkg/bliss/httpfx/primitives.go +++ b/pkg/bliss/httpfx/primitives.go @@ -1,7 +1,7 @@ package httpfx type ( - Handler func(*Context) Response + Handler func(*Context) ResponseResult HandlerChain []Handler Middleware func() Handler ) diff --git a/pkg/bliss/httpfx/results.go b/pkg/bliss/httpfx/results.go index 122ae6f..5d22de7 100644 --- a/pkg/bliss/httpfx/results.go +++ b/pkg/bliss/httpfx/results.go @@ -3,22 +3,26 @@ package httpfx import ( "encoding/json" "net/http" + + "github.com/eser/go-service/pkg/bliss/results" ) -type Response struct { +type ResponseResult struct { + results.Result + StatusCode int Body []byte RedirectToUri string } -func (r Response) WithStatusCode(statusCode int) Response { +func (r ResponseResult) WithStatusCode(statusCode int) ResponseResult { r.StatusCode = statusCode return r } -func (r Response) WithBody(body string) Response { +func (r ResponseResult) WithBody(body string) ResponseResult { r.Body = []byte(body) return r @@ -26,28 +30,28 @@ func (r Response) WithBody(body string) Response { type Results struct{} -func (r *Results) Ok() Response { - return Response{ //nolint:exhaustruct +func (r *Results) Ok() ResponseResult { + return ResponseResult{ //nolint:exhaustruct StatusCode: http.StatusNoContent, Body: []byte{}, } } -func (r *Results) Bytes(body []byte) Response { - return Response{ //nolint:exhaustruct +func (r *Results) Bytes(body []byte) ResponseResult { + return ResponseResult{ //nolint:exhaustruct StatusCode: http.StatusOK, Body: body, } } -func (r *Results) PlainText(body string) Response { - return Response{ //nolint:exhaustruct +func (r *Results) PlainText(body string) ResponseResult { + return ResponseResult{ //nolint:exhaustruct StatusCode: http.StatusOK, Body: []byte(body), } } -func (r *Results) Json(body any) Response { +func (r *Results) Json(body any) ResponseResult { encoded, err := json.Marshal(body) if err != nil { // TODO(@eser): Log error @@ -57,51 +61,51 @@ func (r *Results) Json(body any) Response { ) } - return Response{ //nolint:exhaustruct + return ResponseResult{ //nolint:exhaustruct StatusCode: http.StatusOK, Body: encoded, } } -func (r *Results) Redirect(uri string) Response { - return Response{ +func (r *Results) Redirect(uri string) ResponseResult { + return ResponseResult{ StatusCode: http.StatusTemporaryRedirect, Body: []byte{}, RedirectToUri: uri, } } -func (r *Results) NotFound() Response { - return Response{ //nolint:exhaustruct +func (r *Results) NotFound() ResponseResult { + return ResponseResult{ //nolint:exhaustruct StatusCode: http.StatusNotFound, Body: []byte("Not Found"), } } -func (r *Results) Unauthorized(body string) Response { - return Response{ //nolint:exhaustruct +func (r *Results) Unauthorized(body string) ResponseResult { + return ResponseResult{ //nolint:exhaustruct StatusCode: http.StatusUnauthorized, Body: []byte(body), } } -func (r *Results) BadRequest() Response { - return Response{ //nolint:exhaustruct +func (r *Results) BadRequest() ResponseResult { + return ResponseResult{ //nolint:exhaustruct StatusCode: http.StatusBadRequest, Body: []byte("Bad Request"), } } -func (r *Results) Error(statusCode int, message string) Response { - return Response{ //nolint:exhaustruct +func (r *Results) Error(statusCode int, message string) ResponseResult { + return ResponseResult{ //nolint:exhaustruct StatusCode: statusCode, Body: []byte(message), } } -func (r *Results) Abort() Response { +func (r *Results) Abort() ResponseResult { // TODO(@eser) implement this - return Response{ //nolint:exhaustruct + return ResponseResult{ //nolint:exhaustruct StatusCode: http.StatusNotImplemented, Body: []byte("Not Implemented"), } diff --git a/pkg/bliss/httpfx/uris/clean-path_test.go b/pkg/bliss/httpfx/uris/clean-path_test.go index 0a484e6..4d40aa1 100644 --- a/pkg/bliss/httpfx/uris/clean-path_test.go +++ b/pkg/bliss/httpfx/uris/clean-path_test.go @@ -4,6 +4,7 @@ import ( "testing" "github.com/eser/go-service/pkg/bliss/httpfx/uris" + "github.com/stretchr/testify/assert" ) func TestCleanPath(t *testing.T) { @@ -87,9 +88,7 @@ func TestCleanPath(t *testing.T) { result := uris.CleanPath(tt.input) - if result != tt.expected { - t.Errorf("CleanPath() = %v, want %v", result, tt.expected) - } + assert.Equal(t, result, tt.expected) }) } } diff --git a/pkg/bliss/httpfx/uris/common-path_test.go b/pkg/bliss/httpfx/uris/common-path_test.go index c6e242d..86fee16 100644 --- a/pkg/bliss/httpfx/uris/common-path_test.go +++ b/pkg/bliss/httpfx/uris/common-path_test.go @@ -4,6 +4,7 @@ import ( "testing" "github.com/eser/go-service/pkg/bliss/httpfx/uris" + "github.com/stretchr/testify/assert" ) func TestCommonPath(t *testing.T) { @@ -93,9 +94,7 @@ func TestCommonPath(t *testing.T) { result := uris.CommonPath(&tt.p1, &tt.p2) - if result != tt.expected { - t.Errorf("CommonPath() = %v, want %v", result, tt.expected) - } + assert.Equal(t, tt.expected, result) }) } } diff --git a/pkg/bliss/httpfx/uris/difference-path_test.go b/pkg/bliss/httpfx/uris/difference-path_test.go index 79537f3..91003df 100644 --- a/pkg/bliss/httpfx/uris/difference-path_test.go +++ b/pkg/bliss/httpfx/uris/difference-path_test.go @@ -4,6 +4,7 @@ import ( "testing" "github.com/eser/go-service/pkg/bliss/httpfx/uris" + "github.com/stretchr/testify/assert" ) func TestDifferencePath(t *testing.T) { @@ -106,17 +107,17 @@ func TestDifferencePath(t *testing.T) { t.Parallel() defer func() { - if r := recover(); r != nil { - if tt.want != "" { - t.Errorf("DifferencePath() panicked: %v, want %q", r, tt.want) - } + r := recover() + + if tt.want == "" { + assert.NotNil(t, r, "DifferencePath() did not panic") + } else { + assert.Nil(t, r, "DifferencePath() panicked: %v", r) } }() got := uris.DifferencePath(tt.p1, tt.p2) - if got != tt.want { - t.Errorf("DifferencePath() = %q, want %q", got, tt.want) - } + assert.Equal(t, tt.want, got) }) } } diff --git a/pkg/bliss/lib/arrays_test.go b/pkg/bliss/lib/arrays_test.go index 0dff4a0..e15b0fe 100644 --- a/pkg/bliss/lib/arrays_test.go +++ b/pkg/bliss/lib/arrays_test.go @@ -1,10 +1,10 @@ package lib_test import ( - "reflect" "testing" "github.com/eser/go-service/pkg/bliss/lib" + "github.com/stretchr/testify/assert" ) func TestArraysCopy(t *testing.T) { @@ -30,7 +30,6 @@ func TestArraysCopy(t *testing.T) { items: [][]int{{1, 2}, {3, 4, 5}, {6}}, want: []int{1, 2, 3, 4, 5, 6}, }, - // Add more test cases as needed } for _, tt := range tests { @@ -39,9 +38,7 @@ func TestArraysCopy(t *testing.T) { got := lib.ArraysCopy(tt.items...) - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("got %v, want %v", got, tt.want) - } + assert.ElementsMatch(t, got, tt.want) }) } } diff --git a/pkg/bliss/lib/ids_test.go b/pkg/bliss/lib/ids_test.go index 71b3c01..938ed91 100644 --- a/pkg/bliss/lib/ids_test.go +++ b/pkg/bliss/lib/ids_test.go @@ -4,6 +4,7 @@ import ( "testing" "github.com/eser/go-service/pkg/bliss/lib" + "github.com/stretchr/testify/assert" ) func TestIdsGenerateUnique(t *testing.T) { @@ -18,7 +19,6 @@ func TestIdsGenerateUnique(t *testing.T) { { name: "Test 2", }, - // Add more test cases as needed } for _, tt := range tests { @@ -27,11 +27,7 @@ func TestIdsGenerateUnique(t *testing.T) { got := lib.IdsGenerateUnique() - if len(got) != 26 { - t.Errorf("unexpected length of generated ID, got: %d, want: 26", len(got)) - } - - // Add more assertions as needed + assert.Len(t, got, 26) }) } } diff --git a/pkg/bliss/lib/paths_test.go b/pkg/bliss/lib/paths_test.go index e2ccbe9..c2a93d1 100644 --- a/pkg/bliss/lib/paths_test.go +++ b/pkg/bliss/lib/paths_test.go @@ -4,6 +4,7 @@ import ( "testing" "github.com/eser/go-service/pkg/bliss/lib" + "github.com/stretchr/testify/assert" ) func TestPathsSplit(t *testing.T) { @@ -52,17 +53,9 @@ func TestPathsSplit(t *testing.T) { gotDir, gotFile, gotExt := lib.PathsSplit(tt.filename) - if gotDir != tt.wantDir { - t.Errorf("gotDir = %s, wantDir = %s", gotDir, tt.wantDir) - } - - if gotFile != tt.wantFile { - t.Errorf("gotFile = %s, wantFile = %s", gotFile, tt.wantFile) - } - - if gotExt != tt.wantExt { - t.Errorf("gotExt = %s, wantExt = %s", gotExt, tt.wantExt) - } + assert.Equal(t, gotDir, tt.wantDir) + assert.Equal(t, gotFile, tt.wantFile) + assert.Equal(t, gotExt, tt.wantExt) }) } } diff --git a/pkg/bliss/lib/slog.go b/pkg/bliss/lib/slog.go new file mode 100644 index 0000000..b16812c --- /dev/null +++ b/pkg/bliss/lib/slog.go @@ -0,0 +1,22 @@ +package lib + +import ( + "log/slog" + "strings" +) + +func SerializeSlogAttrs(attrs ...slog.Attr) string { + length := len(attrs) + + if length == 0 { + return "" + } + + result := make([]string, len(attrs)) + + for _, attr := range attrs { + result = append(result, attr.String()) + } + + return strings.Join(result, ", ") +} diff --git a/pkg/bliss/lib/strings.go b/pkg/bliss/lib/strings.go index 51e2763..67fa52f 100644 --- a/pkg/bliss/lib/strings.go +++ b/pkg/bliss/lib/strings.go @@ -7,17 +7,33 @@ import ( ) func StringsTrimLeadingSpaceFromBytes(src []byte) []byte { + if len(src) == 0 { + return src + } + return bytes.TrimLeftFunc(src, unicode.IsSpace) } func StringsTrimTrailingSpaceFromBytes(src []byte) []byte { + if len(src) == 0 { + return src + } + return bytes.TrimRightFunc(src, unicode.IsSpace) } func StringsTrimLeadingSpace(src string) string { + if len(src) == 0 { + return src + } + return strings.TrimLeftFunc(src, unicode.IsSpace) } func StringsTrimTrailingSpace(src string) string { + if len(src) == 0 { + return src + } + return strings.TrimRightFunc(src, unicode.IsSpace) } diff --git a/pkg/bliss/lib/strings_test.go b/pkg/bliss/lib/strings_test.go index 9a0045e..f092bc8 100644 --- a/pkg/bliss/lib/strings_test.go +++ b/pkg/bliss/lib/strings_test.go @@ -4,6 +4,7 @@ import ( "testing" "github.com/eser/go-service/pkg/bliss/lib" + "github.com/stretchr/testify/assert" ) func TestStringsTrimLeadingSpaceFromBytes(t *testing.T) { @@ -29,7 +30,6 @@ func TestStringsTrimLeadingSpaceFromBytes(t *testing.T) { src: []byte(" Hello, World!"), want: []byte("Hello, World!"), }, - // Add more test cases as needed } for _, tt := range tests { @@ -38,9 +38,7 @@ func TestStringsTrimLeadingSpaceFromBytes(t *testing.T) { got := lib.StringsTrimLeadingSpaceFromBytes(tt.src) - if string(got) != string(tt.want) { - t.Errorf("got %s, want %s", got, tt.want) - } + assert.Equal(t, tt.want, got) }) } } @@ -68,7 +66,6 @@ func TestStringsTrimTrailingSpaceFromBytes(t *testing.T) { src: []byte("Hello, World! "), want: []byte("Hello, World!"), }, - // Add more test cases as needed } for _, tt := range tests { @@ -77,9 +74,7 @@ func TestStringsTrimTrailingSpaceFromBytes(t *testing.T) { got := lib.StringsTrimTrailingSpaceFromBytes(tt.src) - if string(got) != string(tt.want) { - t.Errorf("got %s, want %s", got, tt.want) - } + assert.Equal(t, tt.want, got) }) } } @@ -107,7 +102,6 @@ func TestStringsTrimLeadingSpace(t *testing.T) { src: " Hello, World!", want: "Hello, World!", }, - // Add more test cases as needed } for _, tt := range tests { @@ -116,9 +110,7 @@ func TestStringsTrimLeadingSpace(t *testing.T) { got := lib.StringsTrimLeadingSpace(tt.src) - if got != tt.want { - t.Errorf("got %s, want %s", got, tt.want) - } + assert.Equal(t, tt.want, got) }) } } @@ -146,7 +138,6 @@ func TestStringsTrimTrailingSpace(t *testing.T) { src: "Hello, World! ", want: "Hello, World!", }, - // Add more test cases as needed } for _, tt := range tests { @@ -155,9 +146,7 @@ func TestStringsTrimTrailingSpace(t *testing.T) { got := lib.StringsTrimTrailingSpace(tt.src) - if got != tt.want { - t.Errorf("got %s, want %s", got, tt.want) - } + assert.Equal(t, tt.want, got) }) } } diff --git a/pkg/bliss/logfx/fx-adapter.go b/pkg/bliss/logfx/fx-adapter.go index f9a0d38..b8aa67c 100644 --- a/pkg/bliss/logfx/fx-adapter.go +++ b/pkg/bliss/logfx/fx-adapter.go @@ -12,7 +12,7 @@ type ( } ) -func GetFxLogger(logger *slog.Logger) fxevent.Logger { //nolint:ireturn +func GetFxLogger(logger *slog.Logger) fxevent.Logger { return &FxLogger{logger} } diff --git a/pkg/bliss/logfx/fx-adapter_test.go b/pkg/bliss/logfx/fx-adapter_test.go index 6fc667c..7086dd9 100644 --- a/pkg/bliss/logfx/fx-adapter_test.go +++ b/pkg/bliss/logfx/fx-adapter_test.go @@ -158,14 +158,10 @@ func TestFxLogger_LogEvent(t *testing.T) { //nolint:paralleltest scanner := bufio.NewScanner(os.Stdin) for scanner.Scan() { - if scanner.Text() != tt.want { - t.Errorf("LogEvent() = %v, want %v", scanner.Text(), tt.want) - } + assert.Equal(t, tt.want, scanner.Text()) } - if scanner.Err() != nil { - t.Errorf("LogEvent() error = %v", scanner.Err()) - } + assert.NoError(t, scanner.Err()) }() fxLogger.LogEvent(tt.event) diff --git a/pkg/bliss/logfx/replacer_test.go b/pkg/bliss/logfx/replacer_test.go index 85d6bc4..92968d8 100644 --- a/pkg/bliss/logfx/replacer_test.go +++ b/pkg/bliss/logfx/replacer_test.go @@ -122,6 +122,7 @@ func TestTraceLines(t *testing.T) { stackGenerator := func() []uintptr { var pc [32]uintptr n := runtime.Callers(0, pc[:]) + return pc[:n] } @@ -129,11 +130,13 @@ func TestTraceLines(t *testing.T) { var pc [32]uintptr n := runtime.Callers(0, pc[:]) pc[0] = 0 + return pc[:n] } pwd := func() string { _, file, _, _ := runtime.Caller(0) + return file } diff --git a/pkg/bliss/results/error-result.go b/pkg/bliss/results/error-result.go new file mode 100644 index 0000000..cdd8282 --- /dev/null +++ b/pkg/bliss/results/error-result.go @@ -0,0 +1,24 @@ +package results + +// type ErrorResult struct { +// Result + +// Code string +// Message string +// } + +// func (e ErrorResult) Error() string { +// return fmt.Sprintf("[%s] %s", e.Code, e.Message) +// } + +// func NewErrorResult(code string, message string, logAttrs ...slog.Attr) *ErrorResult { +// return &ErrorResult{ +// Result: Result{ +// InnerError: nil, +// LogAttributes: logAttrs, +// }, + +// Code: code, +// Message: message, +// } +// } diff --git a/pkg/bliss/results/result-def.go b/pkg/bliss/results/result-def.go new file mode 100644 index 0000000..56b06ba --- /dev/null +++ b/pkg/bliss/results/result-def.go @@ -0,0 +1,61 @@ +package results + +import ( + "log/slog" +) + +type ResultDef struct { + Code string + Message string + + Attributes []slog.Attr +} + +func NewResultDef(code string, message string, logAttrs ...slog.Attr) *ResultDef { + return &ResultDef{ + Code: code, + Message: message, + + Attributes: logAttrs, + } +} + +func (r *ResultDef) New() ResultOccurrence { + return ResultOccurrence{ + Definition: r, + + InnerResult: nil, + Error: nil, + Attributes: []slog.Attr{}, + } +} + +func (r *ResultDef) NewWithError(err error) ResultOccurrence { + return ResultOccurrence{ + Definition: r, + + InnerResult: nil, + Error: err, + Attributes: []slog.Attr{}, + } +} + +func (r *ResultDef) Wrap(result Result) ResultOccurrence { + return ResultOccurrence{ + Definition: r, + + InnerResult: result, + Error: nil, + Attributes: []slog.Attr{}, + } +} + +func (r *ResultDef) WrapWithError(result Result, err error) ResultOccurrence { + return ResultOccurrence{ + Definition: r, + + InnerResult: result, + Error: err, + Attributes: []slog.Attr{}, + } +} diff --git a/pkg/bliss/results/result-def_test.go b/pkg/bliss/results/result-def_test.go new file mode 100644 index 0000000..0193a3b --- /dev/null +++ b/pkg/bliss/results/result-def_test.go @@ -0,0 +1,31 @@ +package results_test + +import ( + "errors" + "testing" + + "github.com/eser/go-service/pkg/bliss/results" + "github.com/stretchr/testify/assert" +) + +var ( + resultOk = results.NewResultDef("0001", "OK") //nolint:gochecknoglobals + resultErr = results.NewResultDef("0002", "Error") //nolint:gochecknoglobals +) + +func TestResultSimple(t *testing.T) { + t.Parallel() + + occurrenceOk := resultOk.New() + + assert.True(t, occurrenceOk.IsOk()) +} + +func TestResultError(t *testing.T) { + t.Parallel() + + err := errors.New("error") //nolint:err113 + occurrenceErr := resultErr.NewWithError(err) + + assert.False(t, occurrenceErr.IsOk()) +} diff --git a/pkg/bliss/results/result-occurrence.go b/pkg/bliss/results/result-occurrence.go new file mode 100644 index 0000000..6df934d --- /dev/null +++ b/pkg/bliss/results/result-occurrence.go @@ -0,0 +1,52 @@ +package results + +import ( + "fmt" + "log/slog" + + "github.com/eser/go-service/pkg/bliss/lib" +) + +type ResultOccurrence struct { + Definition *ResultDef + + InnerResult Result + Error error + Attributes []slog.Attr +} + +var _ Result = (*ResultOccurrence)(nil) + +func (r ResultOccurrence) IsOk() bool { + return r.Error == nil +} + +func (r ResultOccurrence) String() string { + attrsStr := lib.SerializeSlogAttrs(r.AllAttributes()...) + + if r.InnerResult != nil { + return fmt.Sprintf("[%s] %s (%s): %s", r.Definition.Code, r.Definition.Message, attrsStr, r.InnerResult.String()) + } + + return fmt.Sprintf("[%s] %s (%s)", r.Definition.Code, r.Definition.Message, attrsStr) +} + +func (r ResultOccurrence) AllAttributes() []slog.Attr { + return append(r.Definition.Attributes, r.Attributes...) +} + +func (r ResultOccurrence) Unwrap() Result { + return r.InnerResult +} + +func (r ResultOccurrence) WithAttribute(logAttrs ...slog.Attr) ResultOccurrence { + r.Attributes = append(r.Attributes, logAttrs...) + + return r +} + +func (r ResultOccurrence) WrapResult(result Result) ResultOccurrence { + r.InnerResult = result + + return r +} diff --git a/pkg/bliss/results/result.go b/pkg/bliss/results/result.go new file mode 100644 index 0000000..d21c19c --- /dev/null +++ b/pkg/bliss/results/result.go @@ -0,0 +1,14 @@ +package results + +import ( + "log/slog" +) + +type Result interface { + IsOk() bool + + String() string + AllAttributes() []slog.Attr + + Unwrap() Result +}