first commit

This commit is contained in:
2024-03-29 11:40:39 +03:00
commit 1a1ce70a5c
62 changed files with 4080 additions and 0 deletions
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2020 Gin-Gonic
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+72
View File
@@ -0,0 +1,72 @@
# RequestID
Request ID middleware for Gin Framework. Adds an indentifier to the response using the `X-Request-ID` header. Passes the `X-Request-ID` value back to the caller if it's sent in the request headers.
Copied from [gin-contrib/requestid](https://github.com/gin-contrib/requestid)
## Config
define your custom generator function:
```go
func main() {
r := gin.New()
r.Use(
requestid.New(
requestid.WithGenerator(func() string {
return "test"
}),
requestid.WithCustomHeaderStrKey("your-customer-key"),
),
)
// Example ping request.
r.GET("/ping", func(c *gin.Context) {
c.String(http.StatusOK, "pong "+fmt.Sprint(time.Now().Unix()))
})
// Listen and Server in 0.0.0.0:8080
r.Run(":8080")
}
```
## Example
```go
package main
import (
"fmt"
"net/http"
"time"
"gitverse.ru/andoma/gin-contrib/requestid"
"gitverse.ru/andoma/gin"
)
func main() {
r := gin.New()
r.Use(requestid.New())
// Example ping request.
r.GET("/ping", func(c *gin.Context) {
c.String(http.StatusOK, "pong "+fmt.Sprint(time.Now().Unix()))
})
// Listen and Server in 0.0.0.0:8080
r.Run(":8080")
}
```
How to get the request identifier:
```go
// Example / request.
r.GET("/", func(c *gin.Context) {
c.String(http.StatusOK, "id:"+requestid.Get(c))
})
```
+36
View File
@@ -0,0 +1,36 @@
package requestid
import (
"gitverse.ru/andoma/gin"
)
// Option for queue system
type Option func(*config)
type (
Generator func() string
Handler func(c *gin.Context, requestID string)
)
type HeaderStrKey string
// WithGenerator set generator function
func WithGenerator(g Generator) Option {
return func(cfg *config) {
cfg.generator = g
}
}
// WithCustomHeaderStrKey set custom header key for request id
func WithCustomHeaderStrKey(s HeaderStrKey) Option {
return func(cfg *config) {
cfg.headerKey = s
}
}
// WithHandler set handler function for request id with context
func WithHandler(handler Handler) Option {
return func(cfg *config) {
cfg.handler = handler
}
}
+56
View File
@@ -0,0 +1,56 @@
package requestid
import (
"github.com/google/uuid"
"gitverse.ru/andoma/gin"
)
const defaultHeaderKey = "X-Request-ID"
var headerXRequestID string
// Config defines the config for RequestID middleware
type config struct {
// Generator defines a function to generate an ID.
// Optional. Default: func() string {
// return uuid.New().String()
// }
generator Generator
headerKey HeaderStrKey
handler Handler
}
// New initializes the RequestID middleware.
func New(opts ...Option) gin.HandlerFunc {
cfg := &config{
generator: func() string {
return uuid.New().String()
},
headerKey: defaultHeaderKey,
}
for _, opt := range opts {
opt(cfg)
}
headerXRequestID = string(cfg.headerKey)
return func(c *gin.Context) {
// Get id from request
rid := c.GetHeader(headerXRequestID)
if rid == "" {
rid = cfg.generator()
c.Request.Header.Add(headerXRequestID, rid)
}
if cfg.handler != nil {
cfg.handler(c, rid)
}
// Set the id to ensure that the requestid is in the response
c.Header(headerXRequestID, rid)
}
}
// Get returns the request identifier
func Get(c *gin.Context) string {
return c.GetHeader(headerXRequestID)
}
+131
View File
@@ -0,0 +1,131 @@
package requestid
import (
"context"
"net/http"
"net/http/httptest"
"testing"
"github.com/stretchr/testify/assert"
"gitverse.ru/andoma/gin"
)
const testXRequestID = "test-request-id"
func emptySuccessResponse(c *gin.Context) {
c.String(http.StatusOK, "")
}
func Test_RequestID_CreateNew(t *testing.T) {
r := gin.New()
r.Use(New())
r.GET("/", emptySuccessResponse)
w := httptest.NewRecorder()
req, _ := http.NewRequestWithContext(context.Background(), "GET", "/", nil)
r.ServeHTTP(w, req)
assert.Equal(t, http.StatusOK, w.Code)
assert.NotEmpty(t, w.Header().Get(headerXRequestID))
}
func Test_RequestID_PassThru(t *testing.T) {
r := gin.New()
r.Use(New())
r.GET("/", emptySuccessResponse)
w := httptest.NewRecorder()
req, _ := http.NewRequestWithContext(context.Background(), "GET", "/", nil)
req.Header.Set(headerXRequestID, testXRequestID)
r.ServeHTTP(w, req)
assert.Equal(t, http.StatusOK, w.Code)
assert.Equal(t, testXRequestID, w.Header().Get(headerXRequestID))
}
func TestRequestIDWithCustomID(t *testing.T) {
r := gin.New()
r.Use(
New(
WithGenerator(func() string {
return testXRequestID
}),
),
)
r.GET("/", emptySuccessResponse)
w := httptest.NewRecorder()
req, _ := http.NewRequestWithContext(context.Background(), "GET", "/", nil)
r.ServeHTTP(w, req)
assert.Equal(t, http.StatusOK, w.Code)
assert.Equal(t, testXRequestID, w.Header().Get(headerXRequestID))
}
func TestRequestIDWithCustomHeaderKey(t *testing.T) {
r := gin.New()
r.Use(
New(
WithCustomHeaderStrKey("customKey"),
),
)
r.GET("/", emptySuccessResponse)
w := httptest.NewRecorder()
req, _ := http.NewRequestWithContext(context.Background(), "GET", "/", nil)
req.Header.Set("customKey", testXRequestID)
r.ServeHTTP(w, req)
assert.Equal(t, http.StatusOK, w.Code)
assert.Equal(t, testXRequestID, w.Header().Get("customKey"))
}
func TestRequestIDWithHandler(t *testing.T) {
r := gin.New()
called := false
r.Use(
New(
WithHandler(func(c *gin.Context, requestID string) {
called = true
assert.Equal(t, testXRequestID, requestID)
}),
),
)
w := httptest.NewRecorder()
req, _ := http.NewRequestWithContext(context.Background(), "GET", "/", nil)
req.Header.Set("X-Request-ID", testXRequestID)
r.ServeHTTP(w, req)
assert.True(t, called)
}
func TestRequestIDIsAttachedToRequestHeaders(t *testing.T) {
r := gin.New()
r.Use(New())
r.GET("/", func(c *gin.Context) {
result := c.GetHeader(defaultHeaderKey)
assert.NotEmpty(t, result)
})
w := httptest.NewRecorder()
req, _ := http.NewRequestWithContext(context.Background(), "GET", "/", nil)
r.ServeHTTP(w, req)
}
func TestRequestIDNotNilAfterGinCopy(t *testing.T) {
r := gin.New()
r.Use(New())
r.GET("/", func(c *gin.Context) {
copy := c.Copy()
result := Get(copy)
assert.NotEmpty(t, result)
})
w := httptest.NewRecorder()
req, _ := http.NewRequestWithContext(context.Background(), "GET", "/", nil)
r.ServeHTTP(w, req)
}