├── README.md ├── apicache ├── go.mod ├── go.sum └── main.go ├── app_configuration ├── config.yml ├── configuration │ └── configuration.go ├── database │ └── database.go ├── go.mod ├── go.sum ├── main.go └── topics.md ├── btrlogs ├── api │ ├── api.go │ └── countries.go ├── blog │ └── blog.go ├── go.mod ├── go.sum └── main.go ├── chan-listener ├── go.mod ├── main.go └── messanger │ └── messanger.go ├── channels ├── go.mod ├── go.sum └── main.go ├── context ├── context tree.png ├── go.mod ├── main.go ├── message │ └── message.go ├── server │ ├── middleware.go │ └── server.go ├── users │ └── users.go └── values │ └── values.go ├── db_conn ├── go.mod ├── go.sum └── main.go ├── dependency_injection ├── config.yml ├── configuration │ └── configuration.go ├── database │ └── database.go ├── go.mod ├── go.sum ├── main.go └── repository │ ├── repository.go │ └── users.go ├── dockerization ├── .dockerignore ├── Dockerfile ├── api │ ├── api.go │ ├── middleware.go │ └── routes.go ├── go.mod ├── go.sum └── main.go ├── echo ├── api │ ├── api.go │ ├── middleware.go │ └── routes.go ├── go.mod ├── go.sum ├── main.go └── topics.md ├── event-handler ├── accounts │ ├── config │ │ └── config.go │ ├── database │ │ ├── database.go │ │ └── schema.sql │ ├── go.mod │ ├── go.sum │ ├── internal │ │ ├── contracts │ │ │ └── contracts.go │ │ ├── events │ │ │ └── payment_created.go │ │ ├── httpapi │ │ │ ├── handlers │ │ │ │ └── profile │ │ │ │ │ ├── get.go │ │ │ │ │ ├── post.go │ │ │ │ │ └── profile.go │ │ │ └── httpapi.go │ │ ├── listener │ │ │ ├── handlers │ │ │ │ └── paymentsevent │ │ │ │ │ └── handler.go │ │ │ └── listener.go │ │ ├── repositories │ │ │ ├── profiles │ │ │ │ └── profiles.go │ │ │ └── repositories.go │ │ └── services │ │ │ ├── profiler │ │ │ └── profiler.go │ │ │ └── services.go │ ├── logger │ │ └── logger.go │ └── main.go └── payments │ ├── config │ └── config.go │ ├── database │ ├── database.go │ └── schema.sql │ ├── go.mod │ ├── go.sum │ ├── internal │ ├── contracts │ │ └── contracts.go │ ├── events │ │ └── payment_created.go │ ├── httpapi │ │ ├── handlers │ │ │ └── payments │ │ │ │ ├── payments.go │ │ │ │ └── register.go │ │ └── httpapi.go │ ├── repositories │ │ ├── paymentstore │ │ │ └── paymentstore.go │ │ └── repositories.go │ └── services │ │ ├── paymentsmanager │ │ └── paymentsmanager.go │ │ └── services.go │ ├── logger │ └── logger.go │ └── main.go ├── factory ├── configuration │ └── configuration.go ├── diagram.png ├── go.mod ├── main.go └── repository │ ├── mysql │ └── mysql.go │ ├── repository.go │ └── sqlserver │ └── sqlserver.go ├── faker-generator ├── data.db ├── entities │ ├── order.go │ └── user.go ├── generator │ └── generator.go ├── go.mod ├── go.sum ├── main.go └── repository │ ├── orders.go │ ├── repository.go │ └── users.go ├── generics ├── .vscode │ └── launch.json ├── go.mod ├── main.go └── redisclient │ └── redisclient.go ├── go-service-struct ├── README.md ├── advanced │ ├── Dockerfile │ ├── api │ │ └── main.go │ ├── config │ │ └── config.go │ ├── database │ │ └── database.go │ ├── go.mod │ ├── go.work │ ├── go.work.sum │ ├── internal │ │ ├── httpapi │ │ │ ├── handlers │ │ │ │ ├── auth │ │ │ │ │ ├── auth.go │ │ │ │ │ ├── authenticate.go │ │ │ │ │ ├── dtos.go │ │ │ │ │ └── refresh_token.go │ │ │ │ ├── handlers.go │ │ │ │ └── profiles │ │ │ │ │ ├── dtos.go │ │ │ │ │ ├── get.go │ │ │ │ │ ├── profiles.go │ │ │ │ │ └── update.go │ │ │ └── httpapi.go │ │ ├── listener │ │ │ ├── listener.go │ │ │ └── processors │ │ │ │ ├── auth │ │ │ │ ├── auth.go │ │ │ │ └── on_logout.go │ │ │ │ └── users │ │ │ │ ├── on_delete.go │ │ │ │ ├── on_update.go │ │ │ │ └── users.go │ │ ├── repositories │ │ │ ├── profiles │ │ │ │ └── profiles.go │ │ │ ├── repositories.go │ │ │ └── users │ │ │ │ └── users.go │ │ └── services │ │ │ ├── auth │ │ │ └── auth.go │ │ │ ├── profiles │ │ │ ├── get.go │ │ │ ├── profile.go │ │ │ └── update.go │ │ │ └── services.go │ └── worker │ │ ├── Dockerfile │ │ └── main.go ├── msgbroker │ ├── go.mod │ └── msgbroker.go ├── service.code-workspace └── simple │ ├── Dockerfile │ ├── config │ └── config.go │ ├── database │ └── database.go │ ├── go.mod │ ├── go.sum │ ├── internal │ ├── httpapi │ │ ├── handlers │ │ │ ├── auth │ │ │ │ ├── auth.go │ │ │ │ ├── authenticate.go │ │ │ │ ├── dtos.go │ │ │ │ └── refresh_token.go │ │ │ ├── handlers.go │ │ │ └── profiles │ │ │ │ ├── delete.go │ │ │ │ ├── dtos.go │ │ │ │ ├── get.go │ │ │ │ ├── profiles.go │ │ │ │ └── update.go │ │ └── httpapi.go │ ├── repositories │ │ ├── profiles │ │ │ └── profiles.go │ │ ├── repositories.go │ │ └── users │ │ │ └── users.go │ └── services │ │ ├── auth │ │ └── auth.go │ │ ├── profiles │ │ ├── get.go │ │ ├── profile.go │ │ └── update.go │ │ └── services.go │ └── main.go ├── go_basics ├── go.mod └── main.go ├── go_interfaces ├── go.mod ├── main.go └── users │ └── users.go ├── go_introduction ├── go.mod └── main.go ├── go_structs ├── go.mod ├── main.go └── users │ └── users.go ├── go_templates ├── go.mod ├── main.go └── templates │ ├── greeting.txt │ ├── greeting2.txt │ └── index.html ├── gorilla_mux ├── api │ ├── api.go │ ├── middleware.go │ └── routes.go ├── go.mod ├── go.sum ├── main.go └── topics.md ├── goroutines ├── data │ └── data.go ├── go.mod └── main.go ├── gotests ├── .env ├── .vscode │ └── launch.json ├── api │ ├── api.go │ ├── book_by_id.go │ ├── books_test.go │ ├── dto │ │ ├── allBooks.go │ │ ├── book.go │ │ └── saveBook.go │ ├── list_books.go │ ├── routes.go │ └── save_book.go ├── database │ └── database.go ├── go.mod ├── go.sum ├── internal │ └── books │ │ ├── books.go │ │ ├── books.repository.go │ │ ├── books.service.go │ │ ├── books.service_test.go │ │ ├── mock_BookService.go │ │ ├── mock_DBInteractor.go │ │ └── models │ │ └── book.go ├── main.go └── math │ ├── math.go │ └── math_test.go ├── http_client ├── go.mod └── main.go ├── http_server ├── go.mod ├── main.go ├── requirements.md └── server │ ├── handlers.go │ ├── routes.go │ └── server.go ├── httprouter ├── go.mod └── main.go ├── modd ├── engine │ ├── engine.go │ └── templates │ │ └── index.html ├── go.mod ├── go.sum ├── main.go ├── modd.conf └── myProject ├── movie-reviews ├── .env ├── .vscode │ ├── extensions.json │ └── launch.json ├── api │ ├── api.go │ ├── dto │ │ └── register_user.go │ ├── routes.go │ └── user.handler.go ├── database │ └── database.go ├── go.mod ├── go.sum ├── internal │ └── users │ │ ├── model │ │ └── users.go │ │ ├── repository │ │ └── users.repository.go │ │ ├── service │ │ └── users.service.go │ │ └── users.go ├── main.go ├── requirements.md ├── schema.sql └── settings │ └── settings.go ├── recoverpanic ├── go.mod ├── go.sum └── main.go ├── simple_cli ├── commands │ └── commands.go ├── expenses.txt ├── expenses │ └── expenses.go ├── expenses1.txt ├── go.mod ├── main.go └── requirements.md └── traefik ├── books ├── Dockerfile ├── docker-compose.yml ├── go.mod ├── go.sum └── main.go ├── docker-compose.yml └── songs ├── Dockerfile ├── docker-compose.yml ├── go.mod ├── go.sum └── main.go /README.md: -------------------------------------------------------------------------------- 1 | # go_examples 2 | This is a collection of examples used for my youtube channel "go simplified" 3 | -------------------------------------------------------------------------------- /apicache/go.mod: -------------------------------------------------------------------------------- 1 | module apicache 2 | 3 | go 1.22.0 4 | 5 | require ( 6 | github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874 // indirect 7 | github.com/bytedance/sonic v1.9.1 // indirect 8 | github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect 9 | github.com/gabriel-vasile/mimetype v1.4.2 // indirect 10 | github.com/gin-contrib/sse v0.1.0 // indirect 11 | github.com/gin-gonic/gin v1.9.1 // indirect 12 | github.com/go-playground/locales v0.14.1 // indirect 13 | github.com/go-playground/universal-translator v0.18.1 // indirect 14 | github.com/go-playground/validator/v10 v10.14.0 // indirect 15 | github.com/goccy/go-json v0.10.2 // indirect 16 | github.com/json-iterator/go v1.1.12 // indirect 17 | github.com/klauspost/cpuid/v2 v2.2.4 // indirect 18 | github.com/leodido/go-urn v1.2.4 // indirect 19 | github.com/mattn/go-isatty v0.0.19 // indirect 20 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 21 | github.com/modern-go/reflect2 v1.0.2 // indirect 22 | github.com/pelletier/go-toml/v2 v2.0.8 // indirect 23 | github.com/twitchyliquid64/golang-asm v0.15.1 // indirect 24 | github.com/ugorji/go/codec v1.2.11 // indirect 25 | golang.org/x/arch v0.3.0 // indirect 26 | golang.org/x/crypto v0.9.0 // indirect 27 | golang.org/x/net v0.10.0 // indirect 28 | golang.org/x/sys v0.8.0 // indirect 29 | golang.org/x/text v0.9.0 // indirect 30 | google.golang.org/protobuf v1.30.0 // indirect 31 | gopkg.in/yaml.v3 v3.0.1 // indirect 32 | ) 33 | -------------------------------------------------------------------------------- /apicache/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "net/http" 7 | "time" 8 | 9 | "github.com/bradfitz/gomemcache/memcache" 10 | ) 11 | 12 | var cache = memcache.New("localhost:11211") 13 | 14 | type Profile struct { 15 | Name string 16 | Email string 17 | } 18 | 19 | func logexecutionTime(d time.Time) { 20 | fmt.Printf("Execution time: %fs\n", time.Since(d).Seconds()) 21 | } 22 | 23 | func saveProfileToCache(id string, p Profile) error { 24 | bb, err := json.Marshal(p) 25 | if err != nil { 26 | return err 27 | } 28 | 29 | return cache.Set(&memcache.Item{ 30 | Key: id, 31 | Value: bb, 32 | Expiration: 5, 33 | }) 34 | } 35 | 36 | func getProfileFromCache(key string) Profile { 37 | var p Profile 38 | 39 | item, err := cache.Get(key) 40 | if err != nil { 41 | fmt.Println(err) 42 | return Profile{} 43 | } 44 | 45 | err = json.Unmarshal(item.Value, &p) 46 | if err != nil { 47 | fmt.Println(err) 48 | return Profile{} 49 | } 50 | 51 | return p 52 | } 53 | 54 | func handleGetProfile(w http.ResponseWriter, req *http.Request) { 55 | defer logexecutionTime(time.Now()) 56 | 57 | id := req.PathValue("id") 58 | 59 | fmt.Println("getting data from cache") 60 | p := getProfileFromCache(id) 61 | if p.Email != "" { 62 | fmt.Fprintf(w, "%+v", p) 63 | return 64 | } 65 | 66 | fmt.Println("getting data from DB") 67 | time.Sleep(time.Millisecond * 600) 68 | 69 | p = Profile{ 70 | Name: fmt.Sprintf("Jhon_%s", id), 71 | Email: fmt.Sprintf("%s@example.com", id), 72 | } 73 | 74 | saveProfileToCache(id, p) 75 | fmt.Fprintf(w, "%+v", p) 76 | } 77 | 78 | func main() { 79 | r := http.NewServeMux() 80 | r.HandleFunc("/users/{id}", handleGetProfile) 81 | http.ListenAndServe(":8080", r) 82 | } 83 | -------------------------------------------------------------------------------- /app_configuration/config.yml: -------------------------------------------------------------------------------- 1 | app: 2 | port: 3000 3 | debug: true 4 | Database: 5 | host: localhost 6 | port: 3306 7 | user: root 8 | password: 1234 9 | name: books 10 | Email: 11 | host: smtp.gmail.com 12 | port: 587 13 | user: "" 14 | password: "" 15 | -------------------------------------------------------------------------------- /app_configuration/configuration/configuration.go: -------------------------------------------------------------------------------- 1 | package configuration 2 | 3 | import ( 4 | "io/ioutil" 5 | 6 | "gopkg.in/yaml.v2" 7 | ) 8 | 9 | type Database struct { 10 | Host string `yaml:"host"` 11 | Port int `yaml:"port"` 12 | User string `yaml:"user"` 13 | Password string `yaml:"password"` 14 | Name string `yaml:"name"` 15 | } 16 | 17 | type Configuration struct { 18 | DB Database `yaml:"Database"` 19 | } 20 | 21 | func Load(filename string) (*Configuration, error) { 22 | config := &Configuration{} 23 | 24 | content, err := ioutil.ReadFile(filename) 25 | if err != nil { 26 | return nil, err 27 | } 28 | 29 | err = yaml.Unmarshal(content, config) 30 | if err != nil { 31 | return nil, err 32 | } 33 | 34 | return config, nil 35 | } 36 | -------------------------------------------------------------------------------- /app_configuration/database/database.go: -------------------------------------------------------------------------------- 1 | package database 2 | 3 | import ( 4 | "context" 5 | "database/sql" 6 | "fmt" 7 | 8 | "github.com/disturb16/go-examples/app-configuration/configuration" 9 | _ "github.com/go-sql-driver/mysql" 10 | ) 11 | 12 | func CreateConnection(ctx context.Context, config *configuration.Configuration) *sql.DB { 13 | connectionString := fmt.Sprintf( 14 | "%s:1234@tcp(%s:%d)/%s", 15 | config.DB.User, 16 | config.DB.Host, 17 | config.DB.Port, 18 | config.DB.Name, 19 | ) 20 | 21 | db, err := sql.Open("mysql", connectionString) 22 | if err != nil { 23 | panic(err) 24 | } 25 | 26 | err = db.PingContext(ctx) 27 | if err != nil { 28 | panic(err) 29 | } 30 | 31 | return db 32 | } 33 | -------------------------------------------------------------------------------- /app_configuration/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/disturb16/go-examples/app-configuration 2 | 3 | go 1.17 4 | 5 | require github.com/go-sql-driver/mysql v1.6.0 6 | 7 | require ( 8 | github.com/joho/godotenv v1.4.0 // indirect 9 | gopkg.in/yaml.v2 v2.4.0 10 | ) 11 | 12 | require github.com/go-yaml/yaml v2.1.0+incompatible // indirect 13 | -------------------------------------------------------------------------------- /app_configuration/go.sum: -------------------------------------------------------------------------------- 1 | github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= 2 | github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= 3 | github.com/go-yaml/yaml v2.1.0+incompatible h1:RYi2hDdss1u4YE7GwixGzWwVo47T8UQwnTLB6vQiq+o= 4 | github.com/go-yaml/yaml v2.1.0+incompatible/go.mod h1:w2MrLa16VYP0jy6N7M5kHaCkaLENm+P+Tv+MfurjSw0= 5 | github.com/joho/godotenv v1.4.0 h1:3l4+N6zfMWnkbPEXKng2o2/MR5mSwTrBih4ZEkkz1lg= 6 | github.com/joho/godotenv v1.4.0/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= 7 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 8 | gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= 9 | gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= 10 | -------------------------------------------------------------------------------- /app_configuration/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | 7 | "github.com/disturb16/go-examples/app-configuration/configuration" 8 | "github.com/disturb16/go-examples/app-configuration/database" 9 | ) 10 | 11 | func main() { 12 | var id int 13 | var err error 14 | ctx := context.Background() 15 | 16 | config, err := configuration.Load("config.yml") 17 | if err != nil { 18 | panic(err) 19 | } 20 | 21 | // create connection 22 | db := database.CreateConnection(ctx, config) 23 | defer db.Close() 24 | 25 | // test query 26 | err = db.QueryRowContext(ctx, "SELECT id from books").Scan(&id) 27 | if err != nil { 28 | panic(err) 29 | } 30 | 31 | fmt.Println(id) 32 | fmt.Println("All good") 33 | } 34 | -------------------------------------------------------------------------------- /app_configuration/topics.md: -------------------------------------------------------------------------------- 1 | # Métodos para cargar las configuraciones de aplicación 2 | 3 | 4 | - [ ] Variables de entorno desde la terminal 5 | - [ ] Variables de entorno desde archivo 6 | - [ ] Archivo de configuración yaml/json/env -------------------------------------------------------------------------------- /btrlogs/api/api.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "btrlogs/blog" 5 | "context" 6 | "net/http" 7 | 8 | "github.com/gin-gonic/gin" 9 | "github.com/google/uuid" 10 | ) 11 | 12 | func withRequestID() gin.HandlerFunc { 13 | return func(c *gin.Context) { 14 | requestID := c.Request.Header.Get(string(blog.RequestIDKey)) 15 | if requestID == "" { 16 | requestID = uuid.New().String() 17 | } 18 | 19 | ctx := c.Request.Context() 20 | ctx = context.WithValue(ctx, blog.RequestIDKey, requestID) 21 | 22 | req, _ := http.NewRequestWithContext( 23 | ctx, 24 | c.Request.Method, 25 | c.Request.URL.String(), 26 | c.Request.Body, 27 | ) 28 | 29 | c.Request = req 30 | c.Next() 31 | } 32 | } 33 | 34 | func NewRouter() *gin.Engine { 35 | r := gin.Default() 36 | r.Use(withRequestID()) 37 | 38 | r.GET("/", ListCountries) 39 | r.GET("/:short_name", GetCountry) 40 | 41 | return r 42 | } 43 | -------------------------------------------------------------------------------- /btrlogs/api/countries.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "btrlogs/blog" 5 | "log/slog" 6 | "math/rand" 7 | "net/http" 8 | 9 | "github.com/gin-gonic/gin" 10 | ) 11 | 12 | type Country struct { 13 | Name string `json:"name"` 14 | ShortName string `json:"short_name"` 15 | } 16 | 17 | var allCountries = []Country{ 18 | { 19 | Name: "United States of America", 20 | ShortName: "USA", 21 | }, 22 | { 23 | Name: "United Kingdom", 24 | ShortName: "UK", 25 | }, 26 | { 27 | Name: "Canada", 28 | ShortName: "CA", 29 | }, 30 | } 31 | 32 | func ListCountries(c *gin.Context) { 33 | ctx := c.Request.Context() 34 | logger := blog.New(ctx) 35 | 36 | // Simulate an error 40% of the time 37 | if rand.Intn(100) > 60 { 38 | 39 | logger.Error("error listing countries", slog.String("error", "simulated error")) 40 | c.JSON(http.StatusInternalServerError, "error") 41 | return 42 | } 43 | 44 | c.JSON(http.StatusOK, allCountries) 45 | } 46 | 47 | func GetCountry(c *gin.Context) { 48 | ctx := c.Request.Context() 49 | logger := blog.New(ctx) 50 | 51 | shortName := c.Params.ByName("short_name") 52 | 53 | for _, country := range allCountries { 54 | if country.ShortName == shortName { 55 | c.JSON(http.StatusOK, country) 56 | return 57 | } 58 | } 59 | 60 | logger.Error("could not found country", slog.String("short_name", shortName)) 61 | c.JSON(http.StatusNotFound, "country not found") 62 | } 63 | -------------------------------------------------------------------------------- /btrlogs/blog/blog.go: -------------------------------------------------------------------------------- 1 | package blog 2 | 3 | import ( 4 | "context" 5 | "log/slog" 6 | "os" 7 | ) 8 | 9 | type Key string 10 | 11 | var RequestIDKey Key = "X-Request-ID" 12 | 13 | func New(ctx context.Context) *slog.Logger { 14 | requestID := ctx.Value(RequestIDKey) 15 | if requestID == nil { 16 | requestID = "unknown" 17 | } 18 | 19 | h := slog.NewJSONHandler(os.Stdout, 20 | &slog.HandlerOptions{ 21 | Level: slog.LevelDebug, 22 | }).WithAttrs( 23 | []slog.Attr{ 24 | slog.String("app", "btrlogs"), 25 | slog.String("request_id", requestID.(string)), 26 | }) 27 | 28 | return slog.New(h) 29 | } 30 | -------------------------------------------------------------------------------- /btrlogs/go.mod: -------------------------------------------------------------------------------- 1 | module btrlogs 2 | 3 | go 1.21 4 | 5 | require github.com/gin-gonic/gin v1.9.1 6 | 7 | require ( 8 | github.com/bytedance/sonic v1.10.2 // indirect 9 | github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect 10 | github.com/chenzhuoyu/iasm v0.9.1 // indirect 11 | github.com/gabriel-vasile/mimetype v1.4.3 // indirect 12 | github.com/gin-contrib/sse v0.1.0 // indirect 13 | github.com/go-playground/locales v0.14.1 // indirect 14 | github.com/go-playground/universal-translator v0.18.1 // indirect 15 | github.com/go-playground/validator/v10 v10.16.0 // indirect 16 | github.com/goccy/go-json v0.10.2 // indirect 17 | github.com/google/uuid v1.5.0 18 | github.com/json-iterator/go v1.1.12 // indirect 19 | github.com/klauspost/cpuid/v2 v2.2.6 // indirect 20 | github.com/leodido/go-urn v1.2.4 // indirect 21 | github.com/mattn/go-isatty v0.0.20 // indirect 22 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 23 | github.com/modern-go/reflect2 v1.0.2 // indirect 24 | github.com/pelletier/go-toml/v2 v2.1.1 // indirect 25 | github.com/twitchyliquid64/golang-asm v0.15.1 // indirect 26 | github.com/ugorji/go/codec v1.2.12 // indirect 27 | golang.org/x/arch v0.6.0 // indirect 28 | golang.org/x/crypto v0.17.0 // indirect 29 | golang.org/x/net v0.19.0 // indirect 30 | golang.org/x/sys v0.15.0 // indirect 31 | golang.org/x/text v0.14.0 // indirect 32 | google.golang.org/protobuf v1.32.0 // indirect 33 | gopkg.in/yaml.v3 v3.0.1 // indirect 34 | ) 35 | -------------------------------------------------------------------------------- /btrlogs/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "btrlogs/api" 5 | ) 6 | 7 | func main() { 8 | r := api.NewRouter() 9 | 10 | r.Run(":8080") 11 | } 12 | -------------------------------------------------------------------------------- /chan-listener/go.mod: -------------------------------------------------------------------------------- 1 | module chan-listener 2 | 3 | go 1.20 4 | -------------------------------------------------------------------------------- /chan-listener/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "chan-listener/messanger" 5 | "log" 6 | "math/rand" 7 | "time" 8 | ) 9 | 10 | type Person struct { 11 | ID int 12 | Name string 13 | } 14 | 15 | type Animal struct { 16 | ID int 17 | Name string 18 | } 19 | 20 | func handler1(msg any) error { 21 | log.Println("Message received from topic [persons]: ", msg) 22 | return nil 23 | } 24 | 25 | func handler2(msg any) error { 26 | log.Println("Message received from topic [animals]: ", msg) 27 | return nil 28 | } 29 | 30 | func main() { 31 | m := messanger.New() 32 | 33 | m.AddHandler(handler1, "persons") 34 | m.AddHandler(handler2, "animals") 35 | 36 | go func() { 37 | for { 38 | rand.NewSource(time.Now().UnixNano()) 39 | num := rand.Intn(10) 40 | 41 | if num%2 == 0 { 42 | m.SendMessage(messanger.Message{ 43 | Topic: "persons", 44 | Data: Person{ 45 | ID: num, 46 | Name: "Jhon", 47 | }, 48 | }) 49 | } else { 50 | m.SendMessage(messanger.Message{ 51 | Topic: "animals", 52 | Data: Animal{ 53 | ID: num, 54 | Name: "Dog", 55 | }, 56 | }) 57 | } 58 | } 59 | }() 60 | 61 | go m.Start() 62 | 63 | time.Sleep(3 * time.Second) 64 | } 65 | -------------------------------------------------------------------------------- /chan-listener/messanger/messanger.go: -------------------------------------------------------------------------------- 1 | package messanger 2 | 3 | import ( 4 | "log" 5 | "sync" 6 | ) 7 | 8 | type HandlerFunc func(any) error 9 | 10 | type Message struct { 11 | Topic string 12 | Data any 13 | } 14 | 15 | type Messanger struct { 16 | mainChan chan Message 17 | handlers map[string]HandlerFunc 18 | buffer []Message 19 | wg *sync.WaitGroup 20 | isRunning bool 21 | } 22 | 23 | func New() *Messanger { 24 | return &Messanger{ 25 | mainChan: make(chan Message), 26 | handlers: map[string]HandlerFunc{}, 27 | } 28 | } 29 | 30 | func (m *Messanger) AddHandler(h HandlerFunc, topic string) { 31 | m.handlers[topic] = h 32 | } 33 | 34 | func (m *Messanger) SendMessage(msg Message) { 35 | if !m.isRunning { 36 | m.buffer = append(m.buffer, msg) 37 | return 38 | } 39 | 40 | m.mainChan <- msg 41 | } 42 | 43 | func (m *Messanger) Listen() { 44 | for msg := range m.mainChan { 45 | h := m.handlers[msg.Topic] 46 | err := h(msg) 47 | if err != nil { 48 | log.Panic(err) 49 | } 50 | } 51 | } 52 | 53 | func (m *Messanger) Start() { 54 | m.wg = &sync.WaitGroup{} 55 | m.wg.Add(1) 56 | go m.Listen() 57 | 58 | m.isRunning = true 59 | 60 | for _, msg := range m.buffer { 61 | m.mainChan <- msg 62 | } 63 | 64 | m.wg.Wait() 65 | } 66 | 67 | func (m *Messanger) Stop() { 68 | close(m.mainChan) 69 | m.isRunning = false 70 | m.wg.Done() 71 | } 72 | -------------------------------------------------------------------------------- /channels/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/disturb16/go_examples/channels 2 | 3 | go 1.17 4 | 5 | require github.com/google/uuid v1.3.0 // indirect 6 | -------------------------------------------------------------------------------- /channels/go.sum: -------------------------------------------------------------------------------- 1 | github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= 2 | github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 3 | -------------------------------------------------------------------------------- /channels/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | 7 | "github.com/google/uuid" 8 | ) 9 | 10 | func main() { 11 | 12 | wg := &sync.WaitGroup{} 13 | IDsChan := make(chan string) 14 | FakeIDsChan := make(chan string) 15 | closedChans := make(chan int) 16 | 17 | wg.Add(3) 18 | 19 | go generateIDS(wg, IDsChan, closedChans) 20 | go generateFakeIDs(wg, FakeIDsChan, closedChans) 21 | 22 | go logIDs(wg, IDsChan, FakeIDsChan, closedChans) 23 | 24 | wg.Wait() 25 | } 26 | 27 | func generateFakeIDs(wg *sync.WaitGroup, fakeIDsChan chan<- string, closedChannels chan<- int) { 28 | for i := 0; i < 100; i++ { 29 | id := uuid.New() 30 | fakeIDsChan <- fmt.Sprintf("%d. %s", i+1, id.String()) 31 | } 32 | 33 | close(fakeIDsChan) 34 | closedChannels <- 1 35 | 36 | wg.Done() 37 | } 38 | 39 | func generateIDS(wg *sync.WaitGroup, idsChan chan<- string, closedChannels chan<- int) { 40 | 41 | for i := 0; i < 100; i++ { 42 | id := uuid.New() 43 | idsChan <- fmt.Sprintf("%d. %s", i+1, id.String()) 44 | } 45 | 46 | close(idsChan) 47 | closedChannels <- 1 48 | 49 | wg.Done() 50 | } 51 | 52 | func logIDs(wg *sync.WaitGroup, idsChan <-chan string, fakeIDsChan <-chan string, closedChannels chan int) { 53 | 54 | closedCounter := 0 55 | 56 | for { 57 | select { 58 | case id, ok := <-idsChan: 59 | if ok { 60 | fmt.Println("ID:", id) 61 | } 62 | 63 | case id, ok := <-fakeIDsChan: 64 | if ok { 65 | fmt.Println("FAKE ID:", id) 66 | } 67 | 68 | case count, ok := <-closedChannels: 69 | if ok { 70 | closedCounter += count 71 | } 72 | } 73 | 74 | if closedCounter == 2 { 75 | close(closedChannels) 76 | break 77 | } 78 | } 79 | 80 | wg.Done() 81 | } 82 | -------------------------------------------------------------------------------- /context/context tree.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/disturb16/go_examples/57ca3799913e394df9810a4a3709bd6c0f197097/context/context tree.png -------------------------------------------------------------------------------- /context/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/user/context 2 | 3 | go 1.17 4 | -------------------------------------------------------------------------------- /context/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/user/context/values" 7 | ) 8 | 9 | func main() { 10 | ctx := context.Background() 11 | ctx = context.WithValue(ctx, "name", "John") 12 | ctx = context.WithValue(ctx, "age", "30") 13 | 14 | values.PrintValues(ctx) 15 | } 16 | -------------------------------------------------------------------------------- /context/message/message.go: -------------------------------------------------------------------------------- 1 | package message 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "time" 7 | ) 8 | 9 | // PrintMessage prints a message after the given delay. 10 | func PrintMessage(ctx context.Context, d time.Duration, msg string) { 11 | select { 12 | case <-time.After(d): 13 | fmt.Println(msg) 14 | 15 | case <-ctx.Done(): 16 | fmt.Println("context is done") 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /context/server/middleware.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import "net/http" 4 | 5 | func Middleware(handler http.HandlerFunc) http.HandlerFunc { 6 | return func(w http.ResponseWriter, r *http.Request) { 7 | 8 | handler(w, r) 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /context/server/server.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | "time" 7 | ) 8 | 9 | func New() *http.Server { 10 | return &http.Server{ 11 | Addr: ":8080", 12 | Handler: http.HandlerFunc(handler), 13 | } 14 | } 15 | 16 | func handler(w http.ResponseWriter, r *http.Request) { 17 | ctx := r.Context() 18 | 19 | select { 20 | case <-ctx.Done(): 21 | fmt.Println("context cancelled ") 22 | case <-time.After(5 * time.Second): 23 | w.Write([]byte("Hello, World!")) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /context/users/users.go: -------------------------------------------------------------------------------- 1 | package users 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "time" 7 | ) 8 | 9 | type User struct { 10 | Id int64 `json:"id"` 11 | Username string `json:"username"` 12 | } 13 | 14 | var users []User = []User{ 15 | {Id: 1, Username: "bob"}, 16 | {Id: 2, Username: "alice"}, 17 | {Id: 3, Username: "Mark"}, 18 | } 19 | 20 | // ListUsers prints one line for each user every second. 21 | func ListUsers(ctx context.Context, ch chan<- bool) { 22 | for _, user := range users { 23 | contextIsDone := false 24 | 25 | // wait for one second or until the context is done 26 | select { 27 | case <-ctx.Done(): 28 | contextIsDone = true 29 | break 30 | case <-time.After(time.Second): 31 | fmt.Println(user) 32 | } 33 | 34 | // if the context is done, break out of the loop 35 | if contextIsDone { 36 | fmt.Println("context done") 37 | ch <- true 38 | break 39 | } 40 | } 41 | 42 | ch <- true 43 | } 44 | -------------------------------------------------------------------------------- /context/values/values.go: -------------------------------------------------------------------------------- 1 | package values 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | ) 7 | 8 | func PrintValues(ctx context.Context) { 9 | 10 | name, ok := ctx.Value("name").(string) 11 | if !ok { 12 | fmt.Println("name not found") 13 | return 14 | } 15 | 16 | age, ok := ctx.Value("age").(int) 17 | if !ok { 18 | fmt.Println("age not found") 19 | return 20 | } 21 | 22 | fmt.Println("name:", name) 23 | fmt.Println("age:", age) 24 | } 25 | -------------------------------------------------------------------------------- /db_conn/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/user/db-conn 2 | 3 | go 1.17 4 | 5 | require github.com/go-sql-driver/mysql v1.6.0 6 | -------------------------------------------------------------------------------- /db_conn/go.sum: -------------------------------------------------------------------------------- 1 | github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= 2 | github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= 3 | -------------------------------------------------------------------------------- /db_conn/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "database/sql" 6 | "fmt" 7 | "time" 8 | 9 | _ "github.com/go-sql-driver/mysql" 10 | ) 11 | 12 | type Author struct { 13 | ID int64 14 | Name string 15 | } 16 | 17 | type Book struct { 18 | ID int64 19 | Title string 20 | AuthorID string 21 | PublishDate time.Time 22 | Author Author 23 | } 24 | 25 | func main() { 26 | 27 | ctx := context.Background() 28 | 29 | db, err := createConnection() 30 | if err != nil { 31 | panic(err) 32 | } 33 | 34 | err = queryBooks(ctx, db) 35 | if err != nil { 36 | panic(err) 37 | } 38 | 39 | db.Close() 40 | } 41 | 42 | func createConnection() (*sql.DB, error) { 43 | connectionString := "root:1234@tcp(localhost:3306)/books?parseTime=True" 44 | 45 | db, err := sql.Open("mysql", connectionString) 46 | if err != nil { 47 | return nil, err 48 | } 49 | 50 | db.SetMaxOpenConns(5) 51 | 52 | err = db.Ping() 53 | if err != nil { 54 | return nil, err 55 | } 56 | 57 | return db, nil 58 | } 59 | 60 | func queryBooks(ctx context.Context, db *sql.DB) error { 61 | qry := ` 62 | select 63 | b.id, 64 | b.title, 65 | b.author_id, 66 | a.name as 'author', 67 | b.publish_date 68 | from books b 69 | inner join authors a 70 | on a.id = b.author_id` 71 | 72 | rows, err := db.QueryContext(ctx, qry) 73 | if err != nil { 74 | return err 75 | } 76 | 77 | books := []Book{} 78 | 79 | for rows.Next() { 80 | b := Book{} 81 | 82 | err = rows.Scan(&b.ID, &b.Title, &b.AuthorID, &b.Author.Name, &b.PublishDate) 83 | if err != nil { 84 | return err 85 | } 86 | 87 | books = append(books, b) 88 | } 89 | 90 | fmt.Println(books) 91 | 92 | return nil 93 | } 94 | 95 | func addBook(ctx context.Context, db *sql.DB, title string, authorID int64, publishDate time.Time) error { 96 | 97 | qryadd := `insert into books(title, author_id, publish_date) values (?,?,?)` 98 | 99 | result, err := db.ExecContext(ctx, qryadd, title, authorID, publishDate) 100 | if err != nil { 101 | return err 102 | } 103 | 104 | id, err := result.LastInsertId() 105 | if err != nil { 106 | return err 107 | } 108 | 109 | fmt.Println("INSERT ID:", id) 110 | 111 | return nil 112 | } 113 | -------------------------------------------------------------------------------- /dependency_injection/config.yml: -------------------------------------------------------------------------------- 1 | app: 2 | port: 3000 3 | debug: true 4 | Database: 5 | host: localhost 6 | port: 3306 7 | user: root 8 | password: 1234 9 | name: users 10 | -------------------------------------------------------------------------------- /dependency_injection/configuration/configuration.go: -------------------------------------------------------------------------------- 1 | package configuration 2 | 3 | import ( 4 | "io/ioutil" 5 | 6 | "gopkg.in/yaml.v2" 7 | ) 8 | 9 | type Database struct { 10 | Host string `yaml:"host"` 11 | Port int `yaml:"port"` 12 | User string `yaml:"user"` 13 | Password string `yaml:"password"` 14 | Name string `yaml:"name"` 15 | } 16 | 17 | type Configuration struct { 18 | DB Database `yaml:"Database"` 19 | } 20 | 21 | const defaultPath string = "config.yml" 22 | 23 | func Load(filename string) (*Configuration, error) { 24 | config := &Configuration{} 25 | 26 | content, err := ioutil.ReadFile(filename) 27 | if err != nil { 28 | return nil, err 29 | } 30 | 31 | err = yaml.Unmarshal(content, config) 32 | if err != nil { 33 | return nil, err 34 | } 35 | 36 | return config, nil 37 | } 38 | 39 | func New() (*Configuration, error) { 40 | return Load(defaultPath) 41 | } 42 | -------------------------------------------------------------------------------- /dependency_injection/database/database.go: -------------------------------------------------------------------------------- 1 | package database 2 | 3 | import ( 4 | "context" 5 | "database/sql" 6 | "fmt" 7 | "log" 8 | 9 | "github.com/disturb16/go-examples/dependency-injection/configuration" 10 | _ "github.com/mattn/go-sqlite3" 11 | ) 12 | 13 | func CreateSqliteConnection(ctx context.Context, config *configuration.Configuration) (*sql.DB, error) { 14 | log.Println("Connecting to database...") 15 | 16 | source := fmt.Sprintf("./%s.db", config.DB.Name) 17 | db, err := sql.Open("sqlite3", source) 18 | if err != nil { 19 | return nil, err 20 | } 21 | 22 | err = db.PingContext(ctx) 23 | if err != nil { 24 | return nil, err 25 | } 26 | 27 | log.Println("Connected to database") 28 | return db, nil 29 | } 30 | -------------------------------------------------------------------------------- /dependency_injection/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/disturb16/go-examples/dependency-injection 2 | 3 | go 1.17 4 | 5 | require ( 6 | github.com/mattn/go-sqlite3 v1.14.9 7 | gopkg.in/yaml.v2 v2.4.0 8 | ) 9 | 10 | require ( 11 | go.uber.org/atomic v1.6.0 // indirect 12 | go.uber.org/dig v1.12.0 // indirect 13 | go.uber.org/fx v1.16.0 // indirect 14 | go.uber.org/multierr v1.5.0 // indirect 15 | go.uber.org/zap v1.16.0 // indirect 16 | golang.org/x/sys v0.0.0-20210903071746-97244b99971b // indirect 17 | ) 18 | -------------------------------------------------------------------------------- /dependency_injection/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "database/sql" 6 | "fmt" 7 | 8 | "github.com/disturb16/go-examples/dependency-injection/configuration" 9 | "github.com/disturb16/go-examples/dependency-injection/database" 10 | "github.com/disturb16/go-examples/dependency-injection/repository" 11 | "go.uber.org/fx" 12 | ) 13 | 14 | func main() { 15 | app := fx.New( 16 | fx.Provide( 17 | context.Background, 18 | configuration.New, 19 | database.CreateSqliteConnection, 20 | repository.New, 21 | ), 22 | fx.Invoke( 23 | configureLifyeCycleHooks, 24 | ), 25 | ) 26 | 27 | app.Run() 28 | } 29 | 30 | func configureLifyeCycleHooks(lc fx.Lifecycle, db *sql.DB, repo *repository.Repository) { 31 | lc.Append( 32 | fx.Hook{ 33 | OnStart: func(c context.Context) error { 34 | fmt.Println("Starting application") 35 | 36 | err := repo.SaveUser(c, "Charles", "Doe") 37 | if err != nil { 38 | return err 39 | } 40 | 41 | uu, err := repo.GetUsers(c) 42 | if err != nil { 43 | return err 44 | } 45 | 46 | fmt.Println(uu) 47 | 48 | return nil 49 | }, 50 | 51 | OnStop: func(c context.Context) error { 52 | fmt.Println("Closing database connection") 53 | return db.Close() 54 | }, 55 | }, 56 | ) 57 | } 58 | -------------------------------------------------------------------------------- /dependency_injection/repository/repository.go: -------------------------------------------------------------------------------- 1 | package repository 2 | 3 | import ( 4 | "context" 5 | "database/sql" 6 | "fmt" 7 | 8 | "github.com/disturb16/go-examples/dependency-injection/configuration" 9 | ) 10 | 11 | type Repository struct { 12 | db *sql.DB 13 | } 14 | 15 | const schemaQuery string = ` 16 | create table if not exists users 17 | ( 18 | ID INTEGER PRIMARY KEY AUTOINCREMENT, 19 | FirstName TEXT, 20 | LastName TEXT 21 | )` 22 | 23 | func New(ctx context.Context, config *configuration.Configuration, db *sql.DB) (*Repository, error) { 24 | 25 | // Populate schema 26 | _, err := db.ExecContext(ctx, schemaQuery) 27 | if err != nil { 28 | return nil, err 29 | } 30 | 31 | fmt.Println(config.DB.Name) 32 | 33 | return &Repository{db}, nil 34 | } 35 | -------------------------------------------------------------------------------- /dependency_injection/repository/users.go: -------------------------------------------------------------------------------- 1 | package repository 2 | 3 | import "context" 4 | 5 | type User struct { 6 | ID int64 7 | FirstName string 8 | LastName string 9 | } 10 | 11 | // GetUserByID returns a user by id 12 | func (r *Repository) GetUserByID(ctx context.Context, id int64) (*User, error) { 13 | 14 | u := &User{} 15 | row := r.db.QueryRowContext(ctx, "SELECT ID, FirstName, LastName FROM users WHERE id = ?", id) 16 | 17 | err := row.Scan(u.ID, u.FirstName, u.LastName) 18 | if err != nil { 19 | return nil, err 20 | } 21 | 22 | return u, nil 23 | } 24 | 25 | // GetUsers returns all users 26 | func (r *Repository) GetUsers(ctx context.Context) ([]*User, error) { 27 | 28 | users := []*User{} 29 | 30 | rows, err := r.db.QueryContext(ctx, "SELECT ID, FirstName, LastName FROM users") 31 | if err != nil { 32 | return nil, err 33 | } 34 | 35 | for rows.Next() { 36 | u := User{} 37 | err := rows.Scan(&u.ID, &u.FirstName, &u.LastName) 38 | if err != nil { 39 | return nil, err 40 | } 41 | users = append(users, &u) 42 | } 43 | 44 | return users, nil 45 | } 46 | 47 | // SaveUser saves a user 48 | func (r *Repository) SaveUser(ctx context.Context, firstName, lastName string) error { 49 | _, err := r.db.ExecContext( 50 | ctx, 51 | "INSERT INTO users (FirstName, LastName) VALUES (?, ?)", 52 | firstName, 53 | lastName, 54 | ) 55 | if err != nil { 56 | return err 57 | } 58 | 59 | return nil 60 | } 61 | -------------------------------------------------------------------------------- /dockerization/.dockerignore: -------------------------------------------------------------------------------- 1 | **/.classpath 2 | **/.dockerignore 3 | **/.env 4 | **/.git 5 | **/.gitignore 6 | **/.project 7 | **/.settings 8 | **/.toolstarget 9 | **/.vs 10 | **/.vscode 11 | **/*.*proj.user 12 | **/*.dbmdl 13 | **/*.jfm 14 | **/bin 15 | **/charts 16 | **/docker-compose* 17 | **/compose* 18 | **/Dockerfile* 19 | **/node_modules 20 | **/npm-debug.log 21 | **/obj 22 | **/secrets.dev.yaml 23 | **/values.dev.yaml 24 | LICENSE 25 | README.md 26 | -------------------------------------------------------------------------------- /dockerization/Dockerfile: -------------------------------------------------------------------------------- 1 | #build stage 2 | FROM golang:1.19.3-alpine3.17 AS builder 3 | RUN apk add --no-cache git upx 4 | 5 | WORKDIR /app 6 | 7 | COPY ["go.mod", "go.sum", "./"] 8 | RUN go mod download -x 9 | 10 | COPY . . 11 | 12 | RUN go build \ 13 | -ldflags="-s -w" \ 14 | -o app -v . 15 | 16 | RUN upx app 17 | 18 | #final stage 19 | FROM alpine:3.17 20 | LABEL Name=dockerization 21 | 22 | RUN apk update 23 | RUN apk --no-cache add ca-certificates 24 | 25 | WORKDIR /app 26 | 27 | COPY --from=builder /app . 28 | 29 | ENTRYPOINT ["./app"] 30 | -------------------------------------------------------------------------------- /dockerization/api/api.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/labstack/echo/v4" 7 | ) 8 | 9 | type API struct{} 10 | 11 | type BooksParams struct { 12 | Offset int `query:"offset"` 13 | Limit int `query:"limit"` 14 | } 15 | 16 | type BookIdParams struct { 17 | ID int `param:"id"` 18 | } 19 | 20 | type PostBook struct { 21 | Title string `json:"title"` 22 | } 23 | 24 | var ( 25 | books = []string{"Book 1", "Book 2", "Book 3"} 26 | ) 27 | 28 | func (a *API) getBooks(c echo.Context) error { 29 | params := &BooksParams{} 30 | 31 | err := c.Bind(params) 32 | if err != nil { 33 | return c.JSON(http.StatusBadRequest, "Invalid query params") 34 | } 35 | 36 | if params.Offset > len(books) || params.Offset < 0 { 37 | return c.JSON(http.StatusBadRequest, "Invalid query params") 38 | } 39 | 40 | if params.Limit < 0 || params.Limit > len(books) { 41 | return c.JSON(http.StatusBadRequest, "Invalid query params") 42 | } 43 | 44 | var from, to int 45 | 46 | if params.Offset > 0 { 47 | from = params.Offset 48 | } 49 | 50 | if params.Limit > 0 { 51 | to = params.Limit 52 | } else { 53 | to = len(books) 54 | } 55 | 56 | return c.JSON(http.StatusOK, books[from:to]) 57 | } 58 | 59 | func (a *API) getBook(c echo.Context) error { 60 | 61 | params := &BookIdParams{} 62 | 63 | err := c.Bind(params) 64 | if err != nil { 65 | return c.JSON(http.StatusBadRequest, "Invalid parameters") 66 | } 67 | 68 | index := params.ID - 1 69 | 70 | if index < 0 || index > len(books)-1 { 71 | return c.JSON(http.StatusBadRequest, "Invalid parameters") 72 | } 73 | 74 | return c.JSON(http.StatusOK, books[index]) 75 | } 76 | 77 | func (a *API) postBook(c echo.Context) error { 78 | book := &PostBook{} 79 | 80 | err := c.Bind(book) 81 | if err != nil { 82 | return c.JSON(http.StatusBadRequest, "Invalid parameters") 83 | } 84 | 85 | books = append(books, book.Title) 86 | return c.NoContent(http.StatusCreated) 87 | 88 | } 89 | -------------------------------------------------------------------------------- /dockerization/api/middleware.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/google/uuid" 7 | "github.com/labstack/echo/v4" 8 | ) 9 | 10 | var users = map[string]string{"user1": "password1", "user2": "password2"} 11 | 12 | func requestIDHandler(next echo.HandlerFunc) echo.HandlerFunc { 13 | return func(c echo.Context) error { 14 | 15 | requestID := c.Request().Header.Get("X-Request-ID") 16 | 17 | if len(requestID) == 0 { 18 | requestID = uuid.New().String() 19 | } 20 | 21 | c.Response().Header().Set("X-Request-ID", requestID) 22 | return next(c) 23 | } 24 | } 25 | 26 | func authMiddleware(next echo.HandlerFunc) echo.HandlerFunc { 27 | return func(c echo.Context) error { 28 | 29 | user := c.Request().Header.Get("Authorization") 30 | 31 | if users[user] == "" { 32 | return c.JSON(http.StatusUnauthorized, "Unauthorized") 33 | } 34 | 35 | return next(c) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /dockerization/api/routes.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "github.com/labstack/echo/v4" 5 | ) 6 | 7 | func (a *API) RegisterRoutes(e *echo.Echo) { 8 | 9 | e.Use(requestIDHandler) 10 | 11 | public := e.Group("") 12 | protected := e.Group("", authMiddleware) 13 | 14 | public.GET("/books", a.getBooks) 15 | public.GET("/books/:id", a.getBook) 16 | 17 | protected.POST("/books", a.postBook) 18 | } 19 | -------------------------------------------------------------------------------- /dockerization/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/disturb16/go_examples/dockerization 2 | 3 | go 1.19 4 | 5 | require ( 6 | github.com/disturb16/go_examples/echo v0.0.0-20220321063234-189826d35d20 7 | github.com/google/uuid v1.3.0 8 | github.com/labstack/echo/v4 v4.6.3 9 | ) 10 | 11 | require ( 12 | github.com/labstack/gommon v0.3.1 // indirect 13 | github.com/mattn/go-colorable v0.1.11 // indirect 14 | github.com/mattn/go-isatty v0.0.14 // indirect 15 | github.com/valyala/bytebufferpool v1.0.0 // indirect 16 | github.com/valyala/fasttemplate v1.2.1 // indirect 17 | golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 // indirect 18 | golang.org/x/net v0.0.0-20210913180222-943fd674d43e // indirect 19 | golang.org/x/sys v0.0.0-20211103235746-7861aae1554b // indirect 20 | golang.org/x/text v0.3.7 // indirect 21 | ) 22 | -------------------------------------------------------------------------------- /dockerization/go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 2 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 3 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 4 | github.com/disturb16/go_examples/echo v0.0.0-20220321063234-189826d35d20 h1:G7/g1vno50VcsKUStKi8uq5plpCtzc7n7pEzB/+2UNs= 5 | github.com/disturb16/go_examples/echo v0.0.0-20220321063234-189826d35d20/go.mod h1:pIlw96p3WPP62JZTVzhZ27MeKGsEnfU9oM/OTsA3BaA= 6 | github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= 7 | github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= 8 | github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 9 | github.com/gorilla/schema v1.2.0/go.mod h1:kgLaKoK1FELgZqMAVxx/5cbj0kT+57qxUrAlIO2eleU= 10 | github.com/labstack/echo/v4 v4.6.3 h1:VhPuIZYxsbPmo4m9KAkMU/el2442eB7EBFFhNTTT9ac= 11 | github.com/labstack/echo/v4 v4.6.3/go.mod h1:Hk5OiHj0kDqmFq7aHe7eDqI7CUhuCrfpupQtLGGLm7A= 12 | github.com/labstack/gommon v0.3.1 h1:OomWaJXm7xR6L1HmEtGyQf26TEn7V6X88mktX9kee9o= 13 | github.com/labstack/gommon v0.3.1/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM= 14 | github.com/mattn/go-colorable v0.1.11 h1:nQ+aFkoE2TMGc0b68U2OKSexC+eq46+XwZzWXHRmPYs= 15 | github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= 16 | github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= 17 | github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= 18 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 19 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 20 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 21 | github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= 22 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 23 | github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= 24 | github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= 25 | github.com/valyala/fasttemplate v1.2.1 h1:TVEnxayobAdVkhQfrfes2IzOB6o+z4roRkPF52WA1u4= 26 | github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= 27 | golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 h1:HWj/xjIHfjYU5nVXpTM0s39J9CbLn7Cc5a7IC5rwsMQ= 28 | golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 29 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 30 | golang.org/x/net v0.0.0-20210913180222-943fd674d43e h1:+b/22bPvDYt4NPDcy4xAGCmON713ONAWFeY3Z7I3tR8= 31 | golang.org/x/net v0.0.0-20210913180222-943fd674d43e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 32 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 33 | golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 34 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 35 | golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 36 | golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 37 | golang.org/x/sys v0.0.0-20211103235746-7861aae1554b h1:1VkfZQv42XQlA/jchYumAnv1UPo6RgF9rJFkTgZIxO4= 38 | golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 39 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 40 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 41 | golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 42 | golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= 43 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 44 | golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 45 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 46 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 47 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 48 | gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= 49 | gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 50 | -------------------------------------------------------------------------------- /dockerization/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/disturb16/go_examples/echo/api" 7 | "github.com/labstack/echo/v4" 8 | ) 9 | 10 | func handleIndex(c echo.Context) error { 11 | return c.JSON(http.StatusOK, map[string]string{"message": "Hello World"}) 12 | } 13 | 14 | func main() { 15 | e := echo.New() 16 | e.GET("/", handleIndex) 17 | 18 | a := &api.API{} 19 | a.RegisterRoutes(e) 20 | 21 | e.Logger.Fatal(e.Start(":8080")) 22 | } 23 | -------------------------------------------------------------------------------- /echo/api/api.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/labstack/echo/v4" 7 | ) 8 | 9 | type API struct{} 10 | 11 | type BooksParams struct { 12 | Offset int `query:"offset"` 13 | Limit int `query:"limit"` 14 | } 15 | 16 | type BookIdParams struct { 17 | ID int `param:"id"` 18 | } 19 | 20 | type PostBook struct { 21 | Title string `json:"title"` 22 | } 23 | 24 | var ( 25 | books = []string{"Book 1", "Book 2", "Book 3"} 26 | ) 27 | 28 | func (a *API) getBooks(c echo.Context) error { 29 | params := &BooksParams{} 30 | 31 | err := c.Bind(params) 32 | if err != nil { 33 | return c.JSON(http.StatusBadRequest, "Invalid query params") 34 | } 35 | 36 | if params.Offset > len(books) || params.Offset < 0 { 37 | return c.JSON(http.StatusBadRequest, "Invalid query params") 38 | } 39 | 40 | if params.Limit < 0 || params.Limit > len(books) { 41 | return c.JSON(http.StatusBadRequest, "Invalid query params") 42 | } 43 | 44 | var from, to int 45 | 46 | if params.Offset > 0 { 47 | from = params.Offset 48 | } 49 | 50 | if params.Limit > 0 { 51 | to = params.Limit 52 | } else { 53 | to = len(books) 54 | } 55 | 56 | return c.JSON(http.StatusOK, books[from:to]) 57 | } 58 | 59 | func (a *API) getBook(c echo.Context) error { 60 | 61 | params := &BookIdParams{} 62 | 63 | err := c.Bind(params) 64 | if err != nil { 65 | return c.JSON(http.StatusBadRequest, "Invalid parameters") 66 | } 67 | 68 | index := params.ID - 1 69 | 70 | if index < 0 || index > len(books)-1 { 71 | return c.JSON(http.StatusBadRequest, "Invalid parameters") 72 | } 73 | 74 | return c.JSON(http.StatusOK, books[index]) 75 | } 76 | 77 | func (a *API) postBook(c echo.Context) error { 78 | book := &PostBook{} 79 | 80 | err := c.Bind(book) 81 | if err != nil { 82 | return c.JSON(http.StatusBadRequest, "Invalid parameters") 83 | } 84 | 85 | books = append(books, book.Title) 86 | return c.NoContent(http.StatusCreated) 87 | 88 | } 89 | -------------------------------------------------------------------------------- /echo/api/middleware.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/google/uuid" 7 | "github.com/labstack/echo/v4" 8 | ) 9 | 10 | var users = map[string]string{"user1": "password1", "user2": "password2"} 11 | 12 | func requestIDHandler(next echo.HandlerFunc) echo.HandlerFunc { 13 | return func(c echo.Context) error { 14 | 15 | requestID := c.Request().Header.Get("X-Request-ID") 16 | 17 | if len(requestID) == 0 { 18 | requestID = uuid.New().String() 19 | } 20 | 21 | c.Response().Header().Set("X-Request-ID", requestID) 22 | return next(c) 23 | } 24 | } 25 | 26 | func authMiddleware(next echo.HandlerFunc) echo.HandlerFunc { 27 | return func(c echo.Context) error { 28 | 29 | user := c.Request().Header.Get("Authorization") 30 | 31 | if users[user] == "" { 32 | return c.JSON(http.StatusUnauthorized, "Unauthorized") 33 | } 34 | 35 | return next(c) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /echo/api/routes.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "github.com/labstack/echo/v4" 5 | ) 6 | 7 | func (a *API) RegisterRoutes(e *echo.Echo) { 8 | 9 | e.Use(requestIDHandler) 10 | 11 | public := e.Group("") 12 | protected := e.Group("", authMiddleware) 13 | 14 | public.GET("/books", a.getBooks) 15 | public.GET("/books/:id", a.getBook) 16 | 17 | protected.POST("/books", a.postBook) 18 | } 19 | -------------------------------------------------------------------------------- /echo/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/disturb16/go_examples/echo 2 | 3 | go 1.16 4 | 5 | require ( 6 | github.com/google/uuid v1.3.0 7 | github.com/gorilla/schema v1.2.0 8 | github.com/labstack/echo/v4 v4.6.3 9 | ) 10 | -------------------------------------------------------------------------------- /echo/go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 2 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 3 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 4 | github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= 5 | github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= 6 | github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 7 | github.com/gorilla/schema v1.2.0 h1:YufUaxZYCKGFuAq3c96BOhjgd5nmXiOY9NGzF247Tsc= 8 | github.com/gorilla/schema v1.2.0/go.mod h1:kgLaKoK1FELgZqMAVxx/5cbj0kT+57qxUrAlIO2eleU= 9 | github.com/labstack/echo/v4 v4.6.3 h1:VhPuIZYxsbPmo4m9KAkMU/el2442eB7EBFFhNTTT9ac= 10 | github.com/labstack/echo/v4 v4.6.3/go.mod h1:Hk5OiHj0kDqmFq7aHe7eDqI7CUhuCrfpupQtLGGLm7A= 11 | github.com/labstack/gommon v0.3.1 h1:OomWaJXm7xR6L1HmEtGyQf26TEn7V6X88mktX9kee9o= 12 | github.com/labstack/gommon v0.3.1/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM= 13 | github.com/mattn/go-colorable v0.1.11 h1:nQ+aFkoE2TMGc0b68U2OKSexC+eq46+XwZzWXHRmPYs= 14 | github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= 15 | github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= 16 | github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= 17 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 18 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 19 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 20 | github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= 21 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 22 | github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= 23 | github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= 24 | github.com/valyala/fasttemplate v1.2.1 h1:TVEnxayobAdVkhQfrfes2IzOB6o+z4roRkPF52WA1u4= 25 | github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= 26 | golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 h1:HWj/xjIHfjYU5nVXpTM0s39J9CbLn7Cc5a7IC5rwsMQ= 27 | golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 28 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 29 | golang.org/x/net v0.0.0-20210913180222-943fd674d43e h1:+b/22bPvDYt4NPDcy4xAGCmON713ONAWFeY3Z7I3tR8= 30 | golang.org/x/net v0.0.0-20210913180222-943fd674d43e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 31 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 32 | golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 33 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 34 | golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 35 | golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 36 | golang.org/x/sys v0.0.0-20211103235746-7861aae1554b h1:1VkfZQv42XQlA/jchYumAnv1UPo6RgF9rJFkTgZIxO4= 37 | golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 38 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 39 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 40 | golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 41 | golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= 42 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 43 | golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 44 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 45 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 46 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 47 | gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= 48 | gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 49 | -------------------------------------------------------------------------------- /echo/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/disturb16/go_examples/echo/api" 7 | "github.com/labstack/echo/v4" 8 | ) 9 | 10 | func handleIndex(c echo.Context) error { 11 | return c.JSON(http.StatusOK, map[string]string{"message": "Hello World"}) 12 | } 13 | 14 | func main() { 15 | e := echo.New() 16 | e.GET("/", handleIndex) 17 | 18 | a := &api.API{} 19 | a.RegisterRoutes(e) 20 | 21 | e.Logger.Fatal(e.Start(":8081")) 22 | } 23 | -------------------------------------------------------------------------------- /echo/topics.md: -------------------------------------------------------------------------------- 1 | # Topics for this example 2 | 3 | - Set router 4 | - Create Get request with query params 5 | - Create Get request with path params 6 | - Create Post reuqest 7 | - Create middleware -------------------------------------------------------------------------------- /event-handler/accounts/config/config.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | type Config struct { 4 | Address string `json:"app_address"` 5 | DBPath string `json:"db_path"` 6 | RabbitMQAddr string `json:"rabbitmq_addr"` 7 | LogLevel int `json:"log_level"` 8 | } 9 | 10 | func New() *Config { 11 | // TODO: Read values from config file 12 | 13 | return &Config{ 14 | Address: ":8081", 15 | DBPath: "data.db", 16 | RabbitMQAddr: "amqp://guest:guest@localhost:5672", 17 | LogLevel: -4, 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /event-handler/accounts/database/database.go: -------------------------------------------------------------------------------- 1 | package database 2 | 3 | import ( 4 | "database/sql" 5 | "event-handler/accounts/config" 6 | "fmt" 7 | 8 | _ "embed" 9 | 10 | _ "modernc.org/sqlite" 11 | ) 12 | 13 | //go:embed schema.sql 14 | var schema string 15 | 16 | func New(c *config.Config) (*sql.DB, error) { 17 | dbPath := fmt.Sprintf("file:%s", c.DBPath) 18 | 19 | db, err := sql.Open("sqlite", dbPath) 20 | if err != nil { 21 | return nil, err 22 | } 23 | 24 | return db, nil 25 | } 26 | 27 | func PopulateDb(db *sql.DB) error { 28 | _, err := db.Exec(schema) 29 | if err != nil { 30 | return err 31 | } 32 | 33 | _, err = db.Exec("PRAGMA journal_mode = WAL") 34 | if err != nil { 35 | return err 36 | } 37 | 38 | return nil 39 | } 40 | -------------------------------------------------------------------------------- /event-handler/accounts/database/schema.sql: -------------------------------------------------------------------------------- 1 | create table if not exists profiles ( 2 | account_id TEXT, 3 | subscription_id TEXT, 4 | status TEXT 5 | ); 6 | -------------------------------------------------------------------------------- /event-handler/accounts/go.mod: -------------------------------------------------------------------------------- 1 | module event-handler/accounts 2 | 3 | go 1.23 4 | 5 | require ( 6 | github.com/ThreeDotsLabs/watermill v1.4.5 7 | github.com/ThreeDotsLabs/watermill-amqp/v3 v3.0.1 8 | github.com/google/uuid v1.6.0 9 | github.com/labstack/echo/v4 v4.11.3 10 | go.uber.org/fx v1.20.1 11 | modernc.org/sqlite v1.36.1 12 | ) 13 | 14 | require ( 15 | github.com/cenkalti/backoff/v3 v3.2.2 // indirect 16 | github.com/dustin/go-humanize v1.0.1 // indirect 17 | github.com/golang-jwt/jwt v3.2.2+incompatible // indirect 18 | github.com/hashicorp/errwrap v1.1.0 // indirect 19 | github.com/hashicorp/go-multierror v1.1.1 // indirect 20 | github.com/labstack/gommon v0.4.0 // indirect 21 | github.com/lithammer/shortuuid/v3 v3.0.7 // indirect 22 | github.com/mattn/go-colorable v0.1.13 // indirect 23 | github.com/mattn/go-isatty v0.0.20 // indirect 24 | github.com/ncruces/go-strftime v0.1.9 // indirect 25 | github.com/oklog/ulid v1.3.1 // indirect 26 | github.com/pkg/errors v0.9.1 // indirect 27 | github.com/rabbitmq/amqp091-go v1.10.0 // indirect 28 | github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect 29 | github.com/sony/gobreaker v1.0.0 // indirect 30 | github.com/valyala/bytebufferpool v1.0.0 // indirect 31 | github.com/valyala/fasttemplate v1.2.2 // indirect 32 | go.uber.org/atomic v1.11.0 // indirect 33 | go.uber.org/dig v1.17.1 // indirect 34 | go.uber.org/multierr v1.11.0 // indirect 35 | go.uber.org/zap v1.26.0 // indirect 36 | golang.org/x/crypto v0.14.0 // indirect 37 | golang.org/x/exp v0.0.0-20230315142452-642cacee5cc0 // indirect 38 | golang.org/x/net v0.17.0 // indirect 39 | golang.org/x/sys v0.30.0 // indirect 40 | golang.org/x/text v0.13.0 // indirect 41 | golang.org/x/time v0.3.0 // indirect 42 | modernc.org/libc v1.61.13 // indirect 43 | modernc.org/mathutil v1.7.1 // indirect 44 | modernc.org/memory v1.8.2 // indirect 45 | ) 46 | -------------------------------------------------------------------------------- /event-handler/accounts/internal/contracts/contracts.go: -------------------------------------------------------------------------------- 1 | package contracts 2 | 3 | import ( 4 | "github.com/ThreeDotsLabs/watermill/message" 5 | "github.com/labstack/echo/v4" 6 | ) 7 | 8 | type HttpHandler interface { 9 | RegisterRoutes(e *echo.Echo) 10 | } 11 | 12 | type EventHandler interface { 13 | Name() string 14 | Topic() string 15 | Handle(msg *message.Message) error 16 | } 17 | -------------------------------------------------------------------------------- /event-handler/accounts/internal/events/payment_created.go: -------------------------------------------------------------------------------- 1 | package events 2 | 3 | type PaymentCreated struct { 4 | AccountID string `json:"account_id"` 5 | PaymentID string `json:"payment_id"` 6 | TotalPaid int64 `json:"total_paid"` 7 | CreatedAt string `json:"created_at"` 8 | } 9 | -------------------------------------------------------------------------------- /event-handler/accounts/internal/httpapi/handlers/profile/get.go: -------------------------------------------------------------------------------- 1 | package profile 2 | 3 | import ( 4 | "event-handler/accounts/logger" 5 | "net/http" 6 | 7 | "github.com/labstack/echo/v4" 8 | ) 9 | 10 | func (h *handler) ProfileByAccountID(c echo.Context) error { 11 | ctx := c.Request().Context() 12 | log := logger.FromCtx(ctx) 13 | accountID := c.Param("accountID") 14 | 15 | log.Info("trying to get account") 16 | p, err := h.profileManager.ByID(ctx, accountID) 17 | if err != nil { 18 | log.WithError(err).Error("failed to get profile by id") 19 | } 20 | 21 | resp := Response{ 22 | Data: p, 23 | } 24 | 25 | return c.JSON(http.StatusOK, resp) 26 | } 27 | -------------------------------------------------------------------------------- /event-handler/accounts/internal/httpapi/handlers/profile/post.go: -------------------------------------------------------------------------------- 1 | package profile 2 | 3 | import ( 4 | "event-handler/accounts/logger" 5 | "net/http" 6 | 7 | "github.com/google/uuid" 8 | "github.com/labstack/echo/v4" 9 | ) 10 | 11 | func (h *handler) CreateProfile(c echo.Context) error { 12 | ctx := c.Request().Context() 13 | log := logger.FromCtx(ctx) 14 | 15 | accountID := uuid.New().String() 16 | subscriptionID := uuid.New().String() 17 | 18 | err := h.profileManager.Register(ctx, accountID, subscriptionID) 19 | if err != nil { 20 | log.WithError(err).Error("failed to register new account") 21 | return c.JSON(http.StatusInternalServerError, "internal server error") 22 | } 23 | 24 | resp := Response{ 25 | Data: accountID, 26 | } 27 | 28 | return c.JSON(http.StatusOK, resp) 29 | } 30 | -------------------------------------------------------------------------------- /event-handler/accounts/internal/httpapi/handlers/profile/profile.go: -------------------------------------------------------------------------------- 1 | package profile 2 | 3 | import ( 4 | "event-handler/accounts/internal/contracts" 5 | "event-handler/accounts/internal/services/profiler" 6 | 7 | "github.com/labstack/echo/v4" 8 | "go.uber.org/fx" 9 | ) 10 | 11 | type handler struct { 12 | profileManager profiler.Manager 13 | } 14 | 15 | type Result struct { 16 | fx.Out 17 | 18 | Handler contracts.HttpHandler `group:"handlers"` 19 | } 20 | 21 | type Response struct { 22 | Data any `json:"data"` 23 | } 24 | 25 | func New(pm profiler.Manager) Result { 26 | return Result{ 27 | Handler: &handler{ 28 | profileManager: pm, 29 | }, 30 | } 31 | } 32 | 33 | func (h *handler) RegisterRoutes(e *echo.Echo) { 34 | e.GET("/profiles/:accountID", h.ProfileByAccountID) 35 | e.POST("/profiles", h.CreateProfile) 36 | } 37 | -------------------------------------------------------------------------------- /event-handler/accounts/internal/httpapi/httpapi.go: -------------------------------------------------------------------------------- 1 | package httpapi 2 | 3 | import ( 4 | "event-handler/accounts/internal/httpapi/handlers/profile" 5 | 6 | "github.com/labstack/echo/v4" 7 | "github.com/labstack/echo/v4/middleware" 8 | "go.uber.org/fx" 9 | ) 10 | 11 | func New() *echo.Echo { 12 | e := echo.New() 13 | // e.Use(middleware.Recover()) 14 | e.Use(middleware.RequestID()) 15 | 16 | return e 17 | } 18 | 19 | var Module = fx.Module("httpapi", fx.Provide( 20 | profile.New, 21 | )) 22 | -------------------------------------------------------------------------------- /event-handler/accounts/internal/listener/handlers/paymentsevent/handler.go: -------------------------------------------------------------------------------- 1 | package paymentsevent 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "event-handler/accounts/internal/contracts" 7 | "event-handler/accounts/internal/events" 8 | "event-handler/accounts/internal/services/profiler" 9 | 10 | "github.com/ThreeDotsLabs/watermill/message" 11 | "go.uber.org/fx" 12 | ) 13 | 14 | type handler struct { 15 | profilesManager profiler.Manager 16 | } 17 | 18 | type Result struct { 19 | fx.Out 20 | 21 | Handler contracts.EventHandler `group:"event_handlers"` 22 | } 23 | 24 | func New(pm profiler.Manager) Result { 25 | return Result{ 26 | Handler: &handler{ 27 | profilesManager: pm, 28 | }, 29 | } 30 | } 31 | 32 | func (h *handler) Name() string { 33 | return "accounts_payments_handler" 34 | } 35 | 36 | func (h *handler) Topic() string { 37 | return "payments" 38 | } 39 | 40 | func (h *handler) Handle(msg *message.Message) error { 41 | ev := events.PaymentCreated{} 42 | 43 | err := json.Unmarshal(msg.Payload, &ev) 44 | if err != nil { 45 | return err 46 | } 47 | 48 | ctx := context.Background() 49 | err = h.profilesManager.UpdateStatus(ctx, ev.AccountID, "active") 50 | if err != nil { 51 | return err 52 | } 53 | 54 | return nil 55 | } 56 | -------------------------------------------------------------------------------- /event-handler/accounts/internal/listener/listener.go: -------------------------------------------------------------------------------- 1 | package listener 2 | 3 | import ( 4 | "context" 5 | "event-handler/accounts/config" 6 | "event-handler/accounts/internal/contracts" 7 | "event-handler/accounts/internal/listener/handlers/paymentsevent" 8 | "event-handler/accounts/logger" 9 | "time" 10 | 11 | "github.com/ThreeDotsLabs/watermill" 12 | "github.com/ThreeDotsLabs/watermill-amqp/v3/pkg/amqp" 13 | "github.com/ThreeDotsLabs/watermill/message" 14 | "github.com/ThreeDotsLabs/watermill/message/router/middleware" 15 | "github.com/ThreeDotsLabs/watermill/message/router/plugin" 16 | "go.uber.org/fx" 17 | ) 18 | 19 | type eventsListener struct { 20 | router *message.Router 21 | subscriber *amqp.Subscriber 22 | } 23 | 24 | type EventsListenerRouter interface { 25 | Start(context.Context) error 26 | RegisterHandler(h contracts.EventHandler) 27 | Stop() error 28 | } 29 | 30 | var Module = fx.Module("evetslistener", fx.Provide( 31 | paymentsevent.New, 32 | )) 33 | 34 | func New(c *config.Config, log *logger.Logger) (EventsListenerRouter, error) { 35 | wlog := watermill.NewSlogLogger(log.Logger) 36 | router, err := message.NewRouter(message.RouterConfig{}, wlog) 37 | if err != nil { 38 | return nil, err 39 | } 40 | 41 | amqpConfig := amqp.NewDurableQueueConfig(c.RabbitMQAddr) 42 | subscriber, err := amqp.NewSubscriber(amqpConfig, wlog) 43 | if err != nil { 44 | return nil, err 45 | } 46 | 47 | router.AddPlugin(plugin.SignalsHandler) 48 | 49 | router.AddMiddleware(middleware.CorrelationID) 50 | router.AddMiddleware(middleware.Recoverer) 51 | router.AddMiddleware(middleware.Retry{ 52 | MaxRetries: 5, 53 | InitialInterval: time.Millisecond * 100, 54 | Multiplier: 1.5, 55 | Logger: wlog, 56 | }.Middleware) 57 | 58 | el := &eventsListener{ 59 | router: router, 60 | subscriber: subscriber, 61 | } 62 | 63 | return el, nil 64 | } 65 | 66 | func (el *eventsListener) Start(ctx context.Context) error { 67 | return el.router.Run(ctx) 68 | } 69 | 70 | func (el *eventsListener) Stop() error { 71 | return el.router.Close() 72 | } 73 | 74 | func (el *eventsListener) RegisterHandler(h contracts.EventHandler) { 75 | el.router.AddNoPublisherHandler(h.Name(), h.Topic(), el.subscriber, h.Handle) 76 | } 77 | -------------------------------------------------------------------------------- /event-handler/accounts/internal/repositories/profiles/profiles.go: -------------------------------------------------------------------------------- 1 | package profiles 2 | 3 | import ( 4 | "context" 5 | "database/sql" 6 | "errors" 7 | ) 8 | 9 | type repo struct { 10 | db *sql.DB 11 | } 12 | 13 | type ProfileStorer interface { 14 | Save(ctx context.Context, accountID, subscriptionID string) error 15 | UpdateStatus(ctx context.Context, accountID, status string) error 16 | ByAccountID(ctx context.Context, id string) (ProfileRecord, error) 17 | } 18 | 19 | type ProfileRecord struct { 20 | AccountID string `db:"account_id"` 21 | SubscriptionID string `db:"subscription_id"` 22 | Status string `db:"status"` 23 | } 24 | 25 | const ( 26 | ProfileStatusActive = "active" 27 | ProfileStatusPending = "pending" 28 | ) 29 | 30 | func New(db *sql.DB) ProfileStorer { 31 | return &repo{ 32 | db: db, 33 | } 34 | } 35 | 36 | func (r *repo) Save(ctx context.Context, accountID, subscriptionID string) error { 37 | query := ` 38 | insert into profiles (account_id, subscription_id, status) 39 | values (?, ?, ?); 40 | ` 41 | 42 | _, err := r.db.ExecContext(ctx, query, accountID, subscriptionID, ProfileStatusPending) 43 | if err != nil { 44 | return err 45 | } 46 | 47 | return nil 48 | } 49 | 50 | func (r *repo) UpdateStatus(ctx context.Context, accountID, status string) error { 51 | query := ` 52 | update profiles set 53 | status = ? 54 | where account_id = ?;` 55 | 56 | _, err := r.db.ExecContext(ctx, query, status, accountID) 57 | if err != nil { 58 | return err 59 | } 60 | 61 | return nil 62 | } 63 | 64 | func (r *repo) ByAccountID(ctx context.Context, id string) (ProfileRecord, error) { 65 | query := ` 66 | select 67 | account_id, 68 | subscription_id, 69 | status 70 | from profiles 71 | where account_id = ?; 72 | ` 73 | 74 | p := ProfileRecord{} 75 | row := r.db.QueryRowContext(ctx, query, id) 76 | 77 | err := row.Scan(&p.AccountID, &p.SubscriptionID, &p.Status) 78 | if err != nil && !errors.Is(err, sql.ErrNoRows) { 79 | return p, err 80 | } 81 | 82 | return p, nil 83 | } 84 | -------------------------------------------------------------------------------- /event-handler/accounts/internal/repositories/repositories.go: -------------------------------------------------------------------------------- 1 | package repositories 2 | 3 | import ( 4 | "event-handler/accounts/internal/repositories/profiles" 5 | 6 | "go.uber.org/fx" 7 | ) 8 | 9 | var Module = fx.Module("repositories", fx.Provide( 10 | profiles.New, 11 | )) 12 | -------------------------------------------------------------------------------- /event-handler/accounts/internal/services/profiler/profiler.go: -------------------------------------------------------------------------------- 1 | package profiler 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "event-handler/accounts/internal/repositories/profiles" 7 | "event-handler/accounts/logger" 8 | "slices" 9 | ) 10 | 11 | type service struct { 12 | repo profiles.ProfileStorer 13 | } 14 | 15 | type Profile struct { 16 | AccountID string `json:"account_id"` 17 | SubscriptionID string `json:"subscription_id"` 18 | Status string `json:"status"` 19 | } 20 | 21 | type Manager interface { 22 | Register(ctx context.Context, accountID, subscriptionID string) error 23 | UpdateStatus(ctx context.Context, accountID, status string) error 24 | ByID(ctx context.Context, accountID string) (Profile, error) 25 | } 26 | 27 | var validProfileStatuses = []string{profiles.ProfileStatusActive, profiles.ProfileStatusPending} 28 | 29 | var ( 30 | ErrProfileAlreadyExists = errors.New("profile already exists") 31 | ErrProfileStatusNotValid = errors.New("profile status is not valid") 32 | ) 33 | 34 | func New(r profiles.ProfileStorer) Manager { 35 | return &service{ 36 | repo: r, 37 | } 38 | } 39 | 40 | func (s *service) Register(ctx context.Context, accountID, subscriptionID string) error { 41 | log := logger.FromCtx(ctx) 42 | 43 | profile, err := s.repo.ByAccountID(ctx, accountID) 44 | if err != nil { 45 | log.WithError(err).Error("failed to verify profile exists") 46 | return err 47 | } 48 | 49 | if profile.AccountID != "" { 50 | log.WithAny("account_id", accountID).Error("cannot register profile that already exists") 51 | return ErrProfileAlreadyExists 52 | } 53 | 54 | return s.repo.Save(ctx, accountID, subscriptionID) 55 | } 56 | 57 | func (s *service) UpdateStatus(ctx context.Context, accountID, status string) error { 58 | if !slices.Contains(validProfileStatuses, status) { 59 | return ErrProfileStatusNotValid 60 | } 61 | 62 | err := s.repo.UpdateStatus(ctx, accountID, status) 63 | if err != nil { 64 | return err 65 | } 66 | 67 | return nil 68 | } 69 | 70 | func (s *service) ByID(ctx context.Context, accountID string) (Profile, error) { 71 | p := Profile{} 72 | 73 | record, err := s.repo.ByAccountID(ctx, accountID) 74 | if err != nil { 75 | return p, err 76 | } 77 | 78 | p.AccountID = record.AccountID 79 | p.SubscriptionID = record.SubscriptionID 80 | p.Status = record.Status 81 | 82 | return p, nil 83 | } 84 | -------------------------------------------------------------------------------- /event-handler/accounts/internal/services/services.go: -------------------------------------------------------------------------------- 1 | package services 2 | 3 | import ( 4 | "event-handler/accounts/internal/services/profiler" 5 | 6 | "go.uber.org/fx" 7 | ) 8 | 9 | var Module = fx.Module("services", fx.Provide( 10 | profiler.New, 11 | )) 12 | -------------------------------------------------------------------------------- /event-handler/accounts/logger/logger.go: -------------------------------------------------------------------------------- 1 | package logger 2 | 3 | import ( 4 | "context" 5 | "event-handler/accounts/config" 6 | "log" 7 | "log/slog" 8 | "os" 9 | 10 | "github.com/labstack/echo/v4" 11 | ) 12 | 13 | type Logger struct { 14 | *slog.Logger 15 | level slog.Level 16 | } 17 | 18 | var Log Logger 19 | 20 | const ( 21 | CorrelationIDKey = echo.HeaderXRequestID 22 | ) 23 | 24 | func (l *Logger) WithError(err error) *Logger { 25 | n := l.With(slog.Attr{ 26 | Key: "error", 27 | Value: slog.AnyValue(err), 28 | }) 29 | 30 | return &Logger{n, l.level} 31 | } 32 | 33 | func (l *Logger) WithAny(k string, val any) *Logger { 34 | n := l.With(slog.Attr{ 35 | Key: k, 36 | Value: slog.AnyValue(val), 37 | }) 38 | 39 | return &Logger{n, l.level} 40 | } 41 | 42 | func New(s *config.Config) *Logger { 43 | level := slog.Level(s.LogLevel) 44 | 45 | opts := &slog.HandlerOptions{Level: level} 46 | base := slog.New(slog.NewJSONHandler(os.Stdout, opts)) 47 | 48 | Log = Logger{base, level} 49 | return &Log 50 | } 51 | 52 | func FromCtx(ctx context.Context) Logger { 53 | val := ctx.Value(CorrelationIDKey) 54 | newLog := Log.WithAny(CorrelationIDKey, val) 55 | 56 | log.Println("new", newLog) 57 | return *newLog 58 | } 59 | -------------------------------------------------------------------------------- /event-handler/accounts/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "database/sql" 6 | "event-handler/accounts/config" 7 | "event-handler/accounts/database" 8 | "event-handler/accounts/internal/contracts" 9 | "event-handler/accounts/internal/httpapi" 10 | "event-handler/accounts/internal/listener" 11 | "event-handler/accounts/internal/repositories" 12 | "event-handler/accounts/internal/services" 13 | "event-handler/accounts/logger" 14 | 15 | "github.com/labstack/echo/v4" 16 | "go.uber.org/fx" 17 | ) 18 | 19 | type Params struct { 20 | fx.In 21 | 22 | Lc fx.Lifecycle 23 | Config *config.Config 24 | DB *sql.DB 25 | Echo *echo.Echo 26 | Handlers []contracts.HttpHandler `group:"handlers"` 27 | Logger *logger.Logger 28 | EventHandlers []contracts.EventHandler `group:"event_handlers"` 29 | EventsListenerRouter listener.EventsListenerRouter 30 | } 31 | 32 | func main() { 33 | app := fx.New( 34 | fx.Provide( 35 | context.Background, 36 | config.New, 37 | logger.New, 38 | database.New, 39 | httpapi.New, 40 | listener.New, 41 | ), 42 | repositories.Module, 43 | services.Module, 44 | httpapi.Module, 45 | listener.Module, 46 | fx.Invoke( 47 | setLifeCycle, 48 | ), 49 | ) 50 | 51 | app.Run() 52 | } 53 | 54 | func setLifeCycle(p Params) { 55 | p.Lc.Append(fx.Hook{ 56 | OnStart: func(context.Context) error { 57 | p.Logger.Info("starting server") 58 | for _, h := range p.Handlers { 59 | h.RegisterRoutes(p.Echo) 60 | } 61 | 62 | err := database.PopulateDb(p.DB) 63 | if err != nil { 64 | return err 65 | } 66 | 67 | go func() { 68 | p.Echo.Logger.Fatal(p.Echo.Start(p.Config.Address)) 69 | }() 70 | 71 | for _, h := range p.EventHandlers { 72 | p.EventsListenerRouter.RegisterHandler(h) 73 | } 74 | 75 | go func() { 76 | p.EventsListenerRouter.Start(context.Background()) 77 | }() 78 | 79 | return nil 80 | }, 81 | 82 | OnStop: func(ctx context.Context) error { 83 | if err := p.Echo.Shutdown(ctx); err != nil { 84 | p.Logger.WithError(err).Error("failed to shutdown server") 85 | } 86 | 87 | if err := p.DB.Close(); err != nil { 88 | p.Logger.WithError(err).Error("failed to close db connection") 89 | } 90 | 91 | if err := p.EventsListenerRouter.Stop(); err != nil { 92 | p.Logger.WithError(err).Error("failed to stop event listener router") 93 | } 94 | 95 | return nil 96 | }, 97 | }) 98 | } 99 | -------------------------------------------------------------------------------- /event-handler/payments/config/config.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | type Config struct { 4 | Address string `json:"app_address"` 5 | DBPath string `json:"db_path"` 6 | RabbitMQAddr string `json:"rabbitmq_addr"` 7 | LogLevel int `json:"log_level"` 8 | } 9 | 10 | func New() *Config { 11 | // TODO: Read values from config file 12 | 13 | return &Config{ 14 | Address: ":8082", 15 | DBPath: "data.db", 16 | RabbitMQAddr: "amqp://guest:guest@localhost:5672", 17 | LogLevel: -4, 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /event-handler/payments/database/database.go: -------------------------------------------------------------------------------- 1 | package database 2 | 3 | import ( 4 | "database/sql" 5 | "event-handler/payments/config" 6 | "fmt" 7 | 8 | _ "embed" 9 | 10 | _ "modernc.org/sqlite" 11 | ) 12 | 13 | //go:embed schema.sql 14 | var schema string 15 | 16 | func New(c *config.Config) (*sql.DB, error) { 17 | dbPath := fmt.Sprintf("file:%s", c.DBPath) 18 | 19 | db, err := sql.Open("sqlite", dbPath) 20 | if err != nil { 21 | return nil, err 22 | } 23 | 24 | return db, nil 25 | } 26 | 27 | func PopulateDb(db *sql.DB) error { 28 | _, err := db.Exec(schema) 29 | if err != nil { 30 | return err 31 | } 32 | 33 | _, err = db.Exec("PRAGMA journal_mode = WAL") 34 | if err != nil { 35 | return err 36 | } 37 | 38 | return nil 39 | } 40 | -------------------------------------------------------------------------------- /event-handler/payments/database/schema.sql: -------------------------------------------------------------------------------- 1 | create table if not exists payments ( 2 | account_id TEXT, 3 | payment_id TEXT, 4 | total INTEGER 5 | ); 6 | -------------------------------------------------------------------------------- /event-handler/payments/go.mod: -------------------------------------------------------------------------------- 1 | module event-handler/payments 2 | 3 | go 1.23 4 | 5 | require ( 6 | github.com/ThreeDotsLabs/watermill-amqp/v3 v3.0.1 7 | github.com/google/uuid v1.6.0 8 | github.com/labstack/echo/v4 v4.11.3 9 | go.uber.org/fx v1.20.1 10 | modernc.org/sqlite v1.36.1 11 | ) 12 | 13 | require ( 14 | github.com/ThreeDotsLabs/watermill v1.4.5 // indirect 15 | github.com/cenkalti/backoff/v3 v3.2.2 // indirect 16 | github.com/dustin/go-humanize v1.0.1 // indirect 17 | github.com/golang-jwt/jwt v3.2.2+incompatible // indirect 18 | github.com/hashicorp/errwrap v1.1.0 // indirect 19 | github.com/hashicorp/go-multierror v1.1.1 // indirect 20 | github.com/labstack/gommon v0.4.0 // indirect 21 | github.com/lithammer/shortuuid/v3 v3.0.7 // indirect 22 | github.com/mattn/go-colorable v0.1.13 // indirect 23 | github.com/mattn/go-isatty v0.0.20 // indirect 24 | github.com/ncruces/go-strftime v0.1.9 // indirect 25 | github.com/oklog/ulid v1.3.1 // indirect 26 | github.com/pkg/errors v0.9.1 // indirect 27 | github.com/rabbitmq/amqp091-go v1.10.0 // indirect 28 | github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect 29 | github.com/valyala/bytebufferpool v1.0.0 // indirect 30 | github.com/valyala/fasttemplate v1.2.2 // indirect 31 | go.uber.org/atomic v1.11.0 // indirect 32 | go.uber.org/dig v1.17.1 // indirect 33 | go.uber.org/multierr v1.11.0 // indirect 34 | go.uber.org/zap v1.26.0 // indirect 35 | golang.org/x/crypto v0.14.0 // indirect 36 | golang.org/x/exp v0.0.0-20230315142452-642cacee5cc0 // indirect 37 | golang.org/x/net v0.17.0 // indirect 38 | golang.org/x/sys v0.30.0 // indirect 39 | golang.org/x/text v0.13.0 // indirect 40 | golang.org/x/time v0.3.0 // indirect 41 | modernc.org/libc v1.61.13 // indirect 42 | modernc.org/mathutil v1.7.1 // indirect 43 | modernc.org/memory v1.8.2 // indirect 44 | ) 45 | -------------------------------------------------------------------------------- /event-handler/payments/internal/contracts/contracts.go: -------------------------------------------------------------------------------- 1 | package contracts 2 | 3 | import "github.com/labstack/echo/v4" 4 | 5 | type HttpHandler interface { 6 | RegisterRoutes(e *echo.Echo) 7 | } 8 | -------------------------------------------------------------------------------- /event-handler/payments/internal/events/payment_created.go: -------------------------------------------------------------------------------- 1 | package events 2 | 3 | type PaymentCreated struct { 4 | AccountID string `json:"account_id"` 5 | PaymentID string `json:"payment_id"` 6 | TotalPaid int64 `json:"total_paid"` 7 | CreatedAt string `json:"created_at"` 8 | } 9 | -------------------------------------------------------------------------------- /event-handler/payments/internal/httpapi/handlers/payments/payments.go: -------------------------------------------------------------------------------- 1 | package payments 2 | 3 | import ( 4 | "event-handler/payments/internal/contracts" 5 | "event-handler/payments/internal/services/paymentsmanager" 6 | 7 | "github.com/labstack/echo/v4" 8 | "go.uber.org/fx" 9 | ) 10 | 11 | type handler struct { 12 | paymentManager paymentsmanager.Manager 13 | } 14 | 15 | type Result struct { 16 | fx.Out 17 | 18 | Handler contracts.HttpHandler `group:"handlers"` 19 | } 20 | 21 | func New(pm paymentsmanager.Manager) Result { 22 | return Result{ 23 | Handler: &handler{ 24 | paymentManager: pm, 25 | }, 26 | } 27 | } 28 | 29 | func (h *handler) RegisterRoutes(e *echo.Echo) { 30 | e.POST("/payments", h.RegisterPayment) 31 | } 32 | -------------------------------------------------------------------------------- /event-handler/payments/internal/httpapi/handlers/payments/register.go: -------------------------------------------------------------------------------- 1 | package payments 2 | 3 | import ( 4 | "event-handler/payments/logger" 5 | "net/http" 6 | 7 | "github.com/labstack/echo/v4" 8 | ) 9 | 10 | type Request struct { 11 | AccountID string `json:"account_id"` 12 | Total int64 `json:"total"` 13 | } 14 | 15 | type Response struct { 16 | Data any `json:"data"` 17 | } 18 | 19 | func (h *handler) RegisterPayment(c echo.Context) error { 20 | req := Request{} 21 | ctx := c.Request().Context() 22 | log := logger.FromCtx(ctx) 23 | 24 | err := c.Bind(&req) 25 | if err != nil { 26 | log.WithError(err).Error("invalid request data") 27 | return c.JSON(http.StatusBadRequest, "invalid request") 28 | } 29 | 30 | log.WithAny("request", req).Info("") 31 | 32 | err = h.paymentManager.RegisterPayment(ctx, req.AccountID, req.Total) 33 | if err != nil { 34 | log.WithError(err).Error("failed to register payment") 35 | return c.JSON(http.StatusInternalServerError, "internal server error") 36 | } 37 | 38 | resp := Response{ 39 | Data: "success", 40 | } 41 | return c.JSON(http.StatusOK, resp) 42 | } 43 | -------------------------------------------------------------------------------- /event-handler/payments/internal/httpapi/httpapi.go: -------------------------------------------------------------------------------- 1 | package httpapi 2 | 3 | import ( 4 | "event-handler/payments/internal/httpapi/handlers/payments" 5 | 6 | "github.com/labstack/echo/v4" 7 | "github.com/labstack/echo/v4/middleware" 8 | "go.uber.org/fx" 9 | ) 10 | 11 | func New() *echo.Echo { 12 | e := echo.New() 13 | e.Use(middleware.Recover()) 14 | e.Use(middleware.RequestID()) 15 | return e 16 | } 17 | 18 | var Module = fx.Module("httpapi", fx.Provide( 19 | payments.New, 20 | )) 21 | -------------------------------------------------------------------------------- /event-handler/payments/internal/repositories/paymentstore/paymentstore.go: -------------------------------------------------------------------------------- 1 | package paymentstore 2 | 3 | import ( 4 | "context" 5 | "database/sql" 6 | ) 7 | 8 | type PaymentStorer interface { 9 | Save(ctx context.Context, accountID, paymentID string, total int64) error 10 | } 11 | 12 | type store struct { 13 | db *sql.DB 14 | } 15 | 16 | func New(db *sql.DB) PaymentStorer { 17 | return &store{ 18 | db: db, 19 | } 20 | } 21 | 22 | func (s *store) Save(ctx context.Context, accountID, paymentID string, total int64) error { 23 | query := ` 24 | insert into payments (account_id, payment_id, total) 25 | values(?,?,?); 26 | ` 27 | 28 | _, err := s.db.ExecContext(ctx, query, accountID, paymentID, total) 29 | if err != nil { 30 | return err 31 | } 32 | 33 | return nil 34 | } 35 | -------------------------------------------------------------------------------- /event-handler/payments/internal/repositories/repositories.go: -------------------------------------------------------------------------------- 1 | package repositories 2 | 3 | import ( 4 | "event-handler/payments/internal/repositories/paymentstore" 5 | 6 | "go.uber.org/fx" 7 | ) 8 | 9 | var Module = fx.Module("repositories", fx.Provide( 10 | paymentstore.New, 11 | )) 12 | -------------------------------------------------------------------------------- /event-handler/payments/internal/services/paymentsmanager/paymentsmanager.go: -------------------------------------------------------------------------------- 1 | package paymentsmanager 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "errors" 7 | "event-handler/payments/internal/events" 8 | "event-handler/payments/internal/repositories/paymentstore" 9 | "event-handler/payments/logger" 10 | "time" 11 | 12 | "github.com/ThreeDotsLabs/watermill" 13 | "github.com/ThreeDotsLabs/watermill-amqp/v3/pkg/amqp" 14 | "github.com/ThreeDotsLabs/watermill/message" 15 | "github.com/google/uuid" 16 | ) 17 | 18 | type service struct { 19 | repo paymentstore.PaymentStorer 20 | eventsPublisher *amqp.Publisher 21 | } 22 | 23 | type Manager interface { 24 | RegisterPayment(ctx context.Context, accountID string, total int64) error 25 | } 26 | 27 | var ErrInvalidPaymentAmount = errors.New("invalid payment amount") 28 | 29 | func New(repo paymentstore.PaymentStorer, ep *amqp.Publisher) Manager { 30 | return &service{ 31 | repo: repo, 32 | eventsPublisher: ep, 33 | } 34 | } 35 | 36 | func (s *service) RegisterPayment(ctx context.Context, accountID string, total int64) error { 37 | if total < 0 { 38 | return ErrInvalidPaymentAmount 39 | } 40 | 41 | log := logger.FromCtx(ctx) 42 | paymentID := uuid.New().String() 43 | err := s.repo.Save(ctx, accountID, paymentID, total) 44 | if err != nil { 45 | return err 46 | } 47 | 48 | err = s.sendPaymentCreatedEvent(accountID, paymentID, total) 49 | if err != nil { 50 | log.WithError(err).Error("failed to send payment created event") 51 | } 52 | 53 | return nil 54 | } 55 | 56 | func (s *service) sendPaymentCreatedEvent(accountID, paymentID string, total int64) error { 57 | ev := events.PaymentCreated{ 58 | AccountID: accountID, 59 | PaymentID: paymentID, 60 | TotalPaid: total, 61 | CreatedAt: time.Now().In(time.UTC).Format(time.DateOnly), 62 | } 63 | 64 | payload, err := json.Marshal(ev) 65 | if err != nil { 66 | return err 67 | } 68 | 69 | msg := message.NewMessage(watermill.NewUUID(), payload) 70 | 71 | err = s.eventsPublisher.Publish("payments", msg) 72 | if err != nil { 73 | return err 74 | } 75 | 76 | return nil 77 | } 78 | -------------------------------------------------------------------------------- /event-handler/payments/internal/services/services.go: -------------------------------------------------------------------------------- 1 | package services 2 | 3 | import ( 4 | "event-handler/payments/internal/services/paymentsmanager" 5 | 6 | "go.uber.org/fx" 7 | ) 8 | 9 | var Module = fx.Module("services", fx.Provide( 10 | paymentsmanager.New, 11 | )) 12 | -------------------------------------------------------------------------------- /event-handler/payments/logger/logger.go: -------------------------------------------------------------------------------- 1 | package logger 2 | 3 | import ( 4 | "context" 5 | "event-handler/payments/config" 6 | "log/slog" 7 | "os" 8 | 9 | "github.com/labstack/echo/v4" 10 | ) 11 | 12 | type Logger struct { 13 | *slog.Logger 14 | level slog.Level 15 | } 16 | 17 | var Log Logger 18 | 19 | const ( 20 | CorrelationIDKey = echo.HeaderXRequestID 21 | ) 22 | 23 | func (l *Logger) WithError(err error) *Logger { 24 | n := l.With(slog.Attr{ 25 | Key: "error", 26 | Value: slog.AnyValue(err), 27 | }) 28 | 29 | return &Logger{n, l.level} 30 | } 31 | 32 | func (l *Logger) WithAny(k string, val any) *Logger { 33 | n := l.With(slog.Attr{ 34 | Key: k, 35 | Value: slog.AnyValue(val), 36 | }) 37 | 38 | return &Logger{n, l.level} 39 | } 40 | 41 | func New(s *config.Config) *Logger { 42 | level := slog.Level(s.LogLevel) 43 | 44 | opts := &slog.HandlerOptions{Level: level} 45 | base := slog.New(slog.NewJSONHandler(os.Stdout, opts)) 46 | 47 | Log = Logger{base, level} 48 | 49 | return &Log 50 | } 51 | 52 | func FromCtx(ctx context.Context) Logger { 53 | val := ctx.Value(CorrelationIDKey) 54 | newLog := *Log.WithAny(CorrelationIDKey, val) 55 | return newLog 56 | } 57 | -------------------------------------------------------------------------------- /event-handler/payments/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "database/sql" 6 | "event-handler/payments/config" 7 | "event-handler/payments/database" 8 | "event-handler/payments/internal/contracts" 9 | "event-handler/payments/internal/httpapi" 10 | "event-handler/payments/internal/repositories" 11 | "event-handler/payments/internal/services" 12 | "event-handler/payments/logger" 13 | "log" 14 | 15 | "github.com/ThreeDotsLabs/watermill" 16 | "github.com/ThreeDotsLabs/watermill-amqp/v3/pkg/amqp" 17 | "github.com/labstack/echo/v4" 18 | "go.uber.org/fx" 19 | ) 20 | 21 | type Params struct { 22 | fx.In 23 | 24 | Lc fx.Lifecycle 25 | Config *config.Config 26 | DB *sql.DB 27 | Echo *echo.Echo 28 | Handlers []contracts.HttpHandler `group:"handlers"` 29 | } 30 | 31 | func main() { 32 | app := fx.New( 33 | fx.Provide( 34 | context.Background, 35 | config.New, 36 | database.New, 37 | httpapi.New, 38 | logger.New, 39 | amqpPublisher, 40 | ), 41 | repositories.Module, 42 | services.Module, 43 | httpapi.Module, 44 | fx.Invoke( 45 | setLifeCycle, 46 | ), 47 | ) 48 | 49 | app.Run() 50 | } 51 | 52 | func amqpPublisher(c *config.Config, log *logger.Logger) (*amqp.Publisher, error) { 53 | amqpConfig := amqp.NewDurableQueueConfig(c.RabbitMQAddr) 54 | publisher, err := amqp.NewPublisher(amqpConfig, watermill.NewSlogLogger(log.Logger)) 55 | if err != nil { 56 | return nil, err 57 | } 58 | 59 | return publisher, nil 60 | } 61 | 62 | func setLifeCycle(p Params) { 63 | p.Lc.Append(fx.Hook{ 64 | OnStart: func(context.Context) error { 65 | for _, h := range p.Handlers { 66 | h.RegisterRoutes(p.Echo) 67 | } 68 | 69 | err := database.PopulateDb(p.DB) 70 | if err != nil { 71 | return err 72 | } 73 | 74 | go func() { 75 | p.Echo.Logger.Fatal(p.Echo.Start(p.Config.Address)) 76 | }() 77 | 78 | return nil 79 | }, 80 | 81 | OnStop: func(ctx context.Context) error { 82 | if err := p.Echo.Shutdown(ctx); err != nil { 83 | log.Println(err) 84 | } 85 | 86 | if err := p.DB.Close(); err != nil { 87 | log.Println(err) 88 | } 89 | 90 | return nil 91 | }, 92 | }) 93 | } 94 | -------------------------------------------------------------------------------- /factory/configuration/configuration.go: -------------------------------------------------------------------------------- 1 | package configuration 2 | 3 | type Configuration struct { 4 | Engine string 5 | Host string 6 | Port int 7 | User string 8 | Pass string 9 | DBName string 10 | } 11 | -------------------------------------------------------------------------------- /factory/diagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/disturb16/go_examples/57ca3799913e394df9810a4a3709bd6c0f197097/factory/diagram.png -------------------------------------------------------------------------------- /factory/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/user/factory-pattern 2 | 3 | go 1.17 4 | -------------------------------------------------------------------------------- /factory/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/user/factory-pattern/configuration" 7 | "github.com/user/factory-pattern/repository" 8 | ) 9 | 10 | // Factory Pattern 11 | 12 | func main() { 13 | config := &configuration.Configuration{ 14 | Engine: "sqlserver", 15 | Host: "localhost", 16 | } 17 | 18 | repo, err := repository.New(config) 19 | if err != nil { 20 | panic(err) 21 | } 22 | 23 | err = repo.Save("") 24 | if err != nil { 25 | panic(err) 26 | } 27 | 28 | data := repo.Find(1) 29 | fmt.Println(data) 30 | } 31 | -------------------------------------------------------------------------------- /factory/repository/mysql/mysql.go: -------------------------------------------------------------------------------- 1 | package mysql 2 | 3 | import "fmt" 4 | 5 | type Mysql struct{} 6 | 7 | func New() *Mysql { 8 | return &Mysql{} 9 | } 10 | 11 | func (m *Mysql) Find(id int) string { 12 | return "data from mysql" 13 | } 14 | 15 | func (m *Mysql) Save(data string) error { 16 | fmt.Println("save data to mysql") 17 | return nil 18 | } 19 | -------------------------------------------------------------------------------- /factory/repository/repository.go: -------------------------------------------------------------------------------- 1 | package repository 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/user/factory-pattern/configuration" 7 | "github.com/user/factory-pattern/repository/mysql" 8 | "github.com/user/factory-pattern/repository/sqlserver" 9 | ) 10 | 11 | type Repository interface { 12 | Find(id int) string 13 | Save(data string) error 14 | } 15 | 16 | func New(config *configuration.Configuration) (Repository, error) { 17 | 18 | var repo Repository 19 | var err error 20 | 21 | switch config.Engine { 22 | case "mysql": 23 | repo = mysql.New() 24 | 25 | case "sqlserver": 26 | repo = sqlserver.New() 27 | 28 | default: 29 | err = fmt.Errorf("invalid engine %s", config.Engine) 30 | } 31 | 32 | return repo, err 33 | } 34 | -------------------------------------------------------------------------------- /factory/repository/sqlserver/sqlserver.go: -------------------------------------------------------------------------------- 1 | package sqlserver 2 | 3 | import "fmt" 4 | 5 | type SqlServer struct{} 6 | 7 | func New() *SqlServer { 8 | return &SqlServer{} 9 | } 10 | 11 | func (ss *SqlServer) Find(id int) string { 12 | return "data from sqlserver" 13 | } 14 | 15 | func (ss *SqlServer) Save(data string) error { 16 | fmt.Println("save data to sqlserver") 17 | return nil 18 | } 19 | -------------------------------------------------------------------------------- /faker-generator/data.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/disturb16/go_examples/57ca3799913e394df9810a4a3709bd6c0f197097/faker-generator/data.db -------------------------------------------------------------------------------- /faker-generator/entities/order.go: -------------------------------------------------------------------------------- 1 | package entities 2 | 3 | import "time" 4 | 5 | type Order struct { 6 | ID int64 `db:"id"` 7 | UserID int64 `db:"user_id"` 8 | Date time.Time `db:"order_date"` 9 | PaymentMethod string `db:"payment_method" faker:"oneof: cc, paypal, check, cash"` 10 | PaymentRef string `db:"payment_reference" faker:"uuid_digit"` 11 | } 12 | -------------------------------------------------------------------------------- /faker-generator/entities/user.go: -------------------------------------------------------------------------------- 1 | package entities 2 | 3 | type User struct { 4 | ID int64 `db:"id"` 5 | FirstName string `db:"first_name" faker:"first_name"` 6 | LastName string `db:"last_name" faker:"last_name"` 7 | Email string `db:"email" faker:"email"` 8 | Phone string `db:"phone" faker:"phone_number"` 9 | } 10 | -------------------------------------------------------------------------------- /faker-generator/generator/generator.go: -------------------------------------------------------------------------------- 1 | package generator 2 | 3 | import ( 4 | "faker-generator/entities" 5 | 6 | "github.com/go-faker/faker/v4" 7 | "github.com/go-faker/faker/v4/pkg/options" 8 | ) 9 | 10 | func GenerateUsers() ([]entities.User, error) { 11 | uu := []entities.User{} 12 | err := faker.FakeData( 13 | &uu, 14 | options.WithRandomMapAndSliceMaxSize(10), 15 | options.WithRandomMapAndSliceMinSize(5), 16 | ) 17 | if err != nil { 18 | return nil, err 19 | } 20 | 21 | return uu, nil 22 | } 23 | 24 | func GenerateOrders() ([]entities.Order, error) { 25 | 26 | oo := []entities.Order{} 27 | err := faker.FakeData( 28 | &oo, 29 | options.WithRandomMapAndSliceMaxSize(5), 30 | options.WithRandomMapAndSliceMinSize(1), 31 | ) 32 | if err != nil { 33 | return nil, err 34 | } 35 | 36 | return oo, nil 37 | } 38 | -------------------------------------------------------------------------------- /faker-generator/go.mod: -------------------------------------------------------------------------------- 1 | module faker-generator 2 | 3 | go 1.20 4 | 5 | require ( 6 | github.com/go-faker/faker/v4 v4.1.0 7 | github.com/jmoiron/sqlx v1.3.5 8 | github.com/mattn/go-sqlite3 v1.14.16 9 | ) 10 | 11 | require golang.org/x/text v0.3.7 // indirect 12 | -------------------------------------------------------------------------------- /faker-generator/go.sum: -------------------------------------------------------------------------------- 1 | github.com/go-faker/faker/v4 v4.1.0 h1:ffuWmpDrducIUOO0QSKSF5Q2dxAht+dhsT9FvVHhPEI= 2 | github.com/go-faker/faker/v4 v4.1.0/go.mod h1:uuNc0PSRxF8nMgjGrrrU4Nw5cF30Jc6Kd0/FUTTYbhg= 3 | github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= 4 | github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= 5 | github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g= 6 | github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ= 7 | github.com/lib/pq v1.2.0 h1:LXpIM/LZ5xGFhOpXAQUIMM1HdyqzVYM13zNdjCEEcA0= 8 | github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= 9 | github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= 10 | github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y= 11 | github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= 12 | golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= 13 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 14 | -------------------------------------------------------------------------------- /faker-generator/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "faker-generator/generator" 6 | "faker-generator/repository" 7 | "log" 8 | ) 9 | 10 | func main() { 11 | ctx := context.Background() 12 | 13 | repo, err := repository.New(ctx) 14 | if err != nil { 15 | log.Println(err) 16 | } 17 | 18 | users, err := generator.GenerateUsers() 19 | if err != nil { 20 | log.Println(err) 21 | } 22 | 23 | for _, u := range users { 24 | orders, err := generator.GenerateOrders() 25 | if err != nil { 26 | log.Println(err) 27 | } 28 | 29 | err = repo.InsertUserWithOrders(ctx, u, orders) 30 | if err != nil { 31 | log.Println(err) 32 | } 33 | } 34 | 35 | repo.Close(ctx) 36 | } 37 | -------------------------------------------------------------------------------- /faker-generator/repository/orders.go: -------------------------------------------------------------------------------- 1 | package repository 2 | 3 | import ( 4 | "context" 5 | "faker-generator/entities" 6 | ) 7 | 8 | func (r *Repository) InsertOrder(ctx context.Context, a entities.Order) error { 9 | 10 | query := `INSERT INTO orders (user_id, order_date, payment_method, payment_reference) 11 | VALUES (:user_id, :order_date, :payment_method, :payment_reference)` 12 | 13 | _, err := r.db.NamedExecContext(ctx, query, a) 14 | 15 | return err 16 | } 17 | 18 | func (r *Repository) OrderByUserID(ctx context.Context, userID int64) ([]entities.Order, error) { 19 | orders := []entities.Order{} 20 | 21 | query := `SELECT * FROM orders WHERE user_id = ?` 22 | 23 | err := r.db.SelectContext(ctx, &orders, query, userID) 24 | if err != nil { 25 | return nil, err 26 | } 27 | 28 | return orders, nil 29 | } 30 | -------------------------------------------------------------------------------- /faker-generator/repository/repository.go: -------------------------------------------------------------------------------- 1 | package repository 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/jmoiron/sqlx" 7 | _ "github.com/mattn/go-sqlite3" 8 | ) 9 | 10 | const schema string = ` 11 | CREATE TABLE IF NOT EXISTS users ( 12 | id INTEGER PRIMARY KEY, 13 | first_name TEXT NOT NULL, 14 | last_name TEXT NOT NULL, 15 | email TEXT NOT NULL, 16 | phone TEXT NOT NULL 17 | ); 18 | 19 | CREATE TABLE IF NOT EXISTS orders ( 20 | id INTEGER PRIMARY KEY, 21 | order_date datetime NOT NULL, 22 | payment_method TEXT NOT NULL, 23 | payment_reference TEXT NOT NULL, 24 | user_id INTEGER NOT NULL, 25 | 26 | FOREIGN KEY (user_id) REFERENCES users(id) 27 | );` 28 | 29 | type Repository struct { 30 | db *sqlx.DB 31 | } 32 | 33 | func New(ctx context.Context) (Repository, error) { 34 | db, err := sqlx.ConnectContext(ctx, "sqlite3", "./data.db") 35 | if err != nil { 36 | return Repository{}, err 37 | } 38 | 39 | db.SetMaxOpenConns(1) 40 | 41 | _, err = db.ExecContext(ctx, schema) 42 | if err != nil { 43 | return Repository{}, err 44 | } 45 | 46 | return Repository{db: db}, nil 47 | } 48 | 49 | func (r *Repository) Close(ctx context.Context) error { 50 | return r.db.Close() 51 | } 52 | -------------------------------------------------------------------------------- /faker-generator/repository/users.go: -------------------------------------------------------------------------------- 1 | package repository 2 | 3 | import ( 4 | "context" 5 | "faker-generator/entities" 6 | "log" 7 | ) 8 | 9 | func (r *Repository) InsertUser(ctx context.Context, u entities.User) (int64, error) { 10 | 11 | query := `INSERT INTO users (first_name, last_name, email, phone) 12 | VALUES (:first_name, :last_name, :email, :phone)` 13 | 14 | result, err := r.db.NamedExecContext(ctx, query, u) 15 | if err != nil { 16 | return 0, err 17 | } 18 | 19 | return result.LastInsertId() 20 | } 21 | 22 | func (r *Repository) AllUsers(ctx context.Context) ([]entities.User, error) { 23 | users := []entities.User{} 24 | 25 | query := `SELECT * FROM users` 26 | 27 | err := r.db.SelectContext(ctx, &users, query) 28 | if err != nil { 29 | return nil, err 30 | } 31 | 32 | log.Println("Total users:", len(users)) 33 | 34 | return users, nil 35 | } 36 | 37 | func (r *Repository) InsertUserWithOrders(ctx context.Context, u entities.User, orders []entities.Order) error { 38 | var err error 39 | 40 | u.ID, err = r.InsertUser(ctx, u) 41 | if err != nil { 42 | log.Panic(err) 43 | } 44 | 45 | for i := range orders { 46 | orders[i].UserID = u.ID 47 | } 48 | 49 | for _, order := range orders { 50 | err = r.InsertOrder(ctx, order) 51 | if err != nil { 52 | log.Panic(err) 53 | } 54 | } 55 | 56 | return nil 57 | } 58 | -------------------------------------------------------------------------------- /generics/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "Launch Package", 9 | "type": "go", 10 | "request": "launch", 11 | "mode": "auto", 12 | "program": "${fileDirname}" 13 | } 14 | ] 15 | } -------------------------------------------------------------------------------- /generics/go.mod: -------------------------------------------------------------------------------- 1 | module generics 2 | 3 | go 1.19 4 | -------------------------------------------------------------------------------- /generics/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "generics/redisclient" 6 | ) 7 | 8 | type Numeric interface { 9 | int | float64 | float32 | int32 | int64 10 | } 11 | 12 | type Person struct { 13 | Name string 14 | Age int 15 | } 16 | 17 | type Employee struct { 18 | Name string 19 | Position string 20 | } 21 | 22 | func sum[K string, T Numeric](key K, a T, b T) T { 23 | fmt.Println(key) 24 | return a + b 25 | } 26 | 27 | func main() { 28 | 29 | // Basic example 30 | fmt.Println(sum("sum int", 1, 2)) 31 | fmt.Println(sum("sum float", 1.0, 2.0)) 32 | 33 | // Redis example 34 | pp, err := redisclient.Read[[]Person]("persons") 35 | if err != nil { 36 | panic(err) 37 | } 38 | 39 | ee, err := redisclient.Read[[]Employee]("employees") 40 | if err != nil { 41 | panic(err) 42 | } 43 | 44 | fmt.Println("Persons:") 45 | for _, p := range pp { 46 | fmt.Printf("%s is %d years old\n", p.Name, p.Age) 47 | } 48 | 49 | fmt.Println("Employees:") 50 | for _, e := range ee { 51 | fmt.Printf("%s is a %s\n", e.Name, e.Position) 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /generics/redisclient/redisclient.go: -------------------------------------------------------------------------------- 1 | package redisclient 2 | 3 | import ( 4 | "encoding/json" 5 | ) 6 | 7 | var persons string = `[ 8 | {"Name": "John", "Age": 20}, 9 | {"Name": "Jane", "Age": 21} 10 | ] 11 | ` 12 | 13 | var employees string = `[ 14 | {"Name": "John", "Position": "Manager"}, 15 | {"Name": "Jane", "Position": "Developer"} 16 | ] 17 | ` 18 | var db map[string]string = map[string]string{ 19 | "persons": persons, 20 | "employees": employees, 21 | } 22 | 23 | func Read[T any](key string) (T, error) { 24 | var data T 25 | 26 | result := db[key] 27 | 28 | err := json.Unmarshal([]byte(result), &data) 29 | return data, err 30 | } 31 | -------------------------------------------------------------------------------- /go-service-struct/README.md: -------------------------------------------------------------------------------- 1 | # Go Project structure (template) 2 | 3 | How to build docker container for "advanced" project 4 | 5 | ```shell 6 | docker build -t myproject-api -f advanced/Dockerfile --build-arg MAIN_FOLDER=api -p 8080:8080 . 7 | ``` 8 | or 9 | ```shell 10 | docker build -t myproject-api -f advanced/Dockerfile --build-arg MAIN_FOLDER=worker . 11 | ``` 12 | 13 | 1. Set a tag using the `-t` shortcode 14 | 2. Select Dockerfile using `-f` 15 | 3. Pass the type of project using a `--build-arg` 16 | - It has to match the name of the folders `/api` or `/worker` 17 | 4. Map a port if needed 18 | 19 | Note: 20 | The docker build command should be executed at the `root` of repository. 21 | -------------------------------------------------------------------------------- /go-service-struct/advanced/Dockerfile: -------------------------------------------------------------------------------- 1 | # Dockerfile for go application with multi stage build 2 | # Build stage 3 | FROM golang:1.21.6-alpine3.18 AS build-env 4 | ARG MAIN_FOLDER=api 5 | RUN apk add --no-cache git 6 | WORKDIR /app 7 | 8 | 9 | # Copy external project dependencies, 10 | # path is relative to the Dockerfile 11 | COPY ["../msgbroker/go.mod", "../msgbroker/msgbroker.go", "/msgbroker/"] 12 | 13 | # Copy the entire project and download dependencies 14 | COPY ./advanced . 15 | RUN go mod download -x 16 | 17 | # Build the specified project 18 | RUN cd ./$MAIN_FOLDER && go build -o /go/bin/app 19 | 20 | # Final stage 21 | FROM alpine:3.18 22 | WORKDIR /app 23 | COPY --from=build-env /go/bin/app /app/ 24 | ENTRYPOINT ./app 25 | -------------------------------------------------------------------------------- /go-service-struct/advanced/api/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "database/sql" 6 | "log" 7 | 8 | "github.com/disturb16/go-service-struct/advanced/config" 9 | "github.com/disturb16/go-service-struct/advanced/database" 10 | "github.com/disturb16/go-service-struct/advanced/internal/httpapi" 11 | "github.com/disturb16/go-service-struct/advanced/internal/httpapi/handlers" 12 | "github.com/disturb16/go-service-struct/advanced/internal/repositories" 13 | "github.com/disturb16/go-service-struct/advanced/internal/services" 14 | "github.com/labstack/echo/v4" 15 | "go.uber.org/fx" 16 | ) 17 | 18 | type Params struct { 19 | fx.In 20 | 21 | Lc fx.Lifecycle 22 | Config *config.Config 23 | DB *sql.DB 24 | Echo *echo.Echo 25 | Handlers []handlers.Handler `group:"handlers"` 26 | } 27 | 28 | func main() { 29 | app := fx.New( 30 | fx.Provide( 31 | context.Background, 32 | config.New, 33 | database.New, 34 | echo.New, 35 | ), 36 | repositories.Module, 37 | services.Module, 38 | httpapi.Module, 39 | fx.Invoke( 40 | setLifeCycle, 41 | ), 42 | ) 43 | 44 | app.Run() 45 | } 46 | 47 | func setLifeCycle(p Params) { 48 | p.Lc.Append(fx.Hook{ 49 | OnStart: func(context.Context) error { 50 | for _, h := range p.Handlers { 51 | h.RegisterRoutes(p.Echo) 52 | } 53 | 54 | go func() { 55 | p.Echo.Logger.Fatal(p.Echo.Start(p.Config.Address)) 56 | }() 57 | 58 | return nil 59 | }, 60 | 61 | OnStop: func(ctx context.Context) error { 62 | if err := p.Echo.Shutdown(ctx); err != nil { 63 | log.Println(err) 64 | } 65 | 66 | if err := p.DB.Close(); err != nil { 67 | log.Println(err) 68 | } 69 | 70 | return nil 71 | }, 72 | }) 73 | } 74 | -------------------------------------------------------------------------------- /go-service-struct/advanced/config/config.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | type Config struct { 4 | Address string 5 | } 6 | 7 | func New() *Config { 8 | return &Config{ 9 | Address: ":8080", 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /go-service-struct/advanced/database/database.go: -------------------------------------------------------------------------------- 1 | package database 2 | 3 | import ( 4 | "context" 5 | "database/sql" 6 | ) 7 | 8 | func New(ctx context.Context) *sql.DB { 9 | return &sql.DB{} 10 | } 11 | -------------------------------------------------------------------------------- /go-service-struct/advanced/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/disturb16/go-service-struct/advanced 2 | 3 | go 1.21.4 4 | 5 | require ( 6 | github.com/labstack/echo/v4 v4.11.3 7 | go.uber.org/fx v1.20.1 8 | ) 9 | 10 | require ( 11 | github.com/labstack/gommon v0.4.0 // indirect 12 | github.com/mattn/go-colorable v0.1.13 // indirect 13 | github.com/mattn/go-isatty v0.0.19 // indirect 14 | github.com/valyala/bytebufferpool v1.0.0 // indirect 15 | github.com/valyala/fasttemplate v1.2.2 // indirect 16 | go.uber.org/atomic v1.11.0 // indirect 17 | go.uber.org/dig v1.17.1 // indirect 18 | go.uber.org/multierr v1.11.0 // indirect 19 | go.uber.org/zap v1.26.0 // indirect 20 | golang.org/x/crypto v0.14.0 // indirect 21 | golang.org/x/net v0.17.0 // indirect 22 | golang.org/x/sys v0.14.0 // indirect 23 | golang.org/x/text v0.13.0 // indirect 24 | ) 25 | -------------------------------------------------------------------------------- /go-service-struct/advanced/go.work: -------------------------------------------------------------------------------- 1 | go 1.21.4 2 | 3 | use ( 4 | . 5 | ../msgbroker 6 | ) 7 | -------------------------------------------------------------------------------- /go-service-struct/advanced/go.work.sum: -------------------------------------------------------------------------------- 1 | github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= 2 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 3 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 4 | github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= 5 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 6 | github.com/labstack/echo/v4 v4.11.3 h1:Upyu3olaqSHkCjs1EJJwQ3WId8b8b1hxbogyommKktM= 7 | github.com/labstack/echo/v4 v4.11.3/go.mod h1:UcGuQ8V6ZNRmSweBIJkPvGfwCMIlFmiqrPqiEBfPYws= 8 | github.com/labstack/gommon v0.4.0 h1:y7cvthEAEbU0yHOf4axH8ZG2NH8knB9iNSoTO8dyIk8= 9 | github.com/labstack/gommon v0.4.0/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM= 10 | github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= 11 | github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= 12 | github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= 13 | github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= 14 | github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= 15 | github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= 16 | github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= 17 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 18 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 19 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 20 | github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= 21 | github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= 22 | github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= 23 | github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= 24 | github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo= 25 | github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= 26 | go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= 27 | go.uber.org/dig v1.17.1 h1:Tga8Lz8PcYNsWsyHMZ1Vm0OQOUaJNDyvPImgbAu9YSc= 28 | go.uber.org/dig v1.17.1/go.mod h1:Us0rSJiThwCv2GteUN0Q7OKvU7n5J4dxZ9JKUXozFdE= 29 | go.uber.org/fx v1.20.1 h1:zVwVQGS8zYvhh9Xxcu4w1M6ESyeMzebzj2NbSayZ4Mk= 30 | go.uber.org/fx v1.20.1/go.mod h1:iSYNbHf2y55acNCwCXKx7LbWb5WG1Bnue5RDXz1OREg= 31 | go.uber.org/goleak v1.2.0/go.mod h1:XJYK+MuIchqpmGmUSAzotztawfKvYLUIgg7guXrwVUo= 32 | go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= 33 | go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= 34 | go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= 35 | go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= 36 | golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= 37 | golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= 38 | golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= 39 | golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= 40 | golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= 41 | golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 42 | golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 43 | golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 44 | golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 45 | golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 46 | golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q= 47 | golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 48 | golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= 49 | golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= 50 | golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= 51 | golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 52 | golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= 53 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 54 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 55 | gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 56 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 57 | -------------------------------------------------------------------------------- /go-service-struct/advanced/internal/httpapi/handlers/auth/auth.go: -------------------------------------------------------------------------------- 1 | package auth 2 | 3 | import ( 4 | "github.com/disturb16/go-service-struct/advanced/internal/httpapi/handlers" 5 | "github.com/disturb16/go-service-struct/advanced/internal/services/auth" 6 | "github.com/labstack/echo/v4" 7 | "go.uber.org/fx" 8 | ) 9 | 10 | type handler struct { 11 | AuthService auth.Service 12 | } 13 | 14 | type Result struct { 15 | fx.Out 16 | 17 | Handler handlers.Handler `group:"handlers"` 18 | } 19 | 20 | func New() Result { 21 | return Result{ 22 | Handler: &handler{}, 23 | } 24 | } 25 | 26 | func (h *handler) RegisterRoutes(e *echo.Echo) { 27 | group := e.Group("/auth") 28 | group.POST("", h.Authenticate) 29 | group.POST("/token/refresh", h.RefreshToken) 30 | } 31 | -------------------------------------------------------------------------------- /go-service-struct/advanced/internal/httpapi/handlers/auth/authenticate.go: -------------------------------------------------------------------------------- 1 | package auth 2 | 3 | import "github.com/labstack/echo/v4" 4 | 5 | func (h *handler) Authenticate(c echo.Context) error { 6 | 7 | return nil 8 | } 9 | -------------------------------------------------------------------------------- /go-service-struct/advanced/internal/httpapi/handlers/auth/dtos.go: -------------------------------------------------------------------------------- 1 | package auth 2 | 3 | type AuthRequest struct{} 4 | 5 | type AuthResponse struct{} 6 | 7 | type RefreshTokenRequest struct{} 8 | 9 | type RefreshTokenResponse struct{} 10 | -------------------------------------------------------------------------------- /go-service-struct/advanced/internal/httpapi/handlers/auth/refresh_token.go: -------------------------------------------------------------------------------- 1 | package auth 2 | 3 | import "github.com/labstack/echo/v4" 4 | 5 | func (h *handler) RefreshToken(c echo.Context) error { 6 | return nil 7 | } 8 | -------------------------------------------------------------------------------- /go-service-struct/advanced/internal/httpapi/handlers/handlers.go: -------------------------------------------------------------------------------- 1 | package handlers 2 | 3 | import "github.com/labstack/echo/v4" 4 | 5 | type Handler interface { 6 | RegisterRoutes(e *echo.Echo) 7 | } 8 | -------------------------------------------------------------------------------- /go-service-struct/advanced/internal/httpapi/handlers/profiles/dtos.go: -------------------------------------------------------------------------------- 1 | package profiles 2 | 3 | type GetRequest struct{} 4 | 5 | type GetResponse struct{} 6 | 7 | type UpdateRequest struct{} 8 | 9 | type UpdateResponse struct{} 10 | -------------------------------------------------------------------------------- /go-service-struct/advanced/internal/httpapi/handlers/profiles/get.go: -------------------------------------------------------------------------------- 1 | package profiles 2 | 3 | import "github.com/labstack/echo/v4" 4 | 5 | func (h *handler) Get(c echo.Context) error { 6 | 7 | h.service.Get(c.Param("id")) 8 | 9 | return nil 10 | 11 | } 12 | -------------------------------------------------------------------------------- /go-service-struct/advanced/internal/httpapi/handlers/profiles/profiles.go: -------------------------------------------------------------------------------- 1 | package profiles 2 | 3 | import ( 4 | "github.com/disturb16/go-service-struct/advanced/internal/httpapi/handlers" 5 | "github.com/disturb16/go-service-struct/advanced/internal/services/profiles" 6 | "github.com/labstack/echo/v4" 7 | "go.uber.org/fx" 8 | ) 9 | 10 | type handler struct { 11 | service profiles.Service 12 | } 13 | 14 | type Result struct { 15 | fx.Out 16 | 17 | Handler handlers.Handler `group:"handlers"` 18 | } 19 | 20 | func New() Result { 21 | return Result{ 22 | Handler: &handler{}, 23 | } 24 | } 25 | 26 | func (h *handler) RegisterRoutes(e *echo.Echo) { 27 | group := e.Group("/profiles") 28 | group.GET("", h.Get) 29 | group.PUT("", h.Update) 30 | 31 | } 32 | -------------------------------------------------------------------------------- /go-service-struct/advanced/internal/httpapi/handlers/profiles/update.go: -------------------------------------------------------------------------------- 1 | package profiles 2 | 3 | import "github.com/labstack/echo/v4" 4 | 5 | func (h *handler) Update(c echo.Context) error { 6 | return nil 7 | } 8 | -------------------------------------------------------------------------------- /go-service-struct/advanced/internal/httpapi/httpapi.go: -------------------------------------------------------------------------------- 1 | package httpapi 2 | 3 | import ( 4 | "github.com/disturb16/go-service-struct/advanced/internal/httpapi/handlers/auth" 5 | "github.com/disturb16/go-service-struct/advanced/internal/httpapi/handlers/profiles" 6 | "go.uber.org/fx" 7 | ) 8 | 9 | var Module = fx.Module("httpapi", fx.Provide( 10 | auth.New, 11 | profiles.New, 12 | )) 13 | -------------------------------------------------------------------------------- /go-service-struct/advanced/internal/listener/listener.go: -------------------------------------------------------------------------------- 1 | package listener 2 | 3 | import ( 4 | "github.com/disturb16/go-service-struct/advanced/internal/listener/processors/auth" 5 | "github.com/disturb16/go-service-struct/advanced/internal/listener/processors/users" 6 | "go.uber.org/fx" 7 | ) 8 | 9 | var Module = fx.Module("listener", fx.Provide( 10 | auth.New, 11 | users.New, 12 | )) 13 | -------------------------------------------------------------------------------- /go-service-struct/advanced/internal/listener/processors/auth/auth.go: -------------------------------------------------------------------------------- 1 | package auth 2 | 3 | import ( 4 | "msgbroker" 5 | 6 | "go.uber.org/fx" 7 | ) 8 | 9 | type processor struct { 10 | Broker msgbroker.Broker 11 | } 12 | 13 | type Result struct { 14 | fx.Out 15 | 16 | Procesor msgbroker.Processor `group:"procesors"` 17 | } 18 | 19 | func New() Result { 20 | return Result{ 21 | Procesor: &processor{}, 22 | } 23 | } 24 | 25 | func (p *processor) Register(b msgbroker.Broker) { 26 | b.AddProcessor("user-logged-out", p.onLogout) 27 | } 28 | -------------------------------------------------------------------------------- /go-service-struct/advanced/internal/listener/processors/auth/on_logout.go: -------------------------------------------------------------------------------- 1 | package auth 2 | 3 | func (p *processor) onLogout(msg string) error { 4 | return nil 5 | } 6 | -------------------------------------------------------------------------------- /go-service-struct/advanced/internal/listener/processors/users/on_delete.go: -------------------------------------------------------------------------------- 1 | package users 2 | 3 | func (p *processor) onUserDelete(msg string) error { 4 | return nil 5 | } 6 | -------------------------------------------------------------------------------- /go-service-struct/advanced/internal/listener/processors/users/on_update.go: -------------------------------------------------------------------------------- 1 | package users 2 | 3 | func (u *processor) OnUserUpdate(msg string) error { 4 | return nil 5 | } 6 | -------------------------------------------------------------------------------- /go-service-struct/advanced/internal/listener/processors/users/users.go: -------------------------------------------------------------------------------- 1 | package users 2 | 3 | import ( 4 | "msgbroker" 5 | 6 | "github.com/disturb16/go-service-struct/advanced/internal/services/profiles" 7 | "go.uber.org/fx" 8 | ) 9 | 10 | type processor struct { 11 | profileService profiles.Service 12 | } 13 | 14 | type Result struct { 15 | fx.Out 16 | 17 | Procesor msgbroker.Processor `group:"procesors"` 18 | } 19 | 20 | func New(service profiles.Service) Result { 21 | return Result{ 22 | Procesor: &processor{ 23 | profileService: service, 24 | }, 25 | } 26 | } 27 | 28 | func (p *processor) Register(b msgbroker.Broker) { 29 | b.AddProcessor("user-update", p.OnUserUpdate) 30 | b.AddProcessor("user-deleted", p.onUserDelete) 31 | } 32 | -------------------------------------------------------------------------------- /go-service-struct/advanced/internal/repositories/profiles/profiles.go: -------------------------------------------------------------------------------- 1 | package profiles 2 | 3 | import "database/sql" 4 | 5 | type Repository interface{} 6 | 7 | type repository struct { 8 | db *sql.DB 9 | } 10 | 11 | func New(db *sql.DB) Repository { 12 | return &repository{db: db} 13 | } 14 | -------------------------------------------------------------------------------- /go-service-struct/advanced/internal/repositories/repositories.go: -------------------------------------------------------------------------------- 1 | package repositories 2 | 3 | import ( 4 | "github.com/disturb16/go-service-struct/advanced/internal/repositories/profiles" 5 | "github.com/disturb16/go-service-struct/advanced/internal/repositories/users" 6 | "go.uber.org/fx" 7 | ) 8 | 9 | var Module = fx.Module("repositories", fx.Provide( 10 | users.New, 11 | profiles.New, 12 | )) 13 | -------------------------------------------------------------------------------- /go-service-struct/advanced/internal/repositories/users/users.go: -------------------------------------------------------------------------------- 1 | package users 2 | 3 | import "database/sql" 4 | 5 | type Repository interface{} 6 | 7 | type repository struct { 8 | db *sql.DB 9 | } 10 | 11 | func New(db *sql.DB) Repository { 12 | return &repository{db: db} 13 | } 14 | -------------------------------------------------------------------------------- /go-service-struct/advanced/internal/services/auth/auth.go: -------------------------------------------------------------------------------- 1 | package auth 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/disturb16/go-service-struct/advanced/internal/repositories/users" 7 | ) 8 | 9 | type Service interface { 10 | Authenticate(ctx context.Context, username, password string) error 11 | } 12 | 13 | type service struct { 14 | repo users.Repository 15 | } 16 | 17 | func New(repo users.Repository) Service { 18 | return &service{ 19 | repo: repo, 20 | } 21 | } 22 | 23 | func (s *service) Authenticate(ctx context.Context, username, password string) error { 24 | return nil 25 | } 26 | -------------------------------------------------------------------------------- /go-service-struct/advanced/internal/services/profiles/get.go: -------------------------------------------------------------------------------- 1 | package profiles 2 | 3 | func (s *service) Get(id string) (string, error) { 4 | return "", nil 5 | } 6 | -------------------------------------------------------------------------------- /go-service-struct/advanced/internal/services/profiles/profile.go: -------------------------------------------------------------------------------- 1 | package profiles 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/disturb16/go-service-struct/advanced/internal/repositories/profiles" 7 | ) 8 | 9 | type Service interface { 10 | Update(ctx context.Context, username string) error 11 | Get(id string) (string, error) 12 | } 13 | 14 | type service struct { 15 | repository profiles.Repository 16 | } 17 | 18 | func New(repository profiles.Repository) Service { 19 | return &service{ 20 | repository: repository, 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /go-service-struct/advanced/internal/services/profiles/update.go: -------------------------------------------------------------------------------- 1 | package profiles 2 | 3 | import "context" 4 | 5 | func (s *service) Update(ctx context.Context, username string) error { 6 | return nil 7 | } 8 | -------------------------------------------------------------------------------- /go-service-struct/advanced/internal/services/services.go: -------------------------------------------------------------------------------- 1 | package services 2 | 3 | import ( 4 | "github.com/disturb16/go-service-struct/advanced/internal/services/auth" 5 | "github.com/disturb16/go-service-struct/advanced/internal/services/profiles" 6 | "go.uber.org/fx" 7 | ) 8 | 9 | var Module = fx.Module("services", fx.Provide( 10 | auth.New, 11 | profiles.New, 12 | )) 13 | -------------------------------------------------------------------------------- /go-service-struct/advanced/worker/Dockerfile: -------------------------------------------------------------------------------- 1 | # Dockerfile for go application with multi stage build 2 | # Build stage 3 | FROM golang:1.13.4-alpine3.10 AS build-env 4 | RUN apk add --no-cache git 5 | WORKDIR /go/src/app 6 | COPY ../ . 7 | RUN go get -d -v ./... 8 | RUN go build -o /go/bin/app 9 | 10 | # Final stage 11 | FROM alpine:3.10 12 | WORKDIR /app 13 | COPY --from=build-env /go/bin/app /app/ 14 | ENTRYPOINT ./app 15 | -------------------------------------------------------------------------------- /go-service-struct/advanced/worker/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "database/sql" 6 | "log" 7 | "msgbroker" 8 | 9 | "github.com/disturb16/go-service-struct/advanced/config" 10 | "github.com/disturb16/go-service-struct/advanced/database" 11 | "github.com/disturb16/go-service-struct/advanced/internal/listener" 12 | "github.com/disturb16/go-service-struct/advanced/internal/repositories" 13 | "github.com/disturb16/go-service-struct/advanced/internal/services" 14 | "go.uber.org/fx" 15 | ) 16 | 17 | type Params struct { 18 | fx.In 19 | 20 | Lc fx.Lifecycle 21 | Config *config.Config 22 | DB *sql.DB 23 | Broker msgbroker.Broker 24 | Procesors []msgbroker.Processor `group:"procesors"` 25 | } 26 | 27 | func main() { 28 | app := fx.New( 29 | fx.Provide( 30 | context.Background, 31 | config.New, 32 | database.New, 33 | msgbroker.New, 34 | ), 35 | repositories.Module, 36 | services.Module, 37 | listener.Module, 38 | fx.Invoke( 39 | setLifeCycle, 40 | ), 41 | ) 42 | 43 | app.Run() 44 | } 45 | 46 | func setLifeCycle(p Params) { 47 | p.Lc.Append(fx.Hook{ 48 | OnStart: func(context.Context) error { 49 | 50 | for _, procesor := range p.Procesors { 51 | procesor.Register(p.Broker) 52 | } 53 | 54 | go func() { 55 | p.Broker.Start() 56 | }() 57 | 58 | return nil 59 | }, 60 | 61 | OnStop: func(ctx context.Context) error { 62 | if err := p.DB.Close(); err != nil { 63 | log.Println(err) 64 | } 65 | 66 | p.Broker.Stop() 67 | 68 | return nil 69 | }, 70 | }) 71 | } 72 | -------------------------------------------------------------------------------- /go-service-struct/msgbroker/go.mod: -------------------------------------------------------------------------------- 1 | module msgbroker 2 | 3 | go 1.21.4 4 | -------------------------------------------------------------------------------- /go-service-struct/msgbroker/msgbroker.go: -------------------------------------------------------------------------------- 1 | // msgbroker provides a mock implementation of a message broker 2 | package msgbroker 3 | 4 | import "log" 5 | 6 | type HandlerFunc func(msg string) error 7 | 8 | type Processor interface { 9 | Register(b Broker) 10 | } 11 | 12 | type Broker interface { 13 | Start() error 14 | Stop() error 15 | AddProcessor(topic string, f HandlerFunc) 16 | } 17 | 18 | type broker struct { 19 | } 20 | 21 | func (b *broker) Start() error { 22 | log.Println("Listener started") 23 | return nil 24 | } 25 | 26 | func (b *broker) Stop() error { 27 | log.Println("Listener stopped") 28 | return nil 29 | } 30 | 31 | func (b *broker) AddProcessor(topic string, f HandlerFunc) { 32 | log.Printf("Processor for %s added\n", topic) 33 | } 34 | 35 | func New() Broker { 36 | return &broker{} 37 | } 38 | -------------------------------------------------------------------------------- /go-service-struct/service.code-workspace: -------------------------------------------------------------------------------- 1 | { 2 | "folders": [ 3 | { 4 | "path": "simple" 5 | }, 6 | { 7 | "path": "advanced" 8 | }, 9 | { 10 | "path": "msgbroker" 11 | } 12 | ], 13 | "settings": {} 14 | } -------------------------------------------------------------------------------- /go-service-struct/simple/Dockerfile: -------------------------------------------------------------------------------- 1 | # Dockerfile for go application with multi stage build 2 | # Build stage 3 | FROM golang:1.13.4-alpine3.10 AS build-env 4 | RUN apk add --no-cache git 5 | WORKDIR /go/src/app 6 | COPY . . 7 | RUN go get -d -v ./... 8 | RUN go build -o /go/bin/app 9 | 10 | # Final stage 11 | FROM alpine:3.10 12 | WORKDIR /app 13 | COPY --from=build-env /go/bin/app /app/ 14 | ENTRYPOINT ./app 15 | -------------------------------------------------------------------------------- /go-service-struct/simple/config/config.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | type Config struct { 4 | Address string 5 | } 6 | 7 | func New() *Config { 8 | return &Config{ 9 | Address: ":8080", 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /go-service-struct/simple/database/database.go: -------------------------------------------------------------------------------- 1 | package database 2 | 3 | import ( 4 | "context" 5 | "database/sql" 6 | ) 7 | 8 | func New(ctx context.Context) *sql.DB { 9 | return &sql.DB{} 10 | } 11 | -------------------------------------------------------------------------------- /go-service-struct/simple/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/disturb16/go-service-struct/simple 2 | 3 | go 1.21 4 | 5 | require ( 6 | github.com/labstack/echo/v4 v4.11.3 7 | go.uber.org/fx v1.20.1 8 | ) 9 | 10 | require ( 11 | github.com/labstack/gommon v0.4.0 // indirect 12 | github.com/mattn/go-colorable v0.1.13 // indirect 13 | github.com/mattn/go-isatty v0.0.19 // indirect 14 | github.com/valyala/bytebufferpool v1.0.0 // indirect 15 | github.com/valyala/fasttemplate v1.2.2 // indirect 16 | go.uber.org/atomic v1.11.0 // indirect 17 | go.uber.org/dig v1.17.1 // indirect 18 | go.uber.org/multierr v1.11.0 // indirect 19 | go.uber.org/zap v1.26.0 // indirect 20 | golang.org/x/crypto v0.14.0 // indirect 21 | golang.org/x/net v0.17.0 // indirect 22 | golang.org/x/sys v0.14.0 // indirect 23 | golang.org/x/text v0.13.0 // indirect 24 | ) 25 | -------------------------------------------------------------------------------- /go-service-struct/simple/internal/httpapi/handlers/auth/auth.go: -------------------------------------------------------------------------------- 1 | package auth 2 | 3 | import ( 4 | "github.com/disturb16/go-service-struct/simple/internal/httpapi/handlers" 5 | "github.com/disturb16/go-service-struct/simple/internal/services/auth" 6 | "github.com/labstack/echo/v4" 7 | "go.uber.org/fx" 8 | ) 9 | 10 | type handler struct { 11 | AuthService auth.Service 12 | } 13 | 14 | type Result struct { 15 | fx.Out 16 | 17 | Handler handlers.Handler `group:"handlers"` 18 | } 19 | 20 | func New() Result { 21 | return Result{ 22 | Handler: &handler{}, 23 | } 24 | } 25 | 26 | func (h *handler) RegisterRoutes(e *echo.Echo) { 27 | group := e.Group("/auth") 28 | group.POST("", h.Authenticate) 29 | group.POST("/token/refresh", h.RefreshToken) 30 | } 31 | -------------------------------------------------------------------------------- /go-service-struct/simple/internal/httpapi/handlers/auth/authenticate.go: -------------------------------------------------------------------------------- 1 | package auth 2 | 3 | import "github.com/labstack/echo/v4" 4 | 5 | func (h *handler) Authenticate(c echo.Context) error { 6 | 7 | return nil 8 | } 9 | -------------------------------------------------------------------------------- /go-service-struct/simple/internal/httpapi/handlers/auth/dtos.go: -------------------------------------------------------------------------------- 1 | package auth 2 | 3 | type AuthRequest struct{} 4 | 5 | type AuthResponse struct{} 6 | 7 | type RefreshTokenRequest struct{} 8 | 9 | type RefreshTokenResponse struct{} 10 | -------------------------------------------------------------------------------- /go-service-struct/simple/internal/httpapi/handlers/auth/refresh_token.go: -------------------------------------------------------------------------------- 1 | package auth 2 | 3 | import "github.com/labstack/echo/v4" 4 | 5 | func (h *handler) RefreshToken(c echo.Context) error { 6 | return nil 7 | } 8 | -------------------------------------------------------------------------------- /go-service-struct/simple/internal/httpapi/handlers/handlers.go: -------------------------------------------------------------------------------- 1 | package handlers 2 | 3 | import "github.com/labstack/echo/v4" 4 | 5 | type Handler interface { 6 | RegisterRoutes(e *echo.Echo) 7 | } 8 | -------------------------------------------------------------------------------- /go-service-struct/simple/internal/httpapi/handlers/profiles/delete.go: -------------------------------------------------------------------------------- 1 | package profiles 2 | 3 | import "github.com/labstack/echo/v4" 4 | 5 | func (h *handler) Delete(c echo.Context) error { 6 | 7 | h.service.Get(c.Param("id")) 8 | 9 | return c.JSON(200, "success") 10 | 11 | } 12 | -------------------------------------------------------------------------------- /go-service-struct/simple/internal/httpapi/handlers/profiles/dtos.go: -------------------------------------------------------------------------------- 1 | package profiles 2 | 3 | type GetRequest struct{} 4 | 5 | type GetResponse struct{} 6 | 7 | type UpdateRequest struct{} 8 | 9 | type UpdateResponse struct{} 10 | -------------------------------------------------------------------------------- /go-service-struct/simple/internal/httpapi/handlers/profiles/get.go: -------------------------------------------------------------------------------- 1 | package profiles 2 | 3 | import "github.com/labstack/echo/v4" 4 | 5 | func (h *handler) GetByID(c echo.Context) error { 6 | 7 | h.service.Get(c.Param("id")) 8 | 9 | return c.JSON(200, "success") 10 | 11 | } 12 | -------------------------------------------------------------------------------- /go-service-struct/simple/internal/httpapi/handlers/profiles/profiles.go: -------------------------------------------------------------------------------- 1 | package profiles 2 | 3 | import ( 4 | "github.com/disturb16/go-service-struct/simple/internal/httpapi/handlers" 5 | "github.com/disturb16/go-service-struct/simple/internal/services/profiles" 6 | "github.com/labstack/echo/v4" 7 | "go.uber.org/fx" 8 | ) 9 | 10 | type handler struct { 11 | service profiles.Service 12 | } 13 | 14 | type Result struct { 15 | fx.Out 16 | 17 | Handler handlers.Handler `group:"handlers"` 18 | } 19 | 20 | func New() Result { 21 | return Result{ 22 | Handler: &handler{}, 23 | } 24 | } 25 | 26 | func (h *handler) RegisterRoutes(e *echo.Echo) { 27 | group := e.Group("/profiles") 28 | group.GET("/:id", h.GetByID) 29 | group.PUT("/:id", h.Update) 30 | group.DELETE("/:id", h.Delete) 31 | } 32 | -------------------------------------------------------------------------------- /go-service-struct/simple/internal/httpapi/handlers/profiles/update.go: -------------------------------------------------------------------------------- 1 | package profiles 2 | 3 | import "github.com/labstack/echo/v4" 4 | 5 | func (h *handler) Update(c echo.Context) error { 6 | return nil 7 | } 8 | -------------------------------------------------------------------------------- /go-service-struct/simple/internal/httpapi/httpapi.go: -------------------------------------------------------------------------------- 1 | package httpapi 2 | 3 | import ( 4 | "github.com/disturb16/go-service-struct/simple/internal/httpapi/handlers/auth" 5 | "github.com/disturb16/go-service-struct/simple/internal/httpapi/handlers/profiles" 6 | "go.uber.org/fx" 7 | ) 8 | 9 | var Module = fx.Module("httpapi", fx.Provide( 10 | auth.New, 11 | profiles.New, 12 | )) 13 | -------------------------------------------------------------------------------- /go-service-struct/simple/internal/repositories/profiles/profiles.go: -------------------------------------------------------------------------------- 1 | package profiles 2 | 3 | import "database/sql" 4 | 5 | type Repository interface{} 6 | 7 | type repository struct { 8 | db *sql.DB 9 | } 10 | 11 | func New(db *sql.DB) Repository { 12 | return &repository{db: db} 13 | } 14 | -------------------------------------------------------------------------------- /go-service-struct/simple/internal/repositories/repositories.go: -------------------------------------------------------------------------------- 1 | package repositories 2 | 3 | import ( 4 | "github.com/disturb16/go-service-struct/simple/internal/repositories/profiles" 5 | "github.com/disturb16/go-service-struct/simple/internal/repositories/users" 6 | "go.uber.org/fx" 7 | ) 8 | 9 | var Module = fx.Module("repositories", fx.Provide( 10 | users.New, 11 | profiles.New, 12 | )) 13 | -------------------------------------------------------------------------------- /go-service-struct/simple/internal/repositories/users/users.go: -------------------------------------------------------------------------------- 1 | package users 2 | 3 | import "database/sql" 4 | 5 | type Repository interface{} 6 | 7 | type repository struct { 8 | db *sql.DB 9 | } 10 | 11 | func New(db *sql.DB) Repository { 12 | return &repository{db: db} 13 | } 14 | -------------------------------------------------------------------------------- /go-service-struct/simple/internal/services/auth/auth.go: -------------------------------------------------------------------------------- 1 | package auth 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/disturb16/go-service-struct/simple/internal/repositories/users" 7 | ) 8 | 9 | type Service interface { 10 | Authenticate(ctx context.Context, username, password string) error 11 | } 12 | 13 | type service struct { 14 | repo users.Repository 15 | } 16 | 17 | func New(repo users.Repository) Service { 18 | return &service{ 19 | repo: repo, 20 | } 21 | } 22 | 23 | func (s *service) Authenticate(ctx context.Context, username, password string) error { 24 | return nil 25 | } 26 | -------------------------------------------------------------------------------- /go-service-struct/simple/internal/services/profiles/get.go: -------------------------------------------------------------------------------- 1 | package profiles 2 | 3 | func (s *service) Get(id string) (string, error) { 4 | return "", nil 5 | } 6 | -------------------------------------------------------------------------------- /go-service-struct/simple/internal/services/profiles/profile.go: -------------------------------------------------------------------------------- 1 | package profiles 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/disturb16/go-service-struct/simple/internal/repositories/profiles" 7 | ) 8 | 9 | type Service interface { 10 | Update(ctx context.Context, username string) error 11 | Get(id string) (string, error) 12 | } 13 | 14 | type service struct { 15 | repository profiles.Repository 16 | } 17 | 18 | func New(repository profiles.Repository) Service { 19 | return &service{ 20 | repository: repository, 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /go-service-struct/simple/internal/services/profiles/update.go: -------------------------------------------------------------------------------- 1 | package profiles 2 | 3 | import "context" 4 | 5 | func (s *service) Update(ctx context.Context, username string) error { 6 | return nil 7 | } 8 | -------------------------------------------------------------------------------- /go-service-struct/simple/internal/services/services.go: -------------------------------------------------------------------------------- 1 | package services 2 | 3 | import ( 4 | "github.com/disturb16/go-service-struct/simple/internal/services/auth" 5 | "github.com/disturb16/go-service-struct/simple/internal/services/profiles" 6 | "go.uber.org/fx" 7 | ) 8 | 9 | var Module = fx.Module("services", fx.Provide( 10 | auth.New, 11 | profiles.New, 12 | )) 13 | -------------------------------------------------------------------------------- /go-service-struct/simple/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "database/sql" 6 | "log" 7 | 8 | "github.com/disturb16/go-service-struct/simple/config" 9 | "github.com/disturb16/go-service-struct/simple/database" 10 | "github.com/disturb16/go-service-struct/simple/internal/httpapi" 11 | "github.com/disturb16/go-service-struct/simple/internal/httpapi/handlers" 12 | "github.com/disturb16/go-service-struct/simple/internal/repositories" 13 | "github.com/disturb16/go-service-struct/simple/internal/services" 14 | "github.com/labstack/echo/v4" 15 | "go.uber.org/fx" 16 | ) 17 | 18 | type Params struct { 19 | fx.In 20 | 21 | Lc fx.Lifecycle 22 | Config *config.Config 23 | DB *sql.DB 24 | Echo *echo.Echo 25 | Handlers []handlers.Handler `group:"handlers"` 26 | } 27 | 28 | func main() { 29 | app := fx.New( 30 | fx.Provide( 31 | context.Background, 32 | config.New, 33 | database.New, 34 | echo.New, 35 | ), 36 | repositories.Module, 37 | services.Module, 38 | httpapi.Module, 39 | fx.Invoke( 40 | setLifeCycle, 41 | ), 42 | ) 43 | 44 | app.Run() 45 | } 46 | 47 | func setLifeCycle(p Params) { 48 | p.Lc.Append(fx.Hook{ 49 | OnStart: func(context.Context) error { 50 | for _, h := range p.Handlers { 51 | h.RegisterRoutes(p.Echo) 52 | } 53 | 54 | go func() { 55 | p.Echo.Logger.Fatal(p.Echo.Start(p.Config.Address)) 56 | }() 57 | 58 | return nil 59 | }, 60 | 61 | OnStop: func(ctx context.Context) error { 62 | if err := p.Echo.Shutdown(ctx); err != nil { 63 | log.Println(err) 64 | } 65 | 66 | if err := p.DB.Close(); err != nil { 67 | log.Println(err) 68 | } 69 | 70 | return nil 71 | }, 72 | }) 73 | } 74 | -------------------------------------------------------------------------------- /go_basics/go.mod: -------------------------------------------------------------------------------- 1 | module com.example/user/go_basics 2 | 3 | go 1.16 4 | -------------------------------------------------------------------------------- /go_basics/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | fruits := []string{ 7 | "Manzana", 8 | "Banano", 9 | "Sandia", 10 | "Melon", 11 | } 12 | 13 | for _, fruit := range fruits { 14 | index := len(fruit) - 1 15 | letter := fruit[index:] 16 | 17 | if letter == "a" { 18 | continue 19 | } 20 | 21 | fmt.Println("Fruit:", fruit) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /go_interfaces/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/disturb16/go-interfaces 2 | 3 | go 1.16 4 | -------------------------------------------------------------------------------- /go_interfaces/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/disturb16/go-interfaces/users" 7 | ) 8 | 9 | func main() { 10 | var frank users.Cashier = users.NewEmployee("Frank") 11 | var robert users.Admin = users.NewEmployee("Robert") 12 | 13 | total := frank.CalcTotal(90, 65, 93.6) 14 | fmt.Println(total) 15 | 16 | robert.DeactivateEmployee(frank) 17 | 18 | fmt.Println(frank.CalcTotal(90, 65, 93.6)) 19 | } 20 | -------------------------------------------------------------------------------- /go_interfaces/users/users.go: -------------------------------------------------------------------------------- 1 | package users 2 | 3 | import ( 4 | "fmt" 5 | "math/rand" 6 | ) 7 | 8 | type User struct { 9 | Id int 10 | Name string 11 | } 12 | 13 | type Employee struct { 14 | User 15 | Active bool 16 | } 17 | 18 | type Cashier interface { 19 | CalcTotal(item ...float32) float32 20 | deactivate() 21 | reactivate() 22 | } 23 | 24 | type Admin interface { 25 | DeactivateEmployee(c Cashier) 26 | } 27 | 28 | func NewEmployee(name string) *Employee { 29 | return &Employee{ 30 | User: User{ 31 | Id: rand.Intn(1000), 32 | Name: name, 33 | }, 34 | Active: true, 35 | } 36 | } 37 | 38 | func (e *Employee) CalcTotal(item ...float32) float32 { 39 | 40 | if !e.Active { 41 | fmt.Println("Cashier deactivated") 42 | return 0 43 | } 44 | 45 | var sum float32 46 | 47 | for _, i := range item { 48 | sum += i 49 | } 50 | 51 | return sum * 1.15 52 | } 53 | 54 | func (e *Employee) deactivate() { 55 | e.Active = false 56 | } 57 | 58 | func (e *Employee) reactivate() { 59 | e.Active = true 60 | } 61 | 62 | func (e *Employee) DeactivateEmployee(c Cashier) { 63 | c.deactivate() 64 | } 65 | -------------------------------------------------------------------------------- /go_introduction/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/go_introduction 2 | 3 | go 1.16 -------------------------------------------------------------------------------- /go_introduction/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | greeting := "Hello There" 7 | 8 | fmt.Println(greeting) 9 | } 10 | -------------------------------------------------------------------------------- /go_structs/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/disturb/go_structs 2 | 3 | go 1.16 4 | -------------------------------------------------------------------------------- /go_structs/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/disturb/go_structs/users" 7 | ) 8 | 9 | func main() { 10 | // Id: 1, name: Martha, age: 20 11 | 12 | martha := users.User{Id: 1, Name: "Martha", Age: 20} 13 | pedro := users.User{Id: 2, Name: "Pedro", Age: 21} 14 | john := users.User{Id: 3, Name: "John", Age: 20} 15 | jane := users.User{Id: 4, Name: "Jane", Age: 21} 16 | 17 | martha.SayHello() 18 | martha.AddFriend(pedro) 19 | martha.AddFriend(john) 20 | martha.AddFriend(jane) 21 | 22 | martha.ListFriends() 23 | 24 | martha.RemoveFriend(john.Id) 25 | 26 | fmt.Println("========================") 27 | martha.ListFriends() 28 | } 29 | -------------------------------------------------------------------------------- /go_structs/users/users.go: -------------------------------------------------------------------------------- 1 | package users 2 | 3 | import "fmt" 4 | 5 | type User struct { 6 | Id int 7 | Name string 8 | Age int 9 | friends []User 10 | } 11 | 12 | func (u User) SayHello() { 13 | fmt.Println("Hola me llamo", u.Name) 14 | } 15 | 16 | func (u *User) AddFriend(friend User) { 17 | u.friends = append(u.friends, friend) 18 | } 19 | 20 | func (u User) ListFriends() { 21 | for i, f := range u.friends { 22 | fmt.Printf("%d. %s\n", i+1, f.Name) 23 | } 24 | } 25 | 26 | func (u *User) RemoveFriend(Id int) { 27 | index := u.findFriend(Id) 28 | 29 | if index < 0 { 30 | return 31 | } 32 | 33 | u.friends = append(u.friends[:index], u.friends[index+1:]...) 34 | } 35 | 36 | func (u User) findFriend(Id int) int { 37 | for i, f := range u.friends { 38 | if f.Id == Id { 39 | return i 40 | } 41 | } 42 | 43 | return -1 44 | } 45 | -------------------------------------------------------------------------------- /go_templates/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/disturb16/go_templates 2 | 3 | go 1.16 4 | -------------------------------------------------------------------------------- /go_templates/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "html/template" 5 | "os" 6 | ) 7 | 8 | type Person struct { 9 | Name string 10 | Age int 11 | Hobbies []string 12 | } 13 | 14 | var funcs = template.FuncMap{ 15 | "increment": func(num int) int { 16 | return num + 1 17 | }, 18 | } 19 | 20 | func main() { 21 | 22 | john := &Person{"John", 25, []string{"
asdsad
", "ver peliculas", "programar"}} 23 | loadTemplate("index.html", john) 24 | } 25 | 26 | func loadTemplate(fileName string, data interface{}) { 27 | t, err := template.New(fileName).Funcs(funcs).ParseFiles("templates/" + fileName) 28 | 29 | if err != nil { 30 | panic(err) 31 | } 32 | 33 | err = t.Execute(os.Stdout, data) 34 | if err != nil { 35 | panic(err) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /go_templates/templates/greeting.txt: -------------------------------------------------------------------------------- 1 | Hola! 2 | 3 | Mi nombre es {{.Name}}, tengo {{.Age}} años. -------------------------------------------------------------------------------- /go_templates/templates/greeting2.txt: -------------------------------------------------------------------------------- 1 | Hola! 2 | 3 | Mi nombre es {{.Name}}, tengo {{.Age}} años. 4 | 5 | {{ if ge (len .Hobbies) 1 }} 6 | 7 | Mis pasatiempos favoritos son: 8 | 9 | {{ range $index, $hobbie := .Hobbies }} 10 | {{ increment $index}}. {{$hobbie}} 11 | {{end}} 12 | 13 | {{end}} -------------------------------------------------------------------------------- /go_templates/templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |Hola!
4 | 5 |Mi nombre es {{.Name}}, tengo {{.Age}} años.
6 | 7 | {{ if ge (len .Hobbies) 1 }} 8 |Mis pasatiempos favoritos son:
9 | 10 |12 | Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam, quidem. 13 |
14 |15 | Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam, quidem. 16 |
17 |18 | Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam, quidem. 19 |
20 |21 | Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam, quidem. 22 |
23 |24 | Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam, quidem. 25 |
26 |27 | sdasdsa Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam, 28 | quidem. 29 |
30 |31 | Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam, quidem. 32 |
33 |34 | Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam, quidem. 35 |
36 |37 | Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam, quidem. 38 |
39 |40 | Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam, quidem. 41 |
42 |43 | Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam, quidem. 44 |
45 |Lorem ipsum dolor sit amet consectetur adipisicing elit
46 | 47 | 48 | -------------------------------------------------------------------------------- /modd/go.mod: -------------------------------------------------------------------------------- 1 | module watch-files 2 | 3 | go 1.16 4 | 5 | require ( 6 | github.com/cortesi/modd v0.0.0-20211215124449-6083f9d1c171 // indirect 7 | go.uber.org/fx v1.16.0 8 | ) 9 | -------------------------------------------------------------------------------- /modd/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "log" 6 | "watch-files/engine" 7 | 8 | "go.uber.org/fx" 9 | ) 10 | 11 | func main() { 12 | app := fx.New( 13 | fx.Provide( 14 | func() string { 15 | return "Hello there!" 16 | }, 17 | engine.New, 18 | ), 19 | 20 | fx.Invoke( 21 | func(lc fx.Lifecycle, message string, e *engine.Engine) { 22 | lc.Append(fx.Hook{ 23 | OnStart: func(ctx context.Context) error { 24 | log.Println(message) 25 | log.Println(e.RenderIndex()) 26 | return nil 27 | }, 28 | OnStop: func(ctx context.Context) error { 29 | log.Println("Shutting down") 30 | return nil 31 | }, 32 | }) 33 | }, 34 | ), 35 | ) 36 | 37 | app.Run() 38 | } 39 | -------------------------------------------------------------------------------- /modd/modd.conf: -------------------------------------------------------------------------------- 1 | @app= myProject 2 | 3 | **/*.go { 4 | prep +onchange: " 5 | go test -v ./... @dirmods 6 | " 7 | 8 | prep: " 9 | go build -o @app 10 | " 11 | 12 | daemon +sigterm: " 13 | # database migrations 14 | # ENV set to 'test' to run migrations in test mode 15 | ./@app 16 | " 17 | } -------------------------------------------------------------------------------- /modd/myProject: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/disturb16/go_examples/57ca3799913e394df9810a4a3709bd6c0f197097/modd/myProject -------------------------------------------------------------------------------- /movie-reviews/.env: -------------------------------------------------------------------------------- 1 | app.port=8081 2 | 3 | database.host=localhost 4 | database.port=5432 5 | database.dbname=postgres 6 | database.user=postgres 7 | database.password=1234 8 | -------------------------------------------------------------------------------- /movie-reviews/.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "gruntfuggly.todo-tree", 4 | "Gruntfuggly.todo-tree" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /movie-reviews/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "Launch Package", 9 | "type": "go", 10 | "request": "launch", 11 | "mode": "auto", 12 | "program": "${workspaceFolder}" 13 | } 14 | ] 15 | } -------------------------------------------------------------------------------- /movie-reviews/api/api.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import "github.com/disturb16/go_examples/movie-reviews/internal/users" 4 | 5 | type API struct { 6 | userService users.Service 7 | } 8 | 9 | func NewApi(userService users.Service) *API { 10 | return &API{userService} 11 | } 12 | -------------------------------------------------------------------------------- /movie-reviews/api/dto/register_user.go: -------------------------------------------------------------------------------- 1 | package dto 2 | 3 | import models "github.com/disturb16/go_examples/movie-reviews/internal/users/model" 4 | 5 | type RegisterUser struct { 6 | Name string `json:"name"` 7 | Email string `json:"email"` 8 | Password string `json:"password"` 9 | } 10 | 11 | func (dto *RegisterUser) ToUserModel() *models.Users { 12 | return &models.Users{ 13 | Name: dto.Name, 14 | Email: dto.Email, 15 | Password: dto.Password, 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /movie-reviews/api/routes.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import "github.com/labstack/echo/v4" 4 | 5 | func RegisterRoutes(e *echo.Echo, api *API) { 6 | e.POST("/users", api.Register) 7 | } 8 | -------------------------------------------------------------------------------- /movie-reviews/api/user.handler.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "encoding/json" 5 | "net/http" 6 | 7 | "github.com/disturb16/go_examples/movie-reviews/api/dto" 8 | "github.com/labstack/echo/v4" 9 | ) 10 | 11 | func (api *API) Register(c echo.Context) error { 12 | 13 | // ctx := c.Request().Context() 14 | params := dto.RegisterUser{} 15 | 16 | data := c.Request().Body 17 | decoder := json.NewDecoder(data) 18 | 19 | err := decoder.Decode(¶ms) 20 | if err != nil { 21 | // TODO: Handle dto errors 22 | return c.JSON(http.StatusBadRequest, err) 23 | } 24 | 25 | err = api.userService.Register(params.ToUserModel()) 26 | if err != nil { 27 | return c.JSON(http.StatusBadRequest, err) 28 | } 29 | 30 | return c.JSON(http.StatusCreated, err) 31 | } 32 | -------------------------------------------------------------------------------- /movie-reviews/database/database.go: -------------------------------------------------------------------------------- 1 | package database 2 | 3 | import "github.com/jmoiron/sqlx" 4 | 5 | type Config struct { 6 | Host string `json:"database.host"` 7 | Port string `json:"database.port"` 8 | User string `json:"database.user"` 9 | Pass string `json:"database.password"` 10 | DbName string `json:"database.dbname"` 11 | } 12 | 13 | func New() *sqlx.DB {} 14 | -------------------------------------------------------------------------------- /movie-reviews/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/disturb16/go_examples/movie-reviews 2 | 3 | go 1.18 4 | 5 | require ( 6 | github.com/jmoiron/sqlx v1.3.5 // indirect 7 | github.com/joho/godotenv v1.4.0 // indirect 8 | github.com/labstack/echo/v4 v4.7.2 // indirect 9 | github.com/labstack/gommon v0.3.1 // indirect 10 | github.com/mattn/go-colorable v0.1.11 // indirect 11 | github.com/mattn/go-isatty v0.0.14 // indirect 12 | github.com/valyala/bytebufferpool v1.0.0 // indirect 13 | github.com/valyala/fasttemplate v1.2.1 // indirect 14 | go.uber.org/atomic v1.6.0 // indirect 15 | go.uber.org/dig v1.14.0 // indirect 16 | go.uber.org/fx v1.17.1 // indirect 17 | go.uber.org/multierr v1.5.0 // indirect 18 | go.uber.org/zap v1.16.0 // indirect 19 | golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 // indirect 20 | golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f // indirect 21 | golang.org/x/sys v0.0.0-20211103235746-7861aae1554b // indirect 22 | golang.org/x/text v0.3.7 // indirect 23 | ) 24 | -------------------------------------------------------------------------------- /movie-reviews/internal/users/model/users.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | type Users struct { 4 | ID int `json:"-" db:"id"` 5 | Name string `json:"name" db:"name"` 6 | Email string `json:"email" db:"email"` 7 | Password string `json:"-" db:"password"` 8 | } 9 | -------------------------------------------------------------------------------- /movie-reviews/internal/users/repository/users.repository.go: -------------------------------------------------------------------------------- 1 | package repository 2 | 3 | import ( 4 | "github.com/disturb16/go_examples/movie-reviews/internal/users" 5 | models "github.com/disturb16/go_examples/movie-reviews/internal/users/model" 6 | "github.com/jmoiron/sqlx" 7 | ) 8 | 9 | const ( 10 | // CreateUserQuery is the query to create a user 11 | createUserQuery = ` 12 | INSERT INTO users (name, email, password) 13 | VALUES (:name ,:email, :password) 14 | ` 15 | 16 | // GetUserByEmailQuery is the query to get a user by email 17 | getUserByEmailQuery = ` 18 | SELECT 19 | id, 20 | name, 21 | email, 22 | password 23 | FROM users 24 | WHERE email = $1 25 | AND password = $2 26 | ` 27 | ) 28 | 29 | type repository struct { 30 | db *sqlx.DB 31 | } 32 | 33 | func NewUserRepository(db *sqlx.DB) users.Repository { 34 | return &repository{db} 35 | } 36 | 37 | func (r *repository) Save(user *models.Users) error { 38 | _, err := r.db.NamedExec(createUserQuery, user) 39 | return err 40 | } 41 | 42 | func (r *repository) Login(email, password string) (*models.Users, error) { 43 | user := &models.Users{} 44 | err := r.db.Get(user, getUserByEmailQuery, email, password) 45 | return user, err 46 | } 47 | -------------------------------------------------------------------------------- /movie-reviews/internal/users/service/users.service.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "errors" 5 | 6 | "github.com/disturb16/go_examples/movie-reviews/internal/users" 7 | models "github.com/disturb16/go_examples/movie-reviews/internal/users/model" 8 | ) 9 | 10 | var ( 11 | UserNotFoundError = errors.New("User not found") 12 | ) 13 | 14 | type service struct { 15 | repo users.Repository 16 | } 17 | 18 | // NewUserService returns a new user service impolementation. 19 | func NewUserService(repo users.Repository) users.Service { 20 | return &service{repo} 21 | } 22 | 23 | // Register creates a new user. 24 | func (s *service) Register(user *models.Users) error { 25 | 26 | // TODO: Check if the user already exists 27 | 28 | return s.repo.Save(user) 29 | } 30 | 31 | // Login returns a user if the credentials are correct. 32 | func (s *service) Login(email, password string) (*models.Users, error) { 33 | 34 | u, err := s.repo.Login(email, password) 35 | if err != nil { 36 | return nil, UserNotFoundError 37 | } 38 | 39 | return u, nil 40 | } 41 | -------------------------------------------------------------------------------- /movie-reviews/internal/users/users.go: -------------------------------------------------------------------------------- 1 | package users 2 | 3 | import models "github.com/disturb16/go_examples/movie-reviews/internal/users/model" 4 | 5 | //go:generate mockery --name=Repository --output=internal/users --inpackage=true 6 | type Repository interface { 7 | Save(user *models.Users) error 8 | Login(email, password string) (*models.Users, error) 9 | } 10 | 11 | //go:generate mockery --name=Service --output=internal/users --inpackage=true 12 | type Service interface { 13 | Register(user *models.Users) error 14 | Login(email, password string) (*models.Users, error) 15 | } 16 | -------------------------------------------------------------------------------- /movie-reviews/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | 6 | "github.com/disturb16/go_examples/movie-reviews/settings" 7 | "go.uber.org/fx" 8 | ) 9 | 10 | func main() { 11 | 12 | app := fx.New( 13 | fx.Provide( 14 | settings.New, 15 | ), 16 | fx.Invoke( 17 | func(settings *settings.Settings) { 18 | log.Println(settings) 19 | }, 20 | ), 21 | ) 22 | 23 | app.Run() 24 | } 25 | -------------------------------------------------------------------------------- /movie-reviews/requirements.md: -------------------------------------------------------------------------------- 1 | # Users 2 | - Register 3 | - Login 4 | 5 | # Reviews 6 | - Create 7 | - Update 8 | - Delete 9 | 10 | # Comments 11 | - Create 12 | - Delete -------------------------------------------------------------------------------- /movie-reviews/schema.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE users ( 2 | id serial constraint users_id PRIMARY KEY, 3 | name TEXT, 4 | email TEXT, 5 | password TEXT 6 | ); -------------------------------------------------------------------------------- /movie-reviews/settings/settings.go: -------------------------------------------------------------------------------- 1 | package settings 2 | 3 | import ( 4 | "encoding/json" 5 | "log" 6 | 7 | "github.com/disturb16/go_examples/movie-reviews/database" 8 | "github.com/joho/godotenv" 9 | ) 10 | 11 | type Settings struct { 12 | Port string `json:"app.port"` 13 | Database database.Config 14 | } 15 | 16 | func New() *Settings { 17 | config, err := godotenv.Read(".env") 18 | if err != nil { 19 | log.Panic(err) 20 | } 21 | 22 | content, err := json.Marshal(config) 23 | if err != nil { 24 | log.Panic(err) 25 | } 26 | 27 | app := Settings{} 28 | 29 | err = json.Unmarshal(content, &app) 30 | if err != nil { 31 | log.Panic(err) 32 | } 33 | 34 | dbConfig := database.Config{} 35 | 36 | err = json.Unmarshal(content, &dbConfig) 37 | if err != nil { 38 | log.Panic(err) 39 | } 40 | 41 | app.Database = dbConfig 42 | 43 | return &app 44 | } 45 | -------------------------------------------------------------------------------- /recoverpanic/go.mod: -------------------------------------------------------------------------------- 1 | module recoverpanic 2 | 3 | go 1.22.0 4 | -------------------------------------------------------------------------------- /recoverpanic/go.sum: -------------------------------------------------------------------------------- 1 | github.com/labstack/echo/v4 v4.11.4 h1:vDZmA+qNeh1pd/cCkEicDMrjtrnMGQ1QFI9gWN1zGq8= 2 | github.com/labstack/echo/v4 v4.11.4/go.mod h1:noh7EvLwqDsmh/X/HWKPUl1AjzJrhyptRyEbQJfxen8= 3 | github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0= 4 | github.com/labstack/gommon v0.4.2/go.mod h1:QlUFxVM+SNXhDL/Z7YhocGIBYOiwB0mXm1+1bAPHPyU= 5 | github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= 6 | github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= 7 | github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= 8 | github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= 9 | github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= 10 | github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= 11 | github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= 12 | github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo= 13 | github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= 14 | golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= 15 | golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= 16 | golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c= 17 | golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= 18 | golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 19 | golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 20 | golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= 21 | golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 22 | golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= 23 | golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= 24 | -------------------------------------------------------------------------------- /recoverpanic/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | 6 | "github.com/labstack/echo/v4" 7 | "github.com/labstack/echo/v4/middleware" 8 | ) 9 | 10 | func logNum(n int) { 11 | log.Println(n) 12 | 13 | if n == 5 { 14 | panic("n is 5") 15 | } 16 | } 17 | 18 | var lastNum = 0 19 | 20 | func main() { 21 | // defer func() { 22 | // if err := recover(); err != nil { 23 | // log.Println("recovered from panic") 24 | // lastNum++ 25 | // logNums() 26 | // } 27 | // }() 28 | 29 | // logNums() 30 | 31 | r := echo.New() 32 | r.Use(middleware.Recover()) 33 | 34 | r.GET("/", func(c echo.Context) error { 35 | logNums() 36 | return c.String(200, "OK") 37 | }) 38 | 39 | } 40 | 41 | func logNums() { 42 | for i := lastNum; i < 10; i++ { 43 | lastNum = i 44 | logNum(i) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /simple_cli/commands/commands.go: -------------------------------------------------------------------------------- 1 | package commands 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "os" 7 | "strings" 8 | 9 | "gitbhub.com/disturb16/simple_cli/expenses" 10 | ) 11 | 12 | var reader *bufio.Reader = bufio.NewReader(os.Stdin) 13 | 14 | func GetInput() (string, error) { 15 | 16 | fmt.Print("-> ") 17 | str, err := reader.ReadString('\n') 18 | if err != nil { 19 | return "", err 20 | } 21 | 22 | str = strings.Replace(str, "\n", "", 1) 23 | 24 | return str, nil 25 | } 26 | 27 | func ShowInConsole(expensesList []float32) { 28 | fmt.Println(contentString(expensesList)) 29 | } 30 | 31 | func contentString(expensesList []float32) string { 32 | builder := strings.Builder{} 33 | 34 | max, min, avg, total := expensesDetails(expensesList) 35 | 36 | fmt.Println("") 37 | for i, expense := range expensesList { 38 | builder.WriteString(fmt.Sprintf("Expense: %6.2f\n", expense)) 39 | 40 | if i == len(expensesList)-1 { 41 | builder.WriteString("") 42 | builder.WriteString("========================\n") 43 | builder.WriteString(fmt.Sprintf("Total: %6.2f\n", total)) 44 | builder.WriteString(fmt.Sprintf("Max: %6.2f\n", max)) 45 | builder.WriteString(fmt.Sprintf("Min: %6.2f\n", min)) 46 | builder.WriteString(fmt.Sprintf("Average: %6.2f\n", avg)) 47 | builder.WriteString("========================\n") 48 | } 49 | } 50 | 51 | return builder.String() 52 | } 53 | 54 | func expensesDetails(expensesList []float32) (max, min, avg, total float32) { 55 | 56 | if len(expensesList) == 0 { 57 | return 58 | } 59 | 60 | min = expenses.Min(expensesList...) 61 | max = expenses.Max(expensesList...) 62 | total = expenses.Sum(expensesList...) 63 | avg = expenses.Average(expensesList...) 64 | 65 | return 66 | } 67 | 68 | func Export(fileName string, list []float32) error { 69 | 70 | f, err := os.Create(fileName) 71 | if err != nil { 72 | return err 73 | } 74 | 75 | defer f.Close() 76 | 77 | w := bufio.NewWriter(f) 78 | 79 | _, err = w.WriteString(contentString(list)) 80 | if err != nil { 81 | return err 82 | } 83 | 84 | return w.Flush() 85 | } 86 | -------------------------------------------------------------------------------- /simple_cli/expenses.txt: -------------------------------------------------------------------------------- 1 | Expense: 10.00 2 | Expense: 20.00 3 | Expense: 30.00 4 | Expense: 40.00 5 | ======================== 6 | Total: 100.00 7 | Max: 40.00 8 | Min: 10.00 9 | Average: 25.00 10 | ======================== 11 | -------------------------------------------------------------------------------- /simple_cli/expenses/expenses.go: -------------------------------------------------------------------------------- 1 | package expenses 2 | 3 | func Average(expns ...float32) float32 { 4 | return Sum(expns...) / float32(len(expns)) 5 | } 6 | 7 | func Sum(expns ...float32) float32 { 8 | var sum float32 9 | 10 | for _, exp := range expns { 11 | sum += exp 12 | } 13 | 14 | return sum 15 | } 16 | 17 | func Max(expns ...float32) float32 { 18 | var max float32 19 | 20 | for _, exp := range expns { 21 | if exp > max { 22 | max = exp 23 | } 24 | } 25 | 26 | return max 27 | } 28 | 29 | func Min(expns ...float32) float32 { 30 | 31 | if len(expns) == 0 { 32 | return 0 33 | } 34 | 35 | var min float32 = expns[0] 36 | 37 | for _, exp := range expns { 38 | if exp < min { 39 | min = exp 40 | } 41 | } 42 | 43 | return min 44 | } 45 | -------------------------------------------------------------------------------- /simple_cli/expenses1.txt: -------------------------------------------------------------------------------- 1 | Expense: 10.00 2 | Expense: 20.00 3 | Expense: 30.00 4 | Expense: 40.00 5 | Expense: 50.00 6 | Expense: 60.00 7 | ========================Total: 210.00 8 | Max: 60.00 9 | Min: 10.00 10 | Average: 35.00 11 | ======================== -------------------------------------------------------------------------------- /simple_cli/go.mod: -------------------------------------------------------------------------------- 1 | module gitbhub.com/disturb16/simple_cli 2 | 3 | go 1.16 4 | -------------------------------------------------------------------------------- /simple_cli/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "log" 6 | "strconv" 7 | 8 | "gitbhub.com/disturb16/simple_cli/commands" 9 | ) 10 | 11 | func main() { 12 | 13 | var expenses []float32 14 | var export string 15 | 16 | flag.StringVar(&export, "export", "", "Exports the details to .txt") 17 | flag.Parse() 18 | 19 | for { 20 | input, err := commands.GetInput() 21 | if err != nil { 22 | log.Fatal(err) 23 | } 24 | 25 | if input == "cls" { 26 | break 27 | } 28 | 29 | expense, err := strconv.ParseFloat(input, 32) 30 | if err != nil { 31 | continue 32 | } 33 | 34 | expenses = append(expenses, float32(expense)) 35 | } 36 | 37 | if export == "" { 38 | commands.ShowInConsole(expenses) 39 | } else { 40 | commands.Export(export, expenses) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /simple_cli/requirements.md: -------------------------------------------------------------------------------- 1 | # Caracteristicas de la app 2 | 3 | - [x] obtner todos gastos infinitamente hasta ingresar un comando de **stop** 4 | - [x] poder obtener el promedio, valores maximos y minimos de los gastos ingresados 5 | - [x] mostrar un resumen en la consola de todo lo ingresado 6 | - [x] exportar esta informacion a un archivo .txr -------------------------------------------------------------------------------- /traefik/books/Dockerfile: -------------------------------------------------------------------------------- 1 | #build stage 2 | FROM golang:alpine AS builder 3 | RUN apk add --no-cache git 4 | WORKDIR /go/src/app 5 | COPY . . 6 | RUN go get -d -v ./... 7 | RUN go build -o /go/bin/app -v ./... 8 | 9 | #final stage 10 | FROM alpine:latest 11 | RUN apk --no-cache add ca-certificates 12 | COPY --from=builder /go/bin/app /app 13 | ENTRYPOINT /app 14 | LABEL Name=traefik Version=0.0.1 15 | EXPOSE 8080 -------------------------------------------------------------------------------- /traefik/books/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | 3 | services: 4 | api: 5 | build: 6 | context: . 7 | networks: 8 | - traefik-net 9 | labels: 10 | - "traefik.http.routers.books-api.rule=Host(`books.localhost`)" 11 | 12 | networks: 13 | traefik-net: 14 | external: true -------------------------------------------------------------------------------- /traefik/books/go.mod: -------------------------------------------------------------------------------- 1 | module books 2 | 3 | go 1.20 4 | 5 | require github.com/labstack/echo/v4 v4.10.0 6 | 7 | require ( 8 | github.com/labstack/gommon v0.4.0 // indirect 9 | github.com/mattn/go-colorable v0.1.13 // indirect 10 | github.com/mattn/go-isatty v0.0.16 // indirect 11 | github.com/valyala/bytebufferpool v1.0.0 // indirect 12 | github.com/valyala/fasttemplate v1.2.2 // indirect 13 | golang.org/x/crypto v0.2.0 // indirect 14 | golang.org/x/net v0.4.0 // indirect 15 | golang.org/x/sys v0.3.0 // indirect 16 | golang.org/x/text v0.5.0 // indirect 17 | ) 18 | -------------------------------------------------------------------------------- /traefik/books/go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 2 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 3 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 4 | github.com/labstack/echo/v4 v4.10.0 h1:5CiyngihEO4HXsz3vVsJn7f8xAlWwRr3aY6Ih280ZKA= 5 | github.com/labstack/echo/v4 v4.10.0/go.mod h1:S/T/5fy/GigaXnHTkh0ZGe4LpkkQysvRjFMSUTkDRNQ= 6 | github.com/labstack/gommon v0.4.0 h1:y7cvthEAEbU0yHOf4axH8ZG2NH8knB9iNSoTO8dyIk8= 7 | github.com/labstack/gommon v0.4.0/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM= 8 | github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= 9 | github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= 10 | github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= 11 | github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= 12 | github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ= 13 | github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= 14 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 15 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 16 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 17 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 18 | github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= 19 | github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= 20 | github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= 21 | github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= 22 | github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo= 23 | github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= 24 | golang.org/x/crypto v0.2.0 h1:BRXPfhNivWL5Yq0BGQ39a2sW6t44aODpfxkWjYdzewE= 25 | golang.org/x/crypto v0.2.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= 26 | golang.org/x/net v0.4.0 h1:Q5QPcMlvfxFTAPV0+07Xz/MpK9NTXu2VDUuy0FeMfaU= 27 | golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= 28 | golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 29 | golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 30 | golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 31 | golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 32 | golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ= 33 | golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 34 | golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM= 35 | golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= 36 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 37 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 38 | gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 39 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 40 | -------------------------------------------------------------------------------- /traefik/books/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "github.com/labstack/echo/v4" 4 | 5 | func main() { 6 | e := echo.New() 7 | 8 | e.GET("/", func(c echo.Context) error { 9 | return c.JSON(200, []map[string]string{ 10 | {"title": "The Hitchhiker's Guide to the Galaxy", "author": "Douglas Adams"}, 11 | {"title": "The Restaurant at the End of the Universe", "author": "Douglas Adams"}, 12 | }) 13 | }) 14 | 15 | e.Logger.Fatal(e.Start(":8080")) 16 | } 17 | -------------------------------------------------------------------------------- /traefik/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.9' 2 | 3 | services: 4 | reverse-proxy: 5 | # The official v2 Traefik docker image 6 | image: traefik:v2.9.6 7 | # Enables the web UI and tells Traefik to listen to docker 8 | command: --api.insecure=true --providers.docker 9 | ports: 10 | # The HTTP port 11 | - 80:80 12 | # The Web UI (enabled by --api.insecure=true) 13 | - 8080:8080 14 | networks: 15 | - traefik-net 16 | volumes: 17 | # So that Traefik can listen to the Docker events 18 | - /var/run/docker.sock:/var/run/docker.sock 19 | 20 | networks: 21 | traefik-net: 22 | external: true -------------------------------------------------------------------------------- /traefik/songs/Dockerfile: -------------------------------------------------------------------------------- 1 | #build stage 2 | FROM golang:alpine AS builder 3 | RUN apk add --no-cache git 4 | WORKDIR /go/src/app 5 | COPY . . 6 | RUN go get -d -v ./... 7 | RUN go build -o /go/bin/app -v ./... 8 | 9 | #final stage 10 | FROM alpine:latest 11 | RUN apk --no-cache add ca-certificates 12 | COPY --from=builder /go/bin/app /app 13 | ENTRYPOINT /app 14 | LABEL Name=traefik Version=0.0.1 15 | -------------------------------------------------------------------------------- /traefik/songs/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | 3 | services: 4 | api: 5 | build: 6 | context: . 7 | networks: 8 | - traefik-net 9 | labels: 10 | - "traefik.http.routers.songs-api.rule=Host(`songs.localhost`)" 11 | - "traefik.http.services.songs-api.loadbalancer.server.port=1234" 12 | 13 | networks: 14 | traefik-net: 15 | external: true -------------------------------------------------------------------------------- /traefik/songs/go.mod: -------------------------------------------------------------------------------- 1 | module songs 2 | 3 | go 1.20 4 | 5 | require github.com/labstack/echo/v4 v4.10.0 6 | 7 | require ( 8 | github.com/labstack/gommon v0.4.0 // indirect 9 | github.com/mattn/go-colorable v0.1.13 // indirect 10 | github.com/mattn/go-isatty v0.0.16 // indirect 11 | github.com/valyala/bytebufferpool v1.0.0 // indirect 12 | github.com/valyala/fasttemplate v1.2.2 // indirect 13 | golang.org/x/crypto v0.2.0 // indirect 14 | golang.org/x/net v0.4.0 // indirect 15 | golang.org/x/sys v0.3.0 // indirect 16 | golang.org/x/text v0.5.0 // indirect 17 | ) 18 | -------------------------------------------------------------------------------- /traefik/songs/go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 2 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 3 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 4 | github.com/labstack/echo/v4 v4.10.0 h1:5CiyngihEO4HXsz3vVsJn7f8xAlWwRr3aY6Ih280ZKA= 5 | github.com/labstack/echo/v4 v4.10.0/go.mod h1:S/T/5fy/GigaXnHTkh0ZGe4LpkkQysvRjFMSUTkDRNQ= 6 | github.com/labstack/gommon v0.4.0 h1:y7cvthEAEbU0yHOf4axH8ZG2NH8knB9iNSoTO8dyIk8= 7 | github.com/labstack/gommon v0.4.0/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM= 8 | github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= 9 | github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= 10 | github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= 11 | github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= 12 | github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ= 13 | github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= 14 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 15 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 16 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 17 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 18 | github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= 19 | github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= 20 | github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= 21 | github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= 22 | github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo= 23 | github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= 24 | golang.org/x/crypto v0.2.0 h1:BRXPfhNivWL5Yq0BGQ39a2sW6t44aODpfxkWjYdzewE= 25 | golang.org/x/crypto v0.2.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= 26 | golang.org/x/net v0.4.0 h1:Q5QPcMlvfxFTAPV0+07Xz/MpK9NTXu2VDUuy0FeMfaU= 27 | golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= 28 | golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 29 | golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 30 | golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 31 | golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 32 | golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ= 33 | golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 34 | golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM= 35 | golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= 36 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 37 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 38 | gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 39 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 40 | -------------------------------------------------------------------------------- /traefik/songs/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "github.com/labstack/echo/v4" 4 | 5 | func main() { 6 | e := echo.New() 7 | 8 | e.GET("/", func(c echo.Context) error { 9 | return c.JSON(200, []map[string]string{ 10 | {"title": "In The End", "author": "Linkin Park"}, 11 | {"title": "Dance 4 Life", "author": "Tiesto"}, 12 | }) 13 | }) 14 | 15 | e.Logger.Fatal(e.Start(":1234")) 16 | } 17 | --------------------------------------------------------------------------------