├── img ├── get.png └── Post.png ├── models └── models.go ├── main.go ├── controllers ├── baseConversion.go └── Controllers.go ├── README.md ├── go.mod ├── main_test.go └── go.sum /img/get.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Supraav/golang-URL-Shortner/master/img/get.png -------------------------------------------------------------------------------- /img/Post.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Supraav/golang-URL-Shortner/master/img/Post.png -------------------------------------------------------------------------------- /models/models.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | type InputSchema struct { 4 | URL string 5 | } 6 | 7 | var URLs = make(map[string]string) -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "treeleaf/controllers" 5 | "github.com/gin-gonic/gin" 6 | ) 7 | 8 | 9 | func main() { 10 | router := gin.Default() 11 | router.POST("/shorten",controllers.CreateShortURL) 12 | router.GET("/:shortCode", controllers.GetURL) 13 | router.Run("localhost:8080") 14 | } -------------------------------------------------------------------------------- /controllers/baseConversion.go: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | // import ( 4 | // "math/rand" 5 | // "strings" 6 | // ) 7 | 8 | // const ( 9 | // base uint64 = 62 10 | // base62Digits = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" 11 | // ) 12 | 13 | // func Base62Conversion() string { 14 | // var shortURLLength = 6 15 | // var sb strings.Builder 16 | // for i := 0; i < shortURLLength; i++ { 17 | // index := rand.Intn(len(base62Digits)) 18 | // sb.WriteByte(base62Digits[index]) 19 | // } 20 | 21 | // return sb.String() 22 | // } 23 | 24 | // func toBase62(num uint64) string { 25 | // encoded := "" 26 | // for num > 0 { 27 | // r := num % base 28 | // num /= base 29 | // encoded = string(base62Digits[r]) + encoded 30 | 31 | // } 32 | // return encoded 33 | // } 34 | 35 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # URL shortner in GOlang 2 | 3 | A simple URL shortner made using golang and gin router. 4 | The POST and GET requested has been tested on postman. 5 | 6 | The microservice runs on [localhost:8080](http://localhost:8080) by default. 7 | 8 | ### Start: 9 | 10 | git clone 11 | go run . 12 | 13 | ### for POST request: 14 | 15 | In postman's POST request type: 16 | 17 | [localhost:8080/shorten] 18 | 19 | In postman -> Body -> raw , pass the JSON value as: 20 | 21 | { 22 | "url":"http://www.google.com" 23 | } 24 | 25 | ![Post](https://github.com/Supraav/golang-URL-Shortner/assets/47569979/aae8bf77-3e86-4320-bfac-2132b2b2c1cf) 26 | 27 | A respone base62 shortcode will be generated 28 | 29 | ### for GET request: 30 | 31 | paste the received shortcode in the browser as: 32 | 33 | [localhost:8080/shortcode] 34 | 35 | which redirects to the google's homepage 36 | 37 | 38 | ![get](https://github.com/Supraav/golang-URL-Shortner/assets/47569979/db06562a-4feb-4302-89d4-257e04868f0e) 39 | 40 | 41 | ### for Test Cases: 42 | go test 43 | 44 | runs a test file to test the POST method 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module treeleaf 2 | 3 | go 1.20 4 | 5 | require ( 6 | github.com/alextanhongpin/base62 v0.0.0-20181112043535-204cf116eaa9 7 | github.com/gin-gonic/gin v1.9.1 8 | github.com/stretchr/testify v1.8.3 9 | ) 10 | 11 | require ( 12 | github.com/bytedance/sonic v1.9.1 // indirect 13 | github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect 14 | github.com/davecgh/go-spew v1.1.1 // indirect 15 | github.com/gabriel-vasile/mimetype v1.4.2 // indirect 16 | github.com/gin-contrib/sse v0.1.0 // indirect 17 | github.com/go-playground/locales v0.14.1 // indirect 18 | github.com/go-playground/universal-translator v0.18.1 // indirect 19 | github.com/go-playground/validator/v10 v10.14.0 // indirect 20 | github.com/goccy/go-json v0.10.2 // indirect 21 | github.com/json-iterator/go v1.1.12 // indirect 22 | github.com/klauspost/cpuid/v2 v2.2.4 // indirect 23 | github.com/leodido/go-urn v1.2.4 // indirect 24 | github.com/mattn/go-isatty v0.0.19 // indirect 25 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 26 | github.com/modern-go/reflect2 v1.0.2 // indirect 27 | github.com/pelletier/go-toml/v2 v2.0.8 // indirect 28 | github.com/pmezard/go-difflib v1.0.0 // indirect 29 | github.com/twitchyliquid64/golang-asm v0.15.1 // indirect 30 | github.com/ugorji/go/codec v1.2.11 // indirect 31 | golang.org/x/arch v0.3.0 // indirect 32 | golang.org/x/crypto v0.9.0 // indirect 33 | golang.org/x/net v0.10.0 // indirect 34 | golang.org/x/sys v0.8.0 // indirect 35 | golang.org/x/text v0.9.0 // indirect 36 | google.golang.org/protobuf v1.30.0 // indirect 37 | gopkg.in/yaml.v3 v3.0.1 // indirect 38 | ) 39 | -------------------------------------------------------------------------------- /controllers/Controllers.go: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "net/http" 7 | "net/url" 8 | "strconv" 9 | "strings" 10 | "treeleaf/models" 11 | 12 | "github.com/alextanhongpin/base62" 13 | "github.com/gin-gonic/gin" 14 | ) 15 | 16 | func GetURL(ctx *gin.Context) { 17 | shortCode := ctx.Param("shortCode") 18 | longURL,ok:=models.URLs[shortCode] 19 | 20 | if !ok{ 21 | ctx.JSON(http.StatusNotFound, gin.H{"error": "Short code not found"}) 22 | } 23 | ctx.Redirect(http.StatusMovedPermanently, longURL) 24 | } 25 | 26 | func CreateShortURL(ctx *gin.Context){ 27 | var input models.InputSchema 28 | 29 | ctx.BindJSON(&input) 30 | 31 | 32 | if input.URL==""{ 33 | ctx.JSON(http.StatusBadRequest, gin.H{"error": "Invalid JSON payload"}) 34 | return 35 | } 36 | 37 | err:=isValidURL(input.URL) 38 | if err!=nil { 39 | ctx.JSON(http.StatusBadRequest, gin.H{"error":err.Error()}) 40 | return 41 | } 42 | 43 | // to base62 44 | encoded:=base62.Decode(input.URL) 45 | shortCode:=strconv.Itoa(int(encoded)) 46 | 47 | models.URLs[shortCode] = input.URL 48 | fmt.Println(models.URLs) 49 | 50 | response := map[string]string{ 51 | "shortURL": ctx.Request.Host+"/"+shortCode, 52 | } 53 | 54 | ctx.JSON(http.StatusCreated, response) 55 | } 56 | 57 | func isValidURL(inputURL string) error { 58 | if _,err := url.Parse(inputURL); err != nil { 59 | return errors.New("invalid URL") 60 | } 61 | 62 | response, err := http.Get(inputURL) 63 | if err != nil{ 64 | return errors.New("URL doesnot exist") 65 | } 66 | 67 | if response.StatusCode!=200{ 68 | return errors.New("URL doesnot exist") 69 | } 70 | 71 | if !(strings.HasPrefix(inputURL, "http://") || strings.HasPrefix(inputURL, "https://")){ 72 | return errors.New("invalid URL input. E.g: https://www.example.com") 73 | } 74 | return nil 75 | } 76 | -------------------------------------------------------------------------------- /main_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "net/http" 7 | "net/http/httptest" 8 | "strings" 9 | "testing" 10 | "treeleaf/controllers" 11 | "treeleaf/models" 12 | 13 | "github.com/gin-gonic/gin" 14 | "github.com/stretchr/testify/assert" 15 | ) 16 | 17 | func SetUpRouter() *gin.Engine { 18 | router := gin.Default() 19 | return router 20 | } 21 | var obtainedCode="" 22 | 23 | 24 | func TestCreateShortURL(t *testing.T) { 25 | r := SetUpRouter() 26 | r.POST("/shorten", controllers.CreateShortURL) 27 | 28 | url:="https://google.com" 29 | 30 | validInput := models.InputSchema{URL: url} 31 | 32 | validInputJSON, _ := json.Marshal(validInput) 33 | 34 | req, _ := http.NewRequest("POST", "/shorten", bytes.NewBuffer(validInputJSON)) 35 | req.Header.Set("Content-Type", "application/json") 36 | 37 | w := httptest.NewRecorder() 38 | r.ServeHTTP(w, req) 39 | assert.Equal(t, http.StatusCreated, w.Code) 40 | 41 | var response map[string]string 42 | 43 | err := json.Unmarshal(w.Body.Bytes(), &response) 44 | if err != nil { 45 | t.Errorf("Error parsing JSON response: %v", err) 46 | } 47 | shortURL, ok := response["shortURL"] 48 | if ok{ 49 | shortCode := extractShortCode(shortURL) 50 | obtainedCode=shortCode 51 | // fmt.Println(shortCode) 52 | } 53 | 54 | } 55 | 56 | // func TestGetURL(t *testing.T) { 57 | // r := SetUpRouter() 58 | 59 | // shortCode := obtainedCode 60 | // longURL := "https://google.com" 61 | // models.URLs[shortCode] = longURL 62 | 63 | // req := httptest.NewRequest("GET", "/"+shortCode, nil) 64 | 65 | // w := httptest.NewRecorder() 66 | 67 | // r.ServeHTTP(w, req) 68 | 69 | // if w.Code != http.StatusMovedPermanently { 70 | // t.Errorf("Expected status %d, got %d", http.StatusMovedPermanently, w.Code) 71 | // } 72 | 73 | // expectedLocation := longURL 74 | // if gotLocation := w.Header().Get("Location"); gotLocation != expectedLocation { 75 | // t.Errorf("Expected redirect location %s, got %s", expectedLocation, gotLocation) 76 | // } 77 | // } 78 | 79 | func extractShortCode(shortURL string) string { 80 | parts := strings.Split(shortURL, "/") 81 | return parts[len(parts)-1] 82 | } -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/alextanhongpin/base62 v0.0.0-20181112043535-204cf116eaa9 h1:pzEZ91ZEwxRxzXxaQQz2CQpjjQaqQVtPN1qn+N6SyjY= 2 | github.com/alextanhongpin/base62 v0.0.0-20181112043535-204cf116eaa9/go.mod h1:HAkSP6SOWM5D64M8tTKZHcsBsLirOIgk4kGdbb/O9Jw= 3 | github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= 4 | github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s= 5 | github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= 6 | github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= 7 | github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams= 8 | github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= 9 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 10 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 11 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 12 | github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= 13 | github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= 14 | github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= 15 | github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= 16 | github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= 17 | github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= 18 | github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= 19 | github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= 20 | github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= 21 | github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= 22 | github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= 23 | github.com/go-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg/+t63MyGU2n5js= 24 | github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= 25 | github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= 26 | github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= 27 | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= 28 | github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= 29 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 30 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 31 | github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= 32 | github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= 33 | github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= 34 | github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk= 35 | github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= 36 | github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= 37 | github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= 38 | github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= 39 | github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= 40 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 41 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= 42 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 43 | github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= 44 | github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= 45 | github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ= 46 | github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4= 47 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 48 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 49 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 50 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 51 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 52 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 53 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 54 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 55 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 56 | github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 57 | github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 58 | github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY= 59 | github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= 60 | github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= 61 | github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= 62 | github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= 63 | github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= 64 | golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= 65 | golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k= 66 | golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= 67 | golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g= 68 | golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= 69 | golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= 70 | golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= 71 | golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 72 | golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 73 | golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= 74 | golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 75 | golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= 76 | golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= 77 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= 78 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 79 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= 80 | google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= 81 | google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= 82 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 83 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 84 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 85 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 86 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 87 | rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= 88 | --------------------------------------------------------------------------------