From 552b20cda9590282330a2f116f19f21b2dbd53a9 Mon Sep 17 00:00:00 2001 From: Nikita Vasilev Date: Thu, 2 Nov 2023 18:51:50 +0300 Subject: [PATCH] Release `1.0.0` (#1) * Implement `go-middlewares` * Update `README.md` * Update `CHANGELOG.md` * Update `README.md` --- .github/ISSUE_TEMPLATE/bug_report.md | 41 +++++++++++ .github/ISSUE_TEMPLATE/feature_request.md | 11 +++ .../PULL_REQUEST_TEMPLATE/bug_template.yml | 9 +++ .../feature_template.yml | 12 ++++ .github/workflows/ci.yml | 29 ++++++++ .github/workflows/danger.yml | 29 ++++++++ .pre-commit-config.yml | 16 +++++ Brewfile | 1 + Brewfile.lock.json | 62 +++++++++++++++++ CHANGELOG.md | 12 ++++ Dangerfile | 1 + Gemfile | 3 + Gemfile.lock | 66 ++++++++++++++++++ README.md | 68 ++++++++++++++++++- codecov.yml | 48 +++++++++++++ error_middleware.go | 38 +++++++++++ error_middleware_test.go | 44 ++++++++++++ go.mod | 11 +++ go.sum | 10 +++ log_middleware.go | 35 ++++++++++ log_middleware_test.go | 57 ++++++++++++++++ middlewares.go | 16 +++++ middlewares_test.go | 25 +++++++ models.go | 23 +++++++ 24 files changed, 666 insertions(+), 1 deletion(-) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md create mode 100644 .github/PULL_REQUEST_TEMPLATE/bug_template.yml create mode 100644 .github/PULL_REQUEST_TEMPLATE/feature_template.yml create mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/danger.yml create mode 100644 .pre-commit-config.yml create mode 100644 Brewfile create mode 100644 Brewfile.lock.json create mode 100644 CHANGELOG.md create mode 100644 Dangerfile create mode 100644 Gemfile create mode 100644 Gemfile.lock create mode 100644 codecov.yml create mode 100644 error_middleware.go create mode 100644 error_middleware_test.go create mode 100644 go.mod create mode 100644 go.sum create mode 100644 log_middleware.go create mode 100644 log_middleware_test.go create mode 100644 middlewares.go create mode 100644 middlewares_test.go create mode 100644 models.go diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..8dc7e75 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,41 @@ +--- +name: "🐛 Bug Report" +about: Report a reproducible bug or regression. +title: 'Bug: ' +labels: 'bug' + +--- + + + +Application version: + +## Steps To Reproduce + +1. +2. + + + +Link to code example: + + + +## The current behavior + + +## The expected behavior \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..5eabd0a --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,11 @@ +--- +name: 🛠 Feature request +about: If you have a feature request for the go-middlewares, file it here. +labels: 'type: enhancement' +--- + +**Feature description** +Clearly and concisely describe the feature. + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. \ No newline at end of file diff --git a/.github/PULL_REQUEST_TEMPLATE/bug_template.yml b/.github/PULL_REQUEST_TEMPLATE/bug_template.yml new file mode 100644 index 0000000..7d6a149 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE/bug_template.yml @@ -0,0 +1,9 @@ +## Bug description +Clearly and concisely describe the problem. + +## Solution description +Describe your code changes in detail for reviewers. Explain the technical solution you have provided and how it fixes the issue case. + +## Covered unit test cases +- [x] yes +- [x] no \ No newline at end of file diff --git a/.github/PULL_REQUEST_TEMPLATE/feature_template.yml b/.github/PULL_REQUEST_TEMPLATE/feature_template.yml new file mode 100644 index 0000000..ab3978b --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE/feature_template.yml @@ -0,0 +1,12 @@ +## Feature description +Clearly and concisely describe the feature. + +## Solution description +Describe your code changes in detail for reviewers. + +## Areas affected and ensured +List out the areas affected by your code changes. + +## Covered unit test cases +- [x] yes +- [x] no \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..cca4d4a --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,29 @@ +name: go_middlewares + +on: + push: + branches: + - main + - dev + pull_request: + types: + - opened + - synchronize + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Set up Go + uses: actions/setup-go@v3 + with: + go-version: 1.19 + - name: Build + run: go build -v ./... + - name: Test + run: go test -v ./... -coverprofile=coverage.out -covermode=atomic + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v3.1.0 + with: + token: ${{ secrets.CODECOV_TOKEN }} \ No newline at end of file diff --git a/.github/workflows/danger.yml b/.github/workflows/danger.yml new file mode 100644 index 0000000..1a0ab52 --- /dev/null +++ b/.github/workflows/danger.yml @@ -0,0 +1,29 @@ +name: Danger + +on: + pull_request: + types: [synchronize, opened, reopened, labeled, unlabeled, edited] + +env: + LC_CTYPE: en_US.UTF-8 + LANG: en_US.UTF-8 + +jobs: + run-danger: + runs-on: ubuntu-latest + steps: + - name: ruby setup + uses: ruby/setup-ruby@v1 + with: + ruby-version: 2.7 + bundler-cache: true + - name: Checkout code + uses: actions/checkout@v2 + - name: Setup gems + run: | + gem install bundler + bundle install --clean --path vendor/bundle + - name: danger + env: + DANGER_GITHUB_API_TOKEN: ${{ secrets.DANGER_GITHUB_API_TOKEN }} + run: bundle exec danger --verbose \ No newline at end of file diff --git a/.pre-commit-config.yml b/.pre-commit-config.yml new file mode 100644 index 0000000..a272c6b --- /dev/null +++ b/.pre-commit-config.yml @@ -0,0 +1,16 @@ +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.3.0 + hooks: + - id: trailing-whitespace + - id: end-of-file-fixer + - id: check-yaml + - id: check-added-large-files + - repo: https://github.com/dnephin/pre-commit-golang + rev: v0.5.0 + hooks: + - id: go-fmt + - id: go-imports + - id: no-go-testing + - id: golangci-lint + - id: go-unit-tests \ No newline at end of file diff --git a/Brewfile b/Brewfile new file mode 100644 index 0000000..5c5206f --- /dev/null +++ b/Brewfile @@ -0,0 +1 @@ +brew "golangci-lint" \ No newline at end of file diff --git a/Brewfile.lock.json b/Brewfile.lock.json new file mode 100644 index 0000000..1e0aeb0 --- /dev/null +++ b/Brewfile.lock.json @@ -0,0 +1,62 @@ +{ + "entries": { + "brew": { + "golangci-lint": { + "version": "1.55.1", + "bottle": { + "rebuild": 0, + "root_url": "https://ghcr.io/v2/homebrew/core", + "files": { + "arm64_sonoma": { + "cellar": ":any_skip_relocation", + "url": "https://ghcr.io/v2/homebrew/core/golangci-lint/blobs/sha256:9d538964526454d4f8b1ab095bf601bd43fab37f95d4d52fca5d4a012cd2a2df", + "sha256": "9d538964526454d4f8b1ab095bf601bd43fab37f95d4d52fca5d4a012cd2a2df" + }, + "arm64_ventura": { + "cellar": ":any_skip_relocation", + "url": "https://ghcr.io/v2/homebrew/core/golangci-lint/blobs/sha256:f8a3588e7823345d134095988cfcbe69826e912cad50fea2ffaad16b7cfe5e6b", + "sha256": "f8a3588e7823345d134095988cfcbe69826e912cad50fea2ffaad16b7cfe5e6b" + }, + "arm64_monterey": { + "cellar": ":any_skip_relocation", + "url": "https://ghcr.io/v2/homebrew/core/golangci-lint/blobs/sha256:db8b11f44eb2151c60d163a85efaeeb27f4887b0e0d1418d35e451cda446360f", + "sha256": "db8b11f44eb2151c60d163a85efaeeb27f4887b0e0d1418d35e451cda446360f" + }, + "sonoma": { + "cellar": ":any_skip_relocation", + "url": "https://ghcr.io/v2/homebrew/core/golangci-lint/blobs/sha256:1bbdd91bf065e905320c51e050aaaa5cf41386acb896d3f4704e8940a8f323e3", + "sha256": "1bbdd91bf065e905320c51e050aaaa5cf41386acb896d3f4704e8940a8f323e3" + }, + "ventura": { + "cellar": ":any_skip_relocation", + "url": "https://ghcr.io/v2/homebrew/core/golangci-lint/blobs/sha256:b9c815da27c6b3533fcbb84b3cd223dece6e7b740a3ac6c76532715e232e0490", + "sha256": "b9c815da27c6b3533fcbb84b3cd223dece6e7b740a3ac6c76532715e232e0490" + }, + "monterey": { + "cellar": ":any_skip_relocation", + "url": "https://ghcr.io/v2/homebrew/core/golangci-lint/blobs/sha256:a7aad3d6901a2f70fe9a87167245ca09bb1e1de1d938c0ce1ba5cb7587500311", + "sha256": "a7aad3d6901a2f70fe9a87167245ca09bb1e1de1d938c0ce1ba5cb7587500311" + }, + "x86_64_linux": { + "cellar": ":any_skip_relocation", + "url": "https://ghcr.io/v2/homebrew/core/golangci-lint/blobs/sha256:e6270eb1a19cc1d7002d054533a8aeded82fedbc49d17c8909efd5b4f0418a43", + "sha256": "e6270eb1a19cc1d7002d054533a8aeded82fedbc49d17c8909efd5b4f0418a43" + } + } + } + } + } + }, + "system": { + "macos": { + "ventura": { + "HOMEBREW_VERSION": "4.1.16", + "HOMEBREW_PREFIX": "/usr/local", + "Homebrew/homebrew-core": "api", + "CLT": "", + "Xcode": "14.1", + "macOS": "13.4" + } + } + } +} diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..ea4c453 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,12 @@ +# Change Log +All notable changes to this project will be documented in this file. + +#### 1.x Releases +- `1.0.x` Releases - [1.0.0](#100) + +## [1.0.0](https://github.com/space-code/go-middlewares/releases/tag/1.0.0) +Released on 2023-11-02. + +#### Added +- Initial release of go-middlewares. + - Added by [Nikita Vasilev](https://github.com/nik3212). \ No newline at end of file diff --git a/Dangerfile b/Dangerfile new file mode 100644 index 0000000..b266982 --- /dev/null +++ b/Dangerfile @@ -0,0 +1 @@ +danger.import_dangerfile(github: 'space-code/dangerfile') \ No newline at end of file diff --git a/Gemfile b/Gemfile new file mode 100644 index 0000000..20dff64 --- /dev/null +++ b/Gemfile @@ -0,0 +1,3 @@ +source "https://rubygems.org" + +gem 'danger' \ No newline at end of file diff --git a/Gemfile.lock b/Gemfile.lock new file mode 100644 index 0000000..43236ba --- /dev/null +++ b/Gemfile.lock @@ -0,0 +1,66 @@ +GEM + remote: https://rubygems.org/ + specs: + addressable (2.8.5) + public_suffix (>= 2.0.2, < 6.0) + base64 (0.1.1) + claide (1.1.0) + claide-plugins (0.9.2) + cork + nap + open4 (~> 1.3) + colored2 (3.1.2) + cork (0.3.0) + colored2 (~> 3.1) + danger (9.3.2) + claide (~> 1.0) + claide-plugins (>= 0.9.2) + colored2 (~> 3.1) + cork (~> 0.1) + faraday (>= 0.9.0, < 3.0) + faraday-http-cache (~> 2.0) + git (~> 1.13) + kramdown (~> 2.3) + kramdown-parser-gfm (~> 1.0) + no_proxy_fix + octokit (~> 6.0) + terminal-table (>= 1, < 4) + faraday (2.7.11) + base64 + faraday-net_http (>= 2.0, < 3.1) + ruby2_keywords (>= 0.0.4) + faraday-http-cache (2.5.0) + faraday (>= 0.8) + faraday-net_http (3.0.2) + git (1.18.0) + addressable (~> 2.8) + rchardet (~> 1.8) + kramdown (2.4.0) + rexml + kramdown-parser-gfm (1.1.0) + kramdown (~> 2.0) + nap (1.1.0) + no_proxy_fix (0.1.2) + octokit (6.1.1) + faraday (>= 1, < 3) + sawyer (~> 0.9) + open4 (1.3.4) + public_suffix (5.0.3) + rchardet (1.8.0) + rexml (3.2.6) + ruby2_keywords (0.0.5) + sawyer (0.9.2) + addressable (>= 2.3.5) + faraday (>= 0.17.3, < 3) + terminal-table (3.0.2) + unicode-display_width (>= 1.1.1, < 3) + unicode-display_width (2.5.0) + +PLATFORMS + x86_64-darwin-22 + +DEPENDENCIES + danger + +BUNDLED WITH + 2.4.21 diff --git a/README.md b/README.md index 36e2b13..e44891b 100644 --- a/README.md +++ b/README.md @@ -1 +1,67 @@ -# go-middlewares \ No newline at end of file +

go-middlewares

+ +

+License +CI +

+ +## Description +`go-middlewares` is a collection of commonly used middlewares in Go. + +- [Usage](#usage) +- [Installation](#installation) +- [Communication](#communication) +- [Contributing](#contributing) +- [Author](#author) +- [License](#license) + +## Usage + +1. The `ErrorHandlerMiddleware` is a middleware function designed to handle panics and errors that may occur during the execution of an HTTP handler. + +Here's an example of how to use it in your code: + +```go +// Create a new HTTP handler and wrap it with the ErrorHandlerMiddleware. +handler := ErrorHandlerMiddleware(yourHandler) + +// Use the wrapped handler for your server. +http.Handle("/your-route", handler) + +// Start your HTTP server. +http.ListenAndServe(":8080", nil) +``` + +2. The `LogHandlerMiddleware` is designed to log HTTP requests made to your web application. It captures various details about the request and logs them to your preferred output, which is especially useful for debugging and monitoring. + +```go +// Create a new HTTP handler and wrap it with the LogHandlerMiddleware. +handler := LogHandlerMiddleware(yourHandler) + +// Use the wrapped handler for your HTTP server. +http.Handle("/your-route", handler) + +// Start your HTTP server. +http.ListenAndServe(":8080", nil) +``` + +## Installation + +``` +go get github.com/space-code/go-middleware +``` + +## Communication +- If you **found a bug**, open an issue. +- If you **have a feature request**, open an issue. +- If you **want to contribute**, submit a pull request. + +## Contributing + +Please feel free to help out with this project! If you see something that could be made better or want a new feature, open up an issue or send a Pull Request! + +## Author +Nikita Vasilev, nv3212@gmail.com + +## License +go-middlewares is available under the MIT license. See the LICENSE file for more info. \ No newline at end of file diff --git a/codecov.yml b/codecov.yml new file mode 100644 index 0000000..b415604 --- /dev/null +++ b/codecov.yml @@ -0,0 +1,48 @@ +codecov: + # Require CI to pass to show coverage, default yes + require_ci_to_pass: yes + notify: + # Codecov should wait for all CI statuses to complete, default yes + wait_for_ci: yes + +coverage: + # Coverage precision range 0-5, default 2 + precision: 2 + + # Direction to round the coverage value - up, down, nearest, default down + round: nearest + + # Value range for red...green, default 70...100 + range: "70...90" + + status: + # Overall project coverage, compare against pull request base + project: + default: + # The required coverage value + target: 50% + + # The leniency in hitting the target. Allow coverage to drop by X% + threshold: 5% + + # Only measure lines adjusted in the pull request or single commit, if the commit in not in the pr + patch: + default: + # The required coverage value + target: 85% + + # Allow coverage to drop by X% + threshold: 5% + changes: no + +comment: + # Pull request Codecov comment format. + # diff: coverage diff of the pull request + # files: a list of files impacted by the pull request (coverage changes, file is new or removed) + layout: "diff, files" + + # Update Codecov comment, if exists. Otherwise post new + behavior: default + + # If true, only post the Codecov comment if coverage changes + require_changes: false \ No newline at end of file diff --git a/error_middleware.go b/error_middleware.go new file mode 100644 index 0000000..7cac4fa --- /dev/null +++ b/error_middleware.go @@ -0,0 +1,38 @@ +package gomiddlewares + +import ( + "encoding/json" + "net/http" +) + +// ErrorHandlerMiddleware is a middleware function that wraps an HTTP handler +// and handles panics that occur during the execution of the wrapped handler. +// It captures and recovers from panics, then sends an appropriate HTTP +// response with status code and error message in JSON format. +func ErrorHandlerMiddleware(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + defer func() { + if r := recover(); r != nil { + statusCode := http.StatusInternalServerError + errorMessage := "Internal Server Error" + + if err, ok := r.(ErrorWithStatus); ok { + errorMessage = err.Error() + statusCode = err.StatusCode() + } + + response := errorResponse{ + Status: statusCode, + Message: errorMessage, + } + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(statusCode) + + json.NewEncoder(w).Encode(response) + } + }() + + next.ServeHTTP(w, r) + }) +} diff --git a/error_middleware_test.go b/error_middleware_test.go new file mode 100644 index 0000000..92f5b96 --- /dev/null +++ b/error_middleware_test.go @@ -0,0 +1,44 @@ +package gomiddlewares + +import ( + "errors" + "net/http" + "net/http/httptest" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestErrorResponseWithDefaultErrorDescription(t *testing.T) { + req, err := http.NewRequest("GET", "/test", nil) + + if err != nil { + t.Fatal(err) + } + + rr := httptest.NewRecorder() + handler := ErrorHandlerMiddleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + panic(errors.New("error description")) + })) + handler.ServeHTTP(rr, req) + + assert.Contains(t, rr.Body.String(), "{\"status\":500,\"message\":\"Internal Server Error\"}\n") + assert.Equal(t, http.StatusInternalServerError, rr.Code) +} + +func TestErrorResponseWithCustomErrorDescription(t *testing.T) { + req, err := http.NewRequest("GET", "/test", nil) + + if err != nil { + t.Fatal(err) + } + + rr := httptest.NewRecorder() + handler := ErrorHandlerMiddleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + panic(ErrorWithStatus{Message: "status bad gateway", Status: http.StatusBadGateway}) + })) + handler.ServeHTTP(rr, req) + + assert.Contains(t, rr.Body.String(), "{\"status\":502,\"message\":\"status bad gateway\"}\n") + assert.Equal(t, http.StatusBadGateway, rr.Code) +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..79c856b --- /dev/null +++ b/go.mod @@ -0,0 +1,11 @@ +module github.com/space-code/go-middlewares + +go 1.19 + +require github.com/stretchr/testify v1.8.4 + +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..fa4b6e6 --- /dev/null +++ b/go.sum @@ -0,0 +1,10 @@ +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/log_middleware.go b/log_middleware.go new file mode 100644 index 0000000..1467b34 --- /dev/null +++ b/log_middleware.go @@ -0,0 +1,35 @@ +package gomiddlewares + +import ( + "fmt" + "net/http" +) + +type logHandler struct { + http.ResponseWriter + statusCode int + contentLen int +} + +func LogHandlerMiddleware(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + lh := &logHandler{w, http.StatusOK, 0} + next.ServeHTTP(lh, r) + logRequest(lh.statusCode, lh.contentLen, r) + }) +} + +func logRequest(status, len int, r *http.Request) { + fmt.Fprintf(output, "%s %s %s %s %d %d %s", r.RemoteAddr, r.Method, r.RequestURI, r.Proto, status, len, r.UserAgent()) +} + +func (l *logHandler) WriteHeader(statusCode int) { + l.statusCode = statusCode + l.ResponseWriter.WriteHeader(statusCode) +} + +func (l *logHandler) Write(b []byte) (int, error) { + n, err := l.ResponseWriter.Write(b) + l.contentLen += n + return n, err +} diff --git a/log_middleware_test.go b/log_middleware_test.go new file mode 100644 index 0000000..02afc2c --- /dev/null +++ b/log_middleware_test.go @@ -0,0 +1,57 @@ +package gomiddlewares + +import ( + "bytes" + "log" + "net/http" + "net/http/httptest" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestLogFormat(t *testing.T) { + req, err := http.NewRequest("GET", "/test", nil) + req.Header.Set("User-Agent", "application/json") + req.Header.Set("Referer", "testing") + if err != nil { + log.Fatal(err) + } + status := http.StatusOK + conLen := 23 + var b bytes.Buffer + SetOutput(&b) + logRequest(status, conLen, req) + rval := b.String() + assert.Equal(t, rval, " GET HTTP/1.1 200 23 application/json") +} + +func TestShadowResponse(t *testing.T) { + rr := httptest.NewRecorder() + l := logHandler{rr, http.StatusOK, 0} + l.WriteHeader(http.StatusBadGateway) + sval := "this is a body" + l.Write([]byte(sval)) + assert.Equal(t, http.StatusBadGateway, l.statusCode, "should be equal") + assert.Equal(t, len(sval), l.contentLen, "should be equal") +} + +func TestErrorResponseWithLogMiddleware(t *testing.T) { + req, err := http.NewRequest("GET", "/test", nil) + + if err != nil { + t.Fatal(err) + } + + var b bytes.Buffer + SetOutput(&b) + + rr := httptest.NewRecorder() + handler := LogHandlerMiddleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Write([]byte("empty response")) + })) + handler.ServeHTTP(rr, req) + + rval := b.String() + assert.Equal(t, rval, " GET HTTP/1.1 200 14 ") +} diff --git a/middlewares.go b/middlewares.go new file mode 100644 index 0000000..f7f5f79 --- /dev/null +++ b/middlewares.go @@ -0,0 +1,16 @@ +package gomiddlewares + +import ( + "io" + "os" +) + +var output io.Writer + +func init() { + output = os.Stdout +} + +func SetOutput(w io.Writer) { + output = w +} diff --git a/middlewares_test.go b/middlewares_test.go new file mode 100644 index 0000000..35c6761 --- /dev/null +++ b/middlewares_test.go @@ -0,0 +1,25 @@ +package gomiddlewares + +import ( + "io" + "os" +) + +func ExampleSetOutput_singleWriter() { + // Open the log file for writing. + logFile, _ := os.Create("logfile") + + // Set the output to the log file. + SetOutput(logFile) +} + +func ExampleSetOutput_multiWriter() { + // Open the log file for writing. + logFile, _ := os.Create("logfile") + + // Create a MultiWriter that writes to both os.Stdout and the log file. + mw := io.MultiWriter(os.Stdout, logFile) + + // Set the output to the MultiWriter. + SetOutput(mw) +} diff --git a/models.go b/models.go new file mode 100644 index 0000000..8ecc28a --- /dev/null +++ b/models.go @@ -0,0 +1,23 @@ +package gomiddlewares + +// errorResponse is a struct to represent JSON error responses. +type errorResponse struct { + Status int `json:"status"` + Message string `json:"message"` +} + +// ErrorWithStatus is an error type that includes a custom status code. +type ErrorWithStatus struct { + Message string + Status int +} + +// Error returns an error description. +func (e ErrorWithStatus) Error() string { + return e.Message +} + +// StatusCode returns an error status code. +func (e ErrorWithStatus) StatusCode() int { + return e.Status +}